Ticket #1170: 1170-p3.diff

File 1170-p3.diff, 296.8 KB (added by warner, at 2010-08-31T19:57:07Z)

for review: add Protovis-based download-status timeline visualization page

  • src/allmydata/immutable/downloader/finder.py

    diff --git a/src/allmydata/immutable/downloader/finder.py b/src/allmydata/immutable/downloader/finder.py
    index fa6204c..e008ec1 100644
    a b class ShareFinder: 
    137137                      peerid=idlib.shortnodeid_b2a(peerid),
    138138                      level=log.NOISY, umid="Io7pyg")
    139139        time_sent = now()
    140         d_ev = self._download_status.add_dyhb_sent(peerid, time_sent)
     140        d_ev = self._download_status.add_dyhb_request(peerid, time_sent)
    141141        # TODO: get the timer from a Server object, it knows best
    142142        self.overdue_timers[req] = reactor.callLater(self.OVERDUE_TIMEOUT,
    143143                                                     self.overdue, req)
    class ShareFinder: 
    223223        eventually(self.share_consumer.got_shares, shares)
    224224
    225225    def _got_error(self, f, peerid, req, d_ev, lp):
    226         d_ev.finished("error", now())
     226        d_ev.error(now())
    227227        self.log(format="got error from [%(peerid)s]",
    228228                 peerid=idlib.shortnodeid_b2a(peerid), failure=f,
    229229                 level=log.UNUSUAL, parent=lp, umid="zUKdCw")
  • src/allmydata/immutable/downloader/node.py

    diff --git a/src/allmydata/immutable/downloader/node.py b/src/allmydata/immutable/downloader/node.py
    index 33c16cf..0666216 100644
    a b class DownloadNode: 
    7272        # things to track callers that want data
    7373
    7474        # _segment_requests can have duplicates
    75         self._segment_requests = [] # (segnum, d, cancel_handle, logparent)
     75        self._segment_requests = [] # (segnum, d, cancel_handle, seg_ev, lp)
    7676        self._active_segment = None # a SegmentFetcher, with .segnum
    7777
    7878        self._segsize_observers = observer.OneShotObserverList()
    class DownloadNode: 
    119119    # things called by outside callers, via CiphertextFileNode. get_segment()
    120120    # may also be called by Segmentation.
    121121
    122     def read(self, consumer, offset=0, size=None, read_ev=None):
     122    def read(self, consumer, offset, size, read_ev):
    123123        """I am the main entry point, from which FileNode.read() can get
    124124        data. I feed the consumer with the desired range of ciphertext. I
    125125        return a Deferred that fires (with the consumer) when the read is
    126126        finished.
    127127
    128128        Note that there is no notion of a 'file pointer': each call to read()
    129         uses an independent offset= value."""
    130         # for concurrent operations: each gets its own Segmentation manager
    131         if size is None:
    132             size = self._verifycap.size
    133         # clip size so offset+size does not go past EOF
    134         size = min(size, self._verifycap.size-offset)
    135         if read_ev is None:
    136             read_ev = self._download_status.add_read_event(offset, size, now())
     129        uses an independent offset= value.
     130        """
     131        assert size is not None
     132        assert read_ev is not None
    137133
    138134        lp = log.msg(format="imm Node(%(si)s).read(%(offset)d, %(size)d)",
    139135                     si=base32.b2a(self._verifycap.storage_index)[:8],
    class DownloadNode: 
    143139            sp = self._history.stats_provider
    144140            sp.count("downloader.files_downloaded", 1) # really read() calls
    145141            sp.count("downloader.bytes_downloaded", size)
     142        # for concurrent operations, each read() gets its own Segmentation
     143        # manager
    146144        s = Segmentation(self, offset, size, consumer, read_ev, lp)
     145
    147146        # this raises an interesting question: what segments to fetch? if
    148147        # offset=0, always fetch the first segment, and then allow
    149148        # Segmentation to be responsible for pulling the subsequent ones if
    class DownloadNode: 
    181180                     si=base32.b2a(self._verifycap.storage_index)[:8],
    182181                     segnum=segnum,
    183182                     level=log.OPERATIONAL, parent=logparent, umid="UKFjDQ")
    184         self._download_status.add_segment_request(segnum, now())
     183        seg_ev = self._download_status.add_segment_request(segnum, now())
    185184        d = defer.Deferred()
    186185        c = Cancel(self._cancel_request)
    187         self._segment_requests.append( (segnum, d, c, lp) )
     186        self._segment_requests.append( (segnum, d, c, seg_ev, lp) )
    188187        self._start_new_segment()
    189188        return (d, c)
    190189
    class DownloadNode: 
    208207
    209208    def _start_new_segment(self):
    210209        if self._active_segment is None and self._segment_requests:
    211             segnum = self._segment_requests[0][0]
     210            (segnum, d, c, seg_ev, lp) = self._segment_requests[0]
    212211            k = self._verifycap.needed_shares
    213             lp = self._segment_requests[0][3]
    214212            log.msg(format="%(node)s._start_new_segment: segnum=%(segnum)d",
    215213                    node=repr(self), segnum=segnum,
    216214                    level=log.NOISY, parent=lp, umid="wAlnHQ")
    217215            self._active_segment = fetcher = SegmentFetcher(self, segnum, k, lp)
     216            seg_ev.activate(now())
    218217            active_shares = [s for s in self._shares if s.is_alive()]
    219218            fetcher.add_shares(active_shares) # this triggers the loop
    220219
    class DownloadNode: 
    378377    def fetch_failed(self, sf, f):
    379378        assert sf is self._active_segment
    380379        # deliver error upwards
    381         for (d,c) in self._extract_requests(sf.segnum):
     380        for (d,c,seg_ev) in self._extract_requests(sf.segnum):
     381            seg_ev.error(now())
    382382            eventually(self._deliver, d, c, f)
    383383        self._active_segment = None
    384384        self._start_new_segment()
    class DownloadNode: 
    387387        d = defer.maybeDeferred(self._decode_blocks, segnum, blocks)
    388388        d.addCallback(self._check_ciphertext_hash, segnum)
    389389        def _deliver(result):
    390             ds = self._download_status
    391             if isinstance(result, Failure):
    392                 ds.add_segment_error(segnum, now())
    393             else:
    394                 (offset, segment, decodetime) = result
    395                 ds.add_segment_delivery(segnum, now(),
    396                                         offset, len(segment), decodetime)
    397390            log.msg(format="delivering segment(%(segnum)d)",
    398391                    segnum=segnum,
    399392                    level=log.OPERATIONAL, parent=self._lp,
    400393                    umid="j60Ojg")
    401             for (d,c) in self._extract_requests(segnum):
    402                 eventually(self._deliver, d, c, result)
     394            when = now()
     395            if isinstance(result, Failure):
     396                # this catches failures in decode or ciphertext hash
     397                for (d,c,seg_ev) in self._extract_requests(segnum):
     398                    seg_ev.error(when)
     399                    eventually(self._deliver, d, c, result)
     400            else:
     401                (offset, segment, decodetime) = result
     402                for (d,c,seg_ev) in self._extract_requests(segnum):
     403                    # when we have two requests for the same segment, the
     404                    # second one will not be "activated" before the data is
     405                    # delivered, so to allow the status-reporting code to see
     406                    # consistent behavior, we activate them all now. The
     407                    # SegmentEvent will ignore duplicate activate() calls.
     408                    # Note that this will result in an infinite "receive
     409                    # speed" for the second request.
     410                    seg_ev.activate(when)
     411                    seg_ev.deliver(when, offset, len(segment), decodetime)
     412                    eventually(self._deliver, d, c, result)
    403413            self._active_segment = None
    404414            self._start_new_segment()
    405415        d.addBoth(_deliver)
    406         d.addErrback(lambda f:
    407                      log.err("unhandled error during process_blocks",
    408                              failure=f, level=log.WEIRD,
    409                              parent=self._lp, umid="MkEsCg"))
     416        d.addErrback(log.err, "unhandled error during process_blocks",
     417                     level=log.WEIRD, parent=self._lp, umid="MkEsCg")
    410418
    411419    def _decode_blocks(self, segnum, blocks):
    412420        tail = (segnum == self.num_segments-1)
    class DownloadNode: 
    474482    def _extract_requests(self, segnum):
    475483        """Remove matching requests and return their (d,c) tuples so that the
    476484        caller can retire them."""
    477         retire = [(d,c) for (segnum0, d, c, lp) in self._segment_requests
     485        retire = [(d,c,seg_ev)
     486                  for (segnum0,d,c,seg_ev,lp) in self._segment_requests
    478487                  if segnum0 == segnum]
    479488        self._segment_requests = [t for t in self._segment_requests
    480489                                  if t[0] != segnum]
    class DownloadNode: 
    483492    def _cancel_request(self, c):
    484493        self._segment_requests = [t for t in self._segment_requests
    485494                                  if t[2] != c]
    486         segnums = [segnum for (segnum,d,c,lp) in self._segment_requests]
     495        segnums = [segnum for (segnum,d,c,seg_ev,lp) in self._segment_requests]
    487496        # self._active_segment might be None in rare circumstances, so make
    488497        # sure we tolerate it
    489498        if self._active_segment and self._active_segment.segnum not in segnums:
  • src/allmydata/immutable/downloader/segmentation.py

    diff --git a/src/allmydata/immutable/downloader/segmentation.py b/src/allmydata/immutable/downloader/segmentation.py
    index 7c9f5cf..84dddbe 100644
    a b class Segmentation: 
    123123        # the consumer might call our .pauseProducing() inside that write()
    124124        # call, setting self._hungry=False
    125125        self._read_ev.update(len(desired_data), 0, 0)
     126        # note: filenode.DecryptingConsumer is responsible for calling
     127        # _read_ev.update with how much decrypt_time was consumed
    126128        self._maybe_fetch_next()
    127129
    128130    def _retry_bad_segment(self, f):
  • src/allmydata/immutable/downloader/share.py

    diff --git a/src/allmydata/immutable/downloader/share.py b/src/allmydata/immutable/downloader/share.py
    index 78cce8e..6179ff9 100644
    a b class Share: 
    727727                         share=repr(self),
    728728                         start=start, length=length,
    729729                         level=log.NOISY, parent=self._lp, umid="sgVAyA")
    730             req_ev = ds.add_request_sent(self._peerid, self._shnum,
    731                                          start, length, now())
     730            block_ev = ds.add_block_request(self._peerid, self._shnum,
     731                                            start, length, now())
    732732            d = self._send_request(start, length)
    733             d.addCallback(self._got_data, start, length, req_ev, lp)
    734             d.addErrback(self._got_error, start, length, req_ev, lp)
     733            d.addCallback(self._got_data, start, length, block_ev, lp)
     734            d.addErrback(self._got_error, start, length, block_ev, lp)
    735735            d.addCallback(self._trigger_loop)
    736736            d.addErrback(lambda f:
    737737                         log.err(format="unhandled error during send_request",
    class Share: 
    741741    def _send_request(self, start, length):
    742742        return self._rref.callRemote("read", start, length)
    743743
    744     def _got_data(self, data, start, length, req_ev, lp):
    745         req_ev.finished(len(data), now())
     744    def _got_data(self, data, start, length, block_ev, lp):
     745        block_ev.finished(len(data), now())
    746746        if not self._alive:
    747747            return
    748748        log.msg(format="%(share)s._got_data [%(start)d:+%(length)d] -> %(datalen)d",
    class Share: 
    784784        # the wanted/needed span is only "wanted" for the first pass. Once
    785785        # the offset table arrives, it's all "needed".
    786786
    787     def _got_error(self, f, start, length, req_ev, lp):
    788         req_ev.finished("error", now())
     787    def _got_error(self, f, start, length, block_ev, lp):
     788        block_ev.error(now())
    789789        log.msg(format="error requesting %(start)d+%(length)d"
    790790                " from %(server)s for si %(si)s",
    791791                start=start, length=length,
  • src/allmydata/immutable/downloader/status.py

    diff --git a/src/allmydata/immutable/downloader/status.py b/src/allmydata/immutable/downloader/status.py
    index 3970ca1..dfaa27a 100644
    a b import itertools 
    33from zope.interface import implements
    44from allmydata.interfaces import IDownloadStatus
    55
    6 class RequestEvent:
    7     def __init__(self, download_status, tag):
    8         self._download_status = download_status
    9         self._tag = tag
    10     def finished(self, received, when):
    11         self._download_status.add_request_finished(self._tag, received, when)
     6class ReadEvent:
     7    def __init__(self, ev, ds):
     8        self._ev = ev
     9        self._ds = ds
     10    def update(self, bytes, decrypttime, pausetime):
     11        self._ev["bytes_returned"] += bytes
     12        self._ev["decrypt_time"] += decrypttime
     13        self._ev["paused_time"] += pausetime
     14    def finished(self, finishtime):
     15        self._ev["finish_time"] = finishtime
     16        self._ds.update_last_timestamp(finishtime)
     17
     18class SegmentEvent:
     19    def __init__(self, ev, ds):
     20        self._ev = ev
     21        self._ds = ds
     22    def activate(self, when):
     23        if self._ev["active_time"] is None:
     24            self._ev["active_time"] = when
     25    def deliver(self, when, start, length, decodetime):
     26        assert self._ev["active_time"] is not None
     27        self._ev["finish_time"] = when
     28        self._ev["success"] = True
     29        self._ev["decode_time"] = decodetime
     30        self._ev["segment_start"] = start
     31        self._ev["segment_length"] = length
     32        self._ds.update_last_timestamp(when)
     33    def error(self, when):
     34        self._ev["finish_time"] = when
     35        self._ev["success"] = False
     36        self._ds.update_last_timestamp(when)
    1237
    1338class DYHBEvent:
    14     def __init__(self, download_status, tag):
    15         self._download_status = download_status
    16         self._tag = tag
     39    def __init__(self, ev, ds):
     40        self._ev = ev
     41        self._ds = ds
     42    def error(self, when):
     43        self._ev["finish_time"] = when
     44        self._ev["success"] = False
     45        self._ds.update_last_timestamp(when)
    1746    def finished(self, shnums, when):
    18         self._download_status.add_dyhb_finished(self._tag, shnums, when)
     47        self._ev["finish_time"] = when
     48        self._ev["success"] = True
     49        self._ev["response_shnums"] = shnums
     50        self._ds.update_last_timestamp(when)
     51
     52class BlockRequestEvent:
     53    def __init__(self, ev, ds):
     54        self._ev = ev
     55        self._ds = ds
     56    def finished(self, received, when):
     57        self._ev["finish_time"] = when
     58        self._ev["success"] = True
     59        self._ev["response_length"] = received
     60        self._ds.update_last_timestamp(when)
     61    def error(self, when):
     62        self._ev["finish_time"] = when
     63        self._ev["success"] = False
     64        self._ds.update_last_timestamp(when)
    1965
    20 class ReadEvent:
    21     def __init__(self, download_status, tag):
    22         self._download_status = download_status
    23         self._tag = tag
    24     def update(self, bytes, decrypttime, pausetime):
    25         self._download_status.update_read_event(self._tag, bytes,
    26                                                 decrypttime, pausetime)
    27     def finished(self, finishtime):
    28         self._download_status.finish_read_event(self._tag, finishtime)
    2966
    3067class DownloadStatus:
    3168    # There is one DownloadStatus for each CiphertextFileNode. The status
    class DownloadStatus: 
    3875        self.size = size
    3976        self.counter = self.statusid_counter.next()
    4077        self.helper = False
    41         self.started = None
    42         # self.dyhb_requests tracks "do you have a share" requests and
    43         # responses. It maps serverid to a tuple of:
    44         #  send time
    45         #  tuple of response shnums (None if response hasn't arrived, "error")
    46         #  response time (None if response hasn't arrived yet)
    47         self.dyhb_requests = {}
    48 
    49         # self.requests tracks share-data requests and responses. It maps
    50         # serverid to a tuple of:
    51         #  shnum,
    52         #  start,length,  (of data requested)
    53         #  send time
    54         #  response length (None if reponse hasn't arrived yet, or "error")
    55         #  response time (None if response hasn't arrived)
    56         self.requests = {}
    57 
    58         # self.segment_events tracks segment requests and delivery. It is a
    59         # list of:
    60         #  type ("request", "delivery", "error")
    61         #  segment number
    62         #  event time
    63         #  segment start (file offset of first byte, None except in "delivery")
    64         #  segment length (only in "delivery")
    65         #  time spent in decode (only in "delivery")
    66         self.segment_events = []
    6778
    68         # self.read_events tracks read() requests. It is a list of:
     79        self.first_timestamp = None
     80        self.last_timestamp = None
     81
     82        # all four of these _events lists are sorted by start_time, because
     83        # they are strictly append-only (some elements are later mutated in
     84        # place, but none are removed or inserted in the middle).
     85
     86        # self.read_events tracks read() requests. It is a list of dicts,
     87        # each with the following keys:
    6988        #  start,length  (of data requested)
    70         #  request time
    71         #  finish time (None until finished)
    72         #  bytes returned (starts at 0, grows as segments are delivered)
    73         #  time spent in decrypt (None for ciphertext-only reads)
    74         #  time spent paused
     89        #  start_time
     90        #  finish_time (None until finished)
     91        #  bytes_returned (starts at 0, grows as segments are delivered)
     92        #  decrypt_time (time spent in decrypt, None for ciphertext-only reads)
     93        #  paused_time (time spent paused by client via pauseProducing)
    7594        self.read_events = []
    7695
     96        # self.segment_events tracks segment requests and their resolution.
     97        # It is a list of dicts:
     98        #  segment_number
     99        #  start_time
     100        #  active_time (None until work has begun)
     101        #  decode_time (time spent in decode, None until delievered)
     102        #  finish_time (None until resolved)
     103        #  success (None until resolved, then boolean)
     104        #  segment_start (file offset of first byte, None until delivered)
     105        #  segment_length (None until delivered)
     106        self.segment_events = []
     107
     108        # self.dyhb_requests tracks "do you have a share" requests and
     109        # responses. It is a list of dicts:
     110        #  serverid (binary)
     111        #  start_time
     112        #  success (None until resolved, then boolean)
     113        #  response_shnums (tuple, None until successful)
     114        #  finish_time (None until resolved)
     115        self.dyhb_requests = []
     116
     117        # self.block_requests tracks share-data requests and responses. It is
     118        # a list of dicts:
     119        #  serverid (binary),
     120        #  shnum,
     121        #  start,length,  (of data requested)
     122        #  start_time
     123        #  finish_time (None until resolved)
     124        #  success (None until resolved, then bool)
     125        #  response_length (None until success)
     126        self.block_requests = []
     127
    77128        self.known_shares = [] # (serverid, shnum)
    78129        self.problems = []
    79130
    80131
    81     def add_dyhb_sent(self, serverid, when):
    82         r = (when, None, None)
    83         if serverid not in self.dyhb_requests:
    84             self.dyhb_requests[serverid] = []
    85         self.dyhb_requests[serverid].append(r)
    86         tag = (serverid, len(self.dyhb_requests[serverid])-1)
    87         return DYHBEvent(self, tag)
    88 
    89     def add_dyhb_finished(self, tag, shnums, when):
    90         # received="error" on error, else tuple(shnums)
    91         (serverid, index) = tag
    92         r = self.dyhb_requests[serverid][index]
    93         (sent, _, _) = r
    94         r = (sent, shnums, when)
    95         self.dyhb_requests[serverid][index] = r
    96 
    97     def add_request_sent(self, serverid, shnum, start, length, when):
    98         r = (shnum, start, length, when, None, None)
    99         if serverid not in self.requests:
    100             self.requests[serverid] = []
    101         self.requests[serverid].append(r)
    102         tag = (serverid, len(self.requests[serverid])-1)
    103         return RequestEvent(self, tag)
    104 
    105     def add_request_finished(self, tag, received, when):
    106         # received="error" on error, else len(data)
    107         (serverid, index) = tag
    108         r = self.requests[serverid][index]
    109         (shnum, start, length, sent, _, _) = r
    110         r = (shnum, start, length, sent, received, when)
    111         self.requests[serverid][index] = r
     132    def add_read_event(self, start, length, when):
     133        if self.first_timestamp is None:
     134            self.first_timestamp = when
     135        r = { "start": start,
     136              "length": length,
     137              "start_time": when,
     138              "finish_time": None,
     139              "bytes_returned": 0,
     140              "decrypt_time": 0,
     141              "paused_time": 0,
     142              }
     143        self.read_events.append(r)
     144        return ReadEvent(r, self)
    112145
    113146    def add_segment_request(self, segnum, when):
    114         if self.started is None:
    115             self.started = when
    116         r = ("request", segnum, when, None, None, None)
    117         self.segment_events.append(r)
    118     def add_segment_delivery(self, segnum, when, start, length, decodetime):
    119         r = ("delivery", segnum, when, start, length, decodetime)
    120         self.segment_events.append(r)
    121     def add_segment_error(self, segnum, when):
    122         r = ("error", segnum, when, None, None, None)
     147        if self.first_timestamp is None:
     148            self.first_timestamp = when
     149        r = { "segment_number": segnum,
     150              "start_time": when,
     151              "active_time": None,
     152              "finish_time": None,
     153              "success": None,
     154              "decode_time": None,
     155              "segment_start": None,
     156              "segment_length": None,
     157              }
    123158        self.segment_events.append(r)
    124 
    125     def add_read_event(self, start, length, when):
    126         if self.started is None:
    127             self.started = when
    128         r = (start, length, when, None, 0, 0, 0)
    129         self.read_events.append(r)
    130         tag = len(self.read_events)-1
    131         return ReadEvent(self, tag)
    132     def update_read_event(self, tag, bytes_d, decrypt_d, paused_d):
    133         r = self.read_events[tag]
    134         (start, length, requesttime, finishtime, bytes, decrypt, paused) = r
    135         bytes += bytes_d
    136         decrypt += decrypt_d
    137         paused += paused_d
    138         r = (start, length, requesttime, finishtime, bytes, decrypt, paused)
    139         self.read_events[tag] = r
    140     def finish_read_event(self, tag, finishtime):
    141         r = self.read_events[tag]
    142         (start, length, requesttime, _, bytes, decrypt, paused) = r
    143         r = (start, length, requesttime, finishtime, bytes, decrypt, paused)
    144         self.read_events[tag] = r
     159        return SegmentEvent(r, self)
     160
     161    def add_dyhb_request(self, serverid, when):
     162        r = { "serverid": serverid,
     163              "start_time": when,
     164              "success": None,
     165              "response_shnums": None,
     166              "finish_time": None,
     167              }
     168        self.dyhb_requests.append(r)
     169        return DYHBEvent(r, self)
     170
     171    def add_block_request(self, serverid, shnum, start, length, when):
     172        r = { "serverid": serverid,
     173              "shnum": shnum,
     174              "start": start,
     175              "length": length,
     176              "start_time": when,
     177              "finish_time": None,
     178              "success": None,
     179              "response_length": None,
     180              }
     181        self.block_requests.append(r)
     182        return BlockRequestEvent(r, self)
     183
     184    def update_last_timestamp(self, when):
     185        if self.last_timestamp is None or when > self.last_timestamp:
     186            self.last_timestamp = when
    145187
    146188    def add_known_share(self, serverid, shnum):
    147189        self.known_shares.append( (serverid, shnum) )
    class DownloadStatus: 
    160202        # mention all outstanding segment requests
    161203        outstanding = set()
    162204        errorful = set()
    163         for s_ev in self.segment_events:
    164             (etype, segnum, when, segstart, seglen, decodetime) = s_ev
    165             if etype == "request":
    166                 outstanding.add(segnum)
    167             elif etype == "delivery":
    168                 outstanding.remove(segnum)
    169             else: # "error"
    170                 outstanding.remove(segnum)
    171                 errorful.add(segnum)
     205        outstanding = set([s_ev["segment_number"]
     206                           for s_ev in self.segment_events
     207                           if s_ev["finish_time"] is None])
     208        errorful = set([s_ev["segment_number"]
     209                        for s_ev in self.segment_events
     210                        if s_ev["success"] is False])
    172211        def join(segnums):
    173212            if len(segnums) == 1:
    174213                return "segment %s" % list(segnums)[0]
    class DownloadStatus: 
    191230            return 0.0
    192231        total_outstanding, total_received = 0, 0
    193232        for r_ev in self.read_events:
    194             (start, length, ign1, finishtime, bytes, ign2, ign3) = r_ev
    195             if finishtime is None:
    196                 total_outstanding += length
    197                 total_received += bytes
     233            if r_ev["finish_time"] is None:
     234                total_outstanding += r_ev["length"]
     235                total_received += r_ev["bytes_returned"]
    198236            # else ignore completed requests
    199237        if not total_outstanding:
    200238            return 1.0
    class DownloadStatus: 
    205243    def get_active(self):
    206244        return False # TODO
    207245    def get_started(self):
    208         return self.started
     246        return self.first_timestamp
    209247    def get_results(self):
    210248        return None # TODO
  • src/allmydata/immutable/filenode.py

    diff --git a/src/allmydata/immutable/filenode.py b/src/allmydata/immutable/filenode.py
    index ed3785b..2a0eae1 100644
    a b class CiphertextFileNode: 
    5555        return a Deferred that fires (with the consumer) when the read is
    5656        finished."""
    5757        self._maybe_create_download_node()
    58         actual_size = size
    59         if actual_size is None:
    60             actual_size = self._verifycap.size - offset
    61         read_ev = self._download_status.add_read_event(offset, actual_size,
    62                                                       now())
     58        if size is None:
     59            size = self._verifycap.size
     60        # clip size so offset+size does not go past EOF
     61        size = min(size, self._verifycap.size-offset)
     62        read_ev = self._download_status.add_read_event(offset, size, now())
    6363        if IDownloadStatusHandlingConsumer.providedBy(consumer):
    6464            consumer.set_download_status_read_event(read_ev)
    6565        return self._node.read(consumer, offset, size, read_ev)
    class DecryptingConsumer: 
    177177
    178178    def __init__(self, consumer, readkey, offset):
    179179        self._consumer = consumer
    180         self._read_event = None
     180        self._read_ev = None
    181181        # TODO: pycryptopp CTR-mode needs random-access operations: I want
    182182        # either a=AES(readkey, offset) or better yet both of:
    183183        #  a=AES(readkey, offset=0)
    class DecryptingConsumer: 
    190190        self._decryptor.process("\x00"*offset_small)
    191191
    192192    def set_download_status_read_event(self, read_ev):
    193         self._read_event = read_ev
     193        self._read_ev = read_ev
    194194
    195195    def registerProducer(self, producer, streaming):
    196196        # this passes through, so the real consumer can flow-control the real
    class DecryptingConsumer: 
    203203    def write(self, ciphertext):
    204204        started = now()
    205205        plaintext = self._decryptor.process(ciphertext)
    206         if self._read_event:
     206        if self._read_ev:
    207207            elapsed = now() - started
    208             self._read_event.update(0, elapsed, 0)
     208            self._read_ev.update(0, elapsed, 0)
    209209        self._consumer.write(plaintext)
    210210
    211211class ImmutableFileNode:
  • src/allmydata/test/test_download.py

    diff --git a/src/allmydata/test/test_download.py b/src/allmydata/test/test_download.py
    index 40f0d62..fc84fa2 100644
    a b class Status(unittest.TestCase): 
    12221222        now = 12345.1
    12231223        ds = DownloadStatus("si-1", 123)
    12241224        self.failUnlessEqual(ds.get_status(), "idle")
    1225         ds.add_segment_request(0, now)
     1225        ev0 = ds.add_segment_request(0, now)
    12261226        self.failUnlessEqual(ds.get_status(), "fetching segment 0")
    1227         ds.add_segment_delivery(0, now+1, 0, 1000, 2.0)
     1227        ev0.activate(now+0.5)
     1228        ev0.deliver(now+1, 0, 1000, 2.0)
    12281229        self.failUnlessEqual(ds.get_status(), "idle")
    1229         ds.add_segment_request(2, now+2)
    1230         ds.add_segment_request(1, now+2)
     1230        ev2 = ds.add_segment_request(2, now+2)
     1231        ev1 = ds.add_segment_request(1, now+2)
    12311232        self.failUnlessEqual(ds.get_status(), "fetching segments 1,2")
    1232         ds.add_segment_error(1, now+3)
     1233        ev1.error(now+3)
    12331234        self.failUnlessEqual(ds.get_status(),
    12341235                             "fetching segment 2; errors on segment 1")
     1236        del ev2 # hush pyflakes
    12351237
    12361238    def test_progress(self):
    12371239        now = 12345.1
  • src/allmydata/test/test_web.py

    diff --git a/src/allmydata/test/test_web.py b/src/allmydata/test/test_web.py
    index f68e98d..cc9be0d 100644
    a b from allmydata.nodemaker import NodeMaker 
    1919from allmydata.unknown import UnknownNode
    2020from allmydata.web import status, common
    2121from allmydata.scripts.debug import CorruptShareOptions, corrupt_share
    22 from allmydata.util import fileutil, base32
     22from allmydata.util import fileutil, base32, hashutil
    2323from allmydata.util.consumer import download_to_data
    2424from allmydata.util.netstring import split_netstring
    2525from allmydata.util.encodingutil import to_str
    def build_one_ds(): 
    7878    ds = DownloadStatus("storage_index", 1234)
    7979    now = time.time()
    8080
    81     ds.add_segment_request(0, now)
    82     # segnum, when, start,len, decodetime
    83     ds.add_segment_delivery(0, now+1, 0, 100, 0.5)
    84     ds.add_segment_request(1, now+2)
    85     ds.add_segment_error(1, now+3)
     81    serverid_a = hashutil.tagged_hash("foo", "serverid_a")[:20]
     82    serverid_b = hashutil.tagged_hash("foo", "serverid_b")[:20]
     83    storage_index = hashutil.storage_index_hash("SI")
     84    e0 = ds.add_segment_request(0, now)
     85    e0.activate(now+0.5)
     86    e0.deliver(now+1, 0, 100, 0.5) # when, start,len, decodetime
     87    e1 = ds.add_segment_request(1, now+2)
     88    e1.error(now+3)
    8689    # two outstanding requests
    87     ds.add_segment_request(2, now+4)
    88     ds.add_segment_request(3, now+5)
     90    e2 = ds.add_segment_request(2, now+4)
     91    e3 = ds.add_segment_request(3, now+5)
     92    del e2,e3 # hush pyflakes
    8993
    90     # simulate a segment which gets delivered faster than a system clock tick (ticket #1166)
    91     ds.add_segment_request(4, now)
    92     ds.add_segment_delivery(4, now, 0, 140, 0.5)
     94    # simulate a segment which gets delivered faster than a system clock tick
     95    # (ticket #1166)
     96    e = ds.add_segment_request(4, now)
     97    e.activate(now)
     98    e.deliver(now, 0, 140, 0.5)
    9399
    94     e = ds.add_dyhb_sent("serverid_a", now)
     100    e = ds.add_dyhb_request(serverid_a, now)
    95101    e.finished([1,2], now+1)
    96     e = ds.add_dyhb_sent("serverid_b", now+2) # left unfinished
     102    e = ds.add_dyhb_request(serverid_b, now+2) # left unfinished
    97103
    98104    e = ds.add_read_event(0, 120, now)
    99105    e.update(60, 0.5, 0.1) # bytes, decrypttime, pausetime
    100106    e.finished(now+1)
    101107    e = ds.add_read_event(120, 30, now+2) # left unfinished
    102108
    103     e = ds.add_request_sent("serverid_a", 1, 100, 20, now)
     109    e = ds.add_block_request(serverid_a, 1, 100, 20, now)
    104110    e.finished(20, now+1)
    105     e = ds.add_request_sent("serverid_a", 1, 120, 30, now+1) # left unfinished
     111    e = ds.add_block_request(serverid_a, 1, 120, 30, now+1) # left unfinished
    106112
    107113    # make sure that add_read_event() can come first too
    108     ds1 = DownloadStatus("storage_index", 1234)
     114    ds1 = DownloadStatus(storage_index, 1234)
    109115    e = ds1.add_read_event(0, 120, now)
    110116    e.update(60, 0.5, 0.1) # bytes, decrypttime, pausetime
    111117    e.finished(now+1)
    class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi 
    554560        def _check_dl(res):
    555561            self.failUnless("File Download Status" in res, res)
    556562        d.addCallback(_check_dl)
    557         d.addCallback(lambda res: self.GET("/status/down-%d?t=json" % dl_num))
     563        d.addCallback(lambda res: self.GET("/status/down-%d/event_json" % dl_num))
    558564        def _check_dl_json(res):
    559565            data = simplejson.loads(res)
    560566            self.failUnless(isinstance(data, dict))
     567            # this data comes from build_one_ds() above
     568            self.failUnlessEqual(set(data["serverids"].values()),
     569                                 set(["phwr", "cmpu"]))
     570            self.failUnlessEqual(len(data["segment"]), 5)
     571            self.failUnlessEqual(len(data["read"]), 2)
    561572        d.addCallback(_check_dl_json)
    562573        d.addCallback(lambda res: self.GET("/status/up-%d" % ul_num))
    563574        def _check_ul(res):
  • new file src/allmydata/web/download-status-timeline.xhtml

    diff --git a/src/allmydata/web/download-status-timeline.xhtml b/src/allmydata/web/download-status-timeline.xhtml
    new file mode 100644
    index 0000000..abc7145
    - +  
     1<html xmlns:n="http://nevow.com/ns/nevow/0.1">
     2  <head>
     3    <title>AllMyData - Tahoe - File Download Status Timeline</title>
     4    <link href="/tahoe_css" rel="stylesheet" type="text/css"/>
     5    <link href="/webform_css" rel="stylesheet" type="text/css"/>
     6    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     7    <script type="text/javascript" src="/jquery.js"></script>
     8    <script type="text/javascript" src="/protovis-r3.2.js"></script>
     9    <script type="text/javascript" src="/download_status_timeline.js"></script>
     10  </head>
     11  <body>
     12
     13<h1>File Download Status</h1>
     14
     15<ul>
     16  <li>Started: <span n:render="started"/></li>
     17  <li>Storage Index: <span n:render="si"/></li>
     18  <li>Helper?: <span n:render="helper"/></li>
     19  <li>Total Size: <span n:render="total_size"/></li>
     20  <li>Progress: <span n:render="progress"/></li>
     21  <li>Status: <span n:render="status"/></li>
     22</ul>
     23
     24
     25<div style="">
     26  <div id="overview" style="float:right;width:166px;height:100px; border: 1px solid #ddd">overview</div>
     27  <div id="timeline" style="width:600px;border: 1px solid #aaa">Timeline</div>
     28</div>
     29
     30<div>Return to the <a href="/">Welcome Page</a></div>
     31
     32  </body>
     33</html>
  • src/allmydata/web/download-status.xhtml

    diff --git a/src/allmydata/web/download-status.xhtml b/src/allmydata/web/download-status.xhtml
    index 30abfca..b3560ed 100644
    a b  
    1616  <li>Total Size: <span n:render="total_size"/></li>
    1717  <li>Progress: <span n:render="progress"/></li>
    1818  <li>Status: <span n:render="status"/></li>
     19  <li><span n:render="timeline_link"/></li>
    1920</ul>
    2021
    2122<div n:render="events"></div>
  • new file src/allmydata/web/download_status_timeline.js

    diff --git a/src/allmydata/web/download_status_timeline.js b/src/allmydata/web/download_status_timeline.js
    new file mode 100644
    index 0000000..ca48eb1
    - +  
     1
     2$(function() {
     3
     4      function onDataReceived(data) {
     5          var bounds = { min: data.bounds.min,
     6                         max: data.bounds.max
     7                       };
     8          //bounds.max = data.dyhb[data.dyhb.length-1].finish_time;
     9          var duration = bounds.max - bounds.min;
     10          var WIDTH = 600;
     11          var vis = new pv.Panel().canvas("timeline").margin(30);
     12
     13          var dyhb_top = 0;
     14          var read_top = dyhb_top + 30*data.dyhb[data.dyhb.length-1].row+60;
     15          var segment_top = read_top + 30*data.read[data.read.length-1].row+60;
     16          var block_top = segment_top + 30*data.segment[data.segment.length-1].row+60;
     17          var block_row_to_y = {};
     18          var row_y=0;
     19          for (var group=0; group < data.block_rownums.length; group++) {
     20              for (var row=0; row < data.block_rownums[group]; row++) {
     21                  block_row_to_y[group+"-"+row] = row_y;
     22                  row_y += 10;
     23              }
     24              row_y += 5;
     25          }
     26
     27          var height = block_top + row_y;
     28          var kx = bounds.min;
     29          var ky = 1;
     30          var x = pv.Scale.linear(bounds.min, bounds.max).range(0, WIDTH-40);
     31          var relx = pv.Scale.linear(0, duration).range(0, WIDTH-40);
     32          //var y = pv.Scale.linear(-ky,ky).range(0, height);
     33          //x.nice(); relx.nice();
     34
     35          /* add the invisible panel now, at the bottom of the stack, so that
     36          it won't steal mouseover events and prevent tooltips from
     37          working. */
     38          vis.add(pv.Panel)
     39              .events("all")
     40              .event("mousedown", pv.Behavior.pan())
     41              .event("mousewheel", pv.Behavior.zoom())
     42              .event("pan", transform)
     43              .event("zoom", transform)
     44          ;
     45
     46          vis.anchor("top").top(-20).add(pv.Label).text("DYHB Requests");
     47
     48          vis.add(pv.Bar)
     49              .data(data.dyhb)
     50              .height(20)
     51              .top(function (d) {return 30*d.row;})
     52              .left(function(d){return x(d.start_time);})
     53              .width(function(d){return x(d.finish_time)-x(d.start_time);})
     54              .title(function(d){return "shnums: "+d.response_shnums;})
     55              .fillStyle(function(d){return data.server_info[d.serverid].color;})
     56              .strokeStyle("black").lineWidth(1);
     57
     58          vis.add(pv.Rule)
     59              .data(data.dyhb)
     60              .top(function(d){return 30*d.row + 20/2;})
     61              .left(0).width(0)
     62              .strokeStyle("#888")
     63              .anchor("left").add(pv.Label)
     64              .text(function(d){return d.serverid.slice(0,4);});
     65
     66          /* we use a function for data=relx.ticks() here instead of
     67           simply .data(relx.ticks()) so that it will be recalculated when
     68           the scales change (by pan/zoom) */
     69          var xaxis = vis.add(pv.Rule)
     70              .data(function() {return relx.ticks();})
     71              .strokeStyle("#ccc")
     72              .left(relx)
     73              .anchor("bottom").add(pv.Label)
     74              .text(function(d){return relx.tickFormat(d)+"s";});
     75
     76          var read = vis.add(pv.Panel).top(read_top);
     77          read.anchor("top").top(-20).add(pv.Label).text("read() requests");
     78
     79          read.add(pv.Bar)
     80              .data(data.read)
     81              .height(20)
     82              .top(function (d) {return 30*d.row;})
     83              .left(function(d){return x(d.start_time);})
     84              .width(function(d){return x(d.finish_time)-x(d.start_time);})
     85              .title(function(d){return "read(start="+d.start+", len="+d.length+") -> "+d.bytes_returned+" bytes";})
     86              .fillStyle("red")
     87              .strokeStyle("black").lineWidth(1);
     88
     89          var segment = vis.add(pv.Panel).top(segment_top);
     90          segment.anchor("top").top(-20).add(pv.Label).text("segment() requests");
     91
     92          segment.add(pv.Bar)
     93              .data(data.segment)
     94              .height(20)
     95              .top(function (d) {return 30*d.row;})
     96              .left(function(d){return x(d.start_time);})
     97              .width(function(d){return x(d.finish_time)-x(d.start_time);})
     98              .title(function(d){return "seg"+d.segment_number+" ["+d.segment_start+":+"+d.segment_length+"] (took "+(d.finish_time-d.start_time)+")";})
     99              .fillStyle(function(d){if (d.success) return "#c0ffc0";
     100                                    else return "#ffc0c0";})
     101              .strokeStyle("black").lineWidth(1);
     102
     103          var block = vis.add(pv.Panel).top(block_top);
     104          block.anchor("top").top(-20).add(pv.Label).text("block() requests");
     105
     106          var shnum_colors = pv.Colors.category10();
     107          block.add(pv.Bar)
     108              .data(data.block)
     109              .height(10)
     110              .top(function (d) {return block_row_to_y[d.row[0]+"-"+d.row[1]];})
     111              .left(function(d){return x(d.start_time);})
     112              .width(function(d){return x(d.finish_time)-x(d.start_time);})
     113              .title(function(d){return "sh"+d.shnum+"-on-"+d.serverid.slice(0,4)+" ["+d.start+":+"+d.length+"] -> "+d.response_length;})
     114              .fillStyle(function(d){return data.server_info[d.serverid].color;})
     115              .strokeStyle(function(d){return shnum_colors(d.shnum).color;})
     116              .lineWidth(function(d)
     117                         {if (d.response_length > 100) return 3;
     118                         else return 1;
     119                          })
     120          ;
     121
     122
     123          vis.height(height);
     124
     125          function transform() {
     126              var t0= this.transform();
     127              var t = this.transform().invert();
     128              // when t.x=0 and t.k=1.0, left should be bounds.min
     129              x.domain(bounds.min + (t.x/WIDTH)*duration,
     130                       bounds.min + t.k*duration + (t.x/WIDTH)*duration);
     131              relx.domain(0 + t.x/WIDTH*duration,
     132                          t.k*duration + (t.x/WIDTH)*duration);
     133              vis.render();
     134          }
     135
     136          vis.render();
     137      }
     138
     139      $.ajax({url: "event_json",
     140              method: 'GET',
     141              dataType: 'json',
     142              success: onDataReceived });
     143});
     144
  • new file src/allmydata/web/jquery.js

    diff --git a/src/allmydata/web/jquery.js b/src/allmydata/web/jquery.js
    new file mode 100644
    index 0000000..9263574
    - +  
     1/*!
     2 * jQuery JavaScript Library v1.3.2
     3 * http://jquery.com/
     4 *
     5 * Copyright (c) 2009 John Resig
     6 * Dual licensed under the MIT and GPL licenses.
     7 * http://docs.jquery.com/License
     8 *
     9 * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
     10 * Revision: 6246
     11 */
     12(function(){
     13
     14var
     15        // Will speed up references to window, and allows munging its name.
     16        window = this,
     17        // Will speed up references to undefined, and allows munging its name.
     18        undefined,
     19        // Map over jQuery in case of overwrite
     20        _jQuery = window.jQuery,
     21        // Map over the $ in case of overwrite
     22        _$ = window.$,
     23
     24        jQuery = window.jQuery = window.$ = function( selector, context ) {
     25                // The jQuery object is actually just the init constructor 'enhanced'
     26                return new jQuery.fn.init( selector, context );
     27        },
     28
     29        // A simple way to check for HTML strings or ID strings
     30        // (both of which we optimize for)
     31        quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,
     32        // Is it a simple selector
     33        isSimple = /^.[^:#\[\.,]*$/;
     34
     35jQuery.fn = jQuery.prototype = {
     36        init: function( selector, context ) {
     37                // Make sure that a selection was provided
     38                selector = selector || document;
     39
     40                // Handle $(DOMElement)
     41                if ( selector.nodeType ) {
     42                        this[0] = selector;
     43                        this.length = 1;
     44                        this.context = selector;
     45                        return this;
     46                }
     47                // Handle HTML strings
     48                if ( typeof selector === "string" ) {
     49                        // Are we dealing with HTML string or an ID?
     50                        var match = quickExpr.exec( selector );
     51
     52                        // Verify a match, and that no context was specified for #id
     53                        if ( match && (match[1] || !context) ) {
     54
     55                                // HANDLE: $(html) -> $(array)
     56                                if ( match[1] )
     57                                        selector = jQuery.clean( [ match[1] ], context );
     58
     59                                // HANDLE: $("#id")
     60                                else {
     61                                        var elem = document.getElementById( match[3] );
     62
     63                                        // Handle the case where IE and Opera return items
     64                                        // by name instead of ID
     65                                        if ( elem && elem.id != match[3] )
     66                                                return jQuery().find( selector );
     67
     68                                        // Otherwise, we inject the element directly into the jQuery object
     69                                        var ret = jQuery( elem || [] );
     70                                        ret.context = document;
     71                                        ret.selector = selector;
     72                                        return ret;
     73                                }
     74
     75                        // HANDLE: $(expr, [context])
     76                        // (which is just equivalent to: $(content).find(expr)
     77                        } else
     78                                return jQuery( context ).find( selector );
     79
     80                // HANDLE: $(function)
     81                // Shortcut for document ready
     82                } else if ( jQuery.isFunction( selector ) )
     83                        return jQuery( document ).ready( selector );
     84
     85                // Make sure that old selector state is passed along
     86                if ( selector.selector && selector.context ) {
     87                        this.selector = selector.selector;
     88                        this.context = selector.context;
     89                }
     90
     91                return this.setArray(jQuery.isArray( selector ) ?
     92                        selector :
     93                        jQuery.makeArray(selector));
     94        },
     95
     96        // Start with an empty selector
     97        selector: "",
     98
     99        // The current version of jQuery being used
     100        jquery: "1.3.2",
     101
     102        // The number of elements contained in the matched element set
     103        size: function() {
     104                return this.length;
     105        },
     106
     107        // Get the Nth element in the matched element set OR
     108        // Get the whole matched element set as a clean array
     109        get: function( num ) {
     110                return num === undefined ?
     111
     112                        // Return a 'clean' array
     113                        Array.prototype.slice.call( this ) :
     114
     115                        // Return just the object
     116                        this[ num ];
     117        },
     118
     119        // Take an array of elements and push it onto the stack
     120        // (returning the new matched element set)
     121        pushStack: function( elems, name, selector ) {
     122                // Build a new jQuery matched element set
     123                var ret = jQuery( elems );
     124
     125                // Add the old object onto the stack (as a reference)
     126                ret.prevObject = this;
     127
     128                ret.context = this.context;
     129
     130                if ( name === "find" )
     131                        ret.selector = this.selector + (this.selector ? " " : "") + selector;
     132                else if ( name )
     133                        ret.selector = this.selector + "." + name + "(" + selector + ")";
     134
     135                // Return the newly-formed element set
     136                return ret;
     137        },
     138
     139        // Force the current matched set of elements to become
     140        // the specified array of elements (destroying the stack in the process)
     141        // You should use pushStack() in order to do this, but maintain the stack
     142        setArray: function( elems ) {
     143                // Resetting the length to 0, then using the native Array push
     144                // is a super-fast way to populate an object with array-like properties
     145                this.length = 0;
     146                Array.prototype.push.apply( this, elems );
     147
     148                return this;
     149        },
     150
     151        // Execute a callback for every element in the matched set.
     152        // (You can seed the arguments with an array of args, but this is
     153        // only used internally.)
     154        each: function( callback, args ) {
     155                return jQuery.each( this, callback, args );
     156        },
     157
     158        // Determine the position of an element within
     159        // the matched set of elements
     160        index: function( elem ) {
     161                // Locate the position of the desired element
     162                return jQuery.inArray(
     163                        // If it receives a jQuery object, the first element is used
     164                        elem && elem.jquery ? elem[0] : elem
     165                , this );
     166        },
     167
     168        attr: function( name, value, type ) {
     169                var options = name;
     170
     171                // Look for the case where we're accessing a style value
     172                if ( typeof name === "string" )
     173                        if ( value === undefined )
     174                                return this[0] && jQuery[ type || "attr" ]( this[0], name );
     175
     176                        else {
     177                                options = {};
     178                                options[ name ] = value;
     179                        }
     180
     181                // Check to see if we're setting style values
     182                return this.each(function(i){
     183                        // Set all the styles
     184                        for ( name in options )
     185                                jQuery.attr(
     186                                        type ?
     187                                                this.style :
     188                                                this,
     189                                        name, jQuery.prop( this, options[ name ], type, i, name )
     190                                );
     191                });
     192        },
     193
     194        css: function( key, value ) {
     195                // ignore negative width and height values
     196                if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
     197                        value = undefined;
     198                return this.attr( key, value, "curCSS" );
     199        },
     200
     201        text: function( text ) {
     202                if ( typeof text !== "object" && text != null )
     203                        return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
     204
     205                var ret = "";
     206
     207                jQuery.each( text || this, function(){
     208                        jQuery.each( this.childNodes, function(){
     209                                if ( this.nodeType != 8 )
     210                                        ret += this.nodeType != 1 ?
     211                                                this.nodeValue :
     212                                                jQuery.fn.text( [ this ] );
     213                        });
     214                });
     215
     216                return ret;
     217        },
     218
     219        wrapAll: function( html ) {
     220                if ( this[0] ) {
     221                        // The elements to wrap the target around
     222                        var wrap = jQuery( html, this[0].ownerDocument ).clone();
     223
     224                        if ( this[0].parentNode )
     225                                wrap.insertBefore( this[0] );
     226
     227                        wrap.map(function(){
     228                                var elem = this;
     229
     230                                while ( elem.firstChild )
     231                                        elem = elem.firstChild;
     232
     233                                return elem;
     234                        }).append(this);
     235                }
     236
     237                return this;
     238        },
     239
     240        wrapInner: function( html ) {
     241                return this.each(function(){
     242                        jQuery( this ).contents().wrapAll( html );
     243                });
     244        },
     245
     246        wrap: function( html ) {
     247                return this.each(function(){
     248                        jQuery( this ).wrapAll( html );
     249                });
     250        },
     251
     252        append: function() {
     253                return this.domManip(arguments, true, function(elem){
     254                        if (this.nodeType == 1)
     255                                this.appendChild( elem );
     256                });
     257        },
     258
     259        prepend: function() {
     260                return this.domManip(arguments, true, function(elem){
     261                        if (this.nodeType == 1)
     262                                this.insertBefore( elem, this.firstChild );
     263                });
     264        },
     265
     266        before: function() {
     267                return this.domManip(arguments, false, function(elem){
     268                        this.parentNode.insertBefore( elem, this );
     269                });
     270        },
     271
     272        after: function() {
     273                return this.domManip(arguments, false, function(elem){
     274                        this.parentNode.insertBefore( elem, this.nextSibling );
     275                });
     276        },
     277
     278        end: function() {
     279                return this.prevObject || jQuery( [] );
     280        },
     281
     282        // For internal use only.
     283        // Behaves like an Array's method, not like a jQuery method.
     284        push: [].push,
     285        sort: [].sort,
     286        splice: [].splice,
     287
     288        find: function( selector ) {
     289                if ( this.length === 1 ) {
     290                        var ret = this.pushStack( [], "find", selector );
     291                        ret.length = 0;
     292                        jQuery.find( selector, this[0], ret );
     293                        return ret;
     294                } else {
     295                        return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){
     296                                return jQuery.find( selector, elem );
     297                        })), "find", selector );
     298                }
     299        },
     300
     301        clone: function( events ) {
     302                // Do the clone
     303                var ret = this.map(function(){
     304                        if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
     305                                // IE copies events bound via attachEvent when
     306                                // using cloneNode. Calling detachEvent on the
     307                                // clone will also remove the events from the orignal
     308                                // In order to get around this, we use innerHTML.
     309                                // Unfortunately, this means some modifications to
     310                                // attributes in IE that are actually only stored
     311                                // as properties will not be copied (such as the
     312                                // the name attribute on an input).
     313                                var html = this.outerHTML;
     314                                if ( !html ) {
     315                                        var div = this.ownerDocument.createElement("div");
     316                                        div.appendChild( this.cloneNode(true) );
     317                                        html = div.innerHTML;
     318                                }
     319
     320                                return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0];
     321                        } else
     322                                return this.cloneNode(true);
     323                });
     324
     325                // Copy the events from the original to the clone
     326                if ( events === true ) {
     327                        var orig = this.find("*").andSelf(), i = 0;
     328
     329                        ret.find("*").andSelf().each(function(){
     330                                if ( this.nodeName !== orig[i].nodeName )
     331                                        return;
     332
     333                                var events = jQuery.data( orig[i], "events" );
     334
     335                                for ( var type in events ) {
     336                                        for ( var handler in events[ type ] ) {
     337                                                jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
     338                                        }
     339                                }
     340
     341                                i++;
     342                        });
     343                }
     344
     345                // Return the cloned set
     346                return ret;
     347        },
     348
     349        filter: function( selector ) {
     350                return this.pushStack(
     351                        jQuery.isFunction( selector ) &&
     352                        jQuery.grep(this, function(elem, i){
     353                                return selector.call( elem, i );
     354                        }) ||
     355
     356                        jQuery.multiFilter( selector, jQuery.grep(this, function(elem){
     357                                return elem.nodeType === 1;
     358                        }) ), "filter", selector );
     359        },
     360
     361        closest: function( selector ) {
     362                var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null,
     363                        closer = 0;
     364
     365                return this.map(function(){
     366                        var cur = this;
     367                        while ( cur && cur.ownerDocument ) {
     368                                if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {
     369                                        jQuery.data(cur, "closest", closer);
     370                                        return cur;
     371                                }
     372                                cur = cur.parentNode;
     373                                closer++;
     374                        }
     375                });
     376        },
     377
     378        not: function( selector ) {
     379                if ( typeof selector === "string" )
     380                        // test special case where just one selector is passed in
     381                        if ( isSimple.test( selector ) )
     382                                return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector );
     383                        else
     384                                selector = jQuery.multiFilter( selector, this );
     385
     386                var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
     387                return this.filter(function() {
     388                        return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
     389                });
     390        },
     391
     392        add: function( selector ) {
     393                return this.pushStack( jQuery.unique( jQuery.merge(
     394                        this.get(),
     395                        typeof selector === "string" ?
     396                                jQuery( selector ) :
     397                                jQuery.makeArray( selector )
     398                )));
     399        },
     400
     401        is: function( selector ) {
     402                return !!selector && jQuery.multiFilter( selector, this ).length > 0;
     403        },
     404
     405        hasClass: function( selector ) {
     406                return !!selector && this.is( "." + selector );
     407        },
     408
     409        val: function( value ) {
     410                if ( value === undefined ) {                   
     411                        var elem = this[0];
     412
     413                        if ( elem ) {
     414                                if( jQuery.nodeName( elem, 'option' ) )
     415                                        return (elem.attributes.value || {}).specified ? elem.value : elem.text;
     416                               
     417                                // We need to handle select boxes special
     418                                if ( jQuery.nodeName( elem, "select" ) ) {
     419                                        var index = elem.selectedIndex,
     420                                                values = [],
     421                                                options = elem.options,
     422                                                one = elem.type == "select-one";
     423
     424                                        // Nothing was selected
     425                                        if ( index < 0 )
     426                                                return null;
     427
     428                                        // Loop through all the selected options
     429                                        for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
     430                                                var option = options[ i ];
     431
     432                                                if ( option.selected ) {
     433                                                        // Get the specifc value for the option
     434                                                        value = jQuery(option).val();
     435
     436                                                        // We don't need an array for one selects
     437                                                        if ( one )
     438                                                                return value;
     439
     440                                                        // Multi-Selects return an array
     441                                                        values.push( value );
     442                                                }
     443                                        }
     444
     445                                        return values;                         
     446                                }
     447
     448                                // Everything else, we just grab the value
     449                                return (elem.value || "").replace(/\r/g, "");
     450
     451                        }
     452
     453                        return undefined;
     454                }
     455
     456                if ( typeof value === "number" )
     457                        value += '';
     458
     459                return this.each(function(){
     460                        if ( this.nodeType != 1 )
     461                                return;
     462
     463                        if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) )
     464                                this.checked = (jQuery.inArray(this.value, value) >= 0 ||
     465                                        jQuery.inArray(this.name, value) >= 0);
     466
     467                        else if ( jQuery.nodeName( this, "select" ) ) {
     468                                var values = jQuery.makeArray(value);
     469
     470                                jQuery( "option", this ).each(function(){
     471                                        this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
     472                                                jQuery.inArray( this.text, values ) >= 0);
     473                                });
     474
     475                                if ( !values.length )
     476                                        this.selectedIndex = -1;
     477
     478                        } else
     479                                this.value = value;
     480                });
     481        },
     482
     483        html: function( value ) {
     484                return value === undefined ?
     485                        (this[0] ?
     486                                this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") :
     487                                null) :
     488                        this.empty().append( value );
     489        },
     490
     491        replaceWith: function( value ) {
     492                return this.after( value ).remove();
     493        },
     494
     495        eq: function( i ) {
     496                return this.slice( i, +i + 1 );
     497        },
     498
     499        slice: function() {
     500                return this.pushStack( Array.prototype.slice.apply( this, arguments ),
     501                        "slice", Array.prototype.slice.call(arguments).join(",") );
     502        },
     503
     504        map: function( callback ) {
     505                return this.pushStack( jQuery.map(this, function(elem, i){
     506                        return callback.call( elem, i, elem );
     507                }));
     508        },
     509
     510        andSelf: function() {
     511                return this.add( this.prevObject );
     512        },
     513
     514        domManip: function( args, table, callback ) {
     515                if ( this[0] ) {
     516                        var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(),
     517                                scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ),
     518                                first = fragment.firstChild;
     519
     520                        if ( first )
     521                                for ( var i = 0, l = this.length; i < l; i++ )
     522                                        callback.call( root(this[i], first), this.length > 1 || i > 0 ?
     523                                                        fragment.cloneNode(true) : fragment );
     524               
     525                        if ( scripts )
     526                                jQuery.each( scripts, evalScript );
     527                }
     528
     529                return this;
     530               
     531                function root( elem, cur ) {
     532                        return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ?
     533                                (elem.getElementsByTagName("tbody")[0] ||
     534                                elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
     535                                elem;
     536                }
     537        }
     538};
     539
     540// Give the init function the jQuery prototype for later instantiation
     541jQuery.fn.init.prototype = jQuery.fn;
     542
     543function evalScript( i, elem ) {
     544        if ( elem.src )
     545                jQuery.ajax({
     546                        url: elem.src,
     547                        async: false,
     548                        dataType: "script"
     549                });
     550
     551        else
     552                jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
     553
     554        if ( elem.parentNode )
     555                elem.parentNode.removeChild( elem );
     556}
     557
     558function now(){
     559        return +new Date;
     560}
     561
     562jQuery.extend = jQuery.fn.extend = function() {
     563        // copy reference to target object
     564        var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
     565
     566        // Handle a deep copy situation
     567        if ( typeof target === "boolean" ) {
     568                deep = target;
     569                target = arguments[1] || {};
     570                // skip the boolean and the target
     571                i = 2;
     572        }
     573
     574        // Handle case when target is a string or something (possible in deep copy)
     575        if ( typeof target !== "object" && !jQuery.isFunction(target) )
     576                target = {};
     577
     578        // extend jQuery itself if only one argument is passed
     579        if ( length == i ) {
     580                target = this;
     581                --i;
     582        }
     583
     584        for ( ; i < length; i++ )
     585                // Only deal with non-null/undefined values
     586                if ( (options = arguments[ i ]) != null )
     587                        // Extend the base object
     588                        for ( var name in options ) {
     589                                var src = target[ name ], copy = options[ name ];
     590
     591                                // Prevent never-ending loop
     592                                if ( target === copy )
     593                                        continue;
     594
     595                                // Recurse if we're merging object values
     596                                if ( deep && copy && typeof copy === "object" && !copy.nodeType )
     597                                        target[ name ] = jQuery.extend( deep,
     598                                                // Never move original objects, clone them
     599                                                src || ( copy.length != null ? [ ] : { } )
     600                                        , copy );
     601
     602                                // Don't bring in undefined values
     603                                else if ( copy !== undefined )
     604                                        target[ name ] = copy;
     605
     606                        }
     607
     608        // Return the modified object
     609        return target;
     610};
     611
     612// exclude the following css properties to add px
     613var     exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
     614        // cache defaultView
     615        defaultView = document.defaultView || {},
     616        toString = Object.prototype.toString;
     617
     618jQuery.extend({
     619        noConflict: function( deep ) {
     620                window.$ = _$;
     621
     622                if ( deep )
     623                        window.jQuery = _jQuery;
     624
     625                return jQuery;
     626        },
     627
     628        // See test/unit/core.js for details concerning isFunction.
     629        // Since version 1.3, DOM methods and functions like alert
     630        // aren't supported. They return false on IE (#2968).
     631        isFunction: function( obj ) {
     632                return toString.call(obj) === "[object Function]";
     633        },
     634
     635        isArray: function( obj ) {
     636                return toString.call(obj) === "[object Array]";
     637        },
     638
     639        // check if an element is in a (or is an) XML document
     640        isXMLDoc: function( elem ) {
     641                return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
     642                        !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );
     643        },
     644
     645        // Evalulates a script in a global context
     646        globalEval: function( data ) {
     647                if ( data && /\S/.test(data) ) {
     648                        // Inspired by code by Andrea Giammarchi
     649                        // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
     650                        var head = document.getElementsByTagName("head")[0] || document.documentElement,
     651                                script = document.createElement("script");
     652
     653                        script.type = "text/javascript";
     654                        if ( jQuery.support.scriptEval )
     655                                script.appendChild( document.createTextNode( data ) );
     656                        else
     657                                script.text = data;
     658
     659                        // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
     660                        // This arises when a base node is used (#2709).
     661                        head.insertBefore( script, head.firstChild );
     662                        head.removeChild( script );
     663                }
     664        },
     665
     666        nodeName: function( elem, name ) {
     667                return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
     668        },
     669
     670        // args is for internal usage only
     671        each: function( object, callback, args ) {
     672                var name, i = 0, length = object.length;
     673
     674                if ( args ) {
     675                        if ( length === undefined ) {
     676                                for ( name in object )
     677                                        if ( callback.apply( object[ name ], args ) === false )
     678                                                break;
     679                        } else
     680                                for ( ; i < length; )
     681                                        if ( callback.apply( object[ i++ ], args ) === false )
     682                                                break;
     683
     684                // A special, fast, case for the most common use of each
     685                } else {
     686                        if ( length === undefined ) {
     687                                for ( name in object )
     688                                        if ( callback.call( object[ name ], name, object[ name ] ) === false )
     689                                                break;
     690                        } else
     691                                for ( var value = object[0];
     692                                        i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
     693                }
     694
     695                return object;
     696        },
     697
     698        prop: function( elem, value, type, i, name ) {
     699                // Handle executable functions
     700                if ( jQuery.isFunction( value ) )
     701                        value = value.call( elem, i );
     702
     703                // Handle passing in a number to a CSS property
     704                return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ?
     705                        value + "px" :
     706                        value;
     707        },
     708
     709        className: {
     710                // internal only, use addClass("class")
     711                add: function( elem, classNames ) {
     712                        jQuery.each((classNames || "").split(/\s+/), function(i, className){
     713                                if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
     714                                        elem.className += (elem.className ? " " : "") + className;
     715                        });
     716                },
     717
     718                // internal only, use removeClass("class")
     719                remove: function( elem, classNames ) {
     720                        if (elem.nodeType == 1)
     721                                elem.className = classNames !== undefined ?
     722                                        jQuery.grep(elem.className.split(/\s+/), function(className){
     723                                                return !jQuery.className.has( classNames, className );
     724                                        }).join(" ") :
     725                                        "";
     726                },
     727
     728                // internal only, use hasClass("class")
     729                has: function( elem, className ) {
     730                        return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
     731                }
     732        },
     733
     734        // A method for quickly swapping in/out CSS properties to get correct calculations
     735        swap: function( elem, options, callback ) {
     736                var old = {};
     737                // Remember the old values, and insert the new ones
     738                for ( var name in options ) {
     739                        old[ name ] = elem.style[ name ];
     740                        elem.style[ name ] = options[ name ];
     741                }
     742
     743                callback.call( elem );
     744
     745                // Revert the old values
     746                for ( var name in options )
     747                        elem.style[ name ] = old[ name ];
     748        },
     749
     750        css: function( elem, name, force, extra ) {
     751                if ( name == "width" || name == "height" ) {
     752                        var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
     753
     754                        function getWH() {
     755                                val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
     756
     757                                if ( extra === "border" )
     758                                        return;
     759
     760                                jQuery.each( which, function() {
     761                                        if ( !extra )
     762                                                val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
     763                                        if ( extra === "margin" )
     764                                                val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
     765                                        else
     766                                                val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
     767                                });
     768                        }
     769
     770                        if ( elem.offsetWidth !== 0 )
     771                                getWH();
     772                        else
     773                                jQuery.swap( elem, props, getWH );
     774
     775                        return Math.max(0, Math.round(val));
     776                }
     777
     778                return jQuery.curCSS( elem, name, force );
     779        },
     780
     781        curCSS: function( elem, name, force ) {
     782                var ret, style = elem.style;
     783
     784                // We need to handle opacity special in IE
     785                if ( name == "opacity" && !jQuery.support.opacity ) {
     786                        ret = jQuery.attr( style, "opacity" );
     787
     788                        return ret == "" ?
     789                                "1" :
     790                                ret;
     791                }
     792
     793                // Make sure we're using the right name for getting the float value
     794                if ( name.match( /float/i ) )
     795                        name = styleFloat;
     796
     797                if ( !force && style && style[ name ] )
     798                        ret = style[ name ];
     799
     800                else if ( defaultView.getComputedStyle ) {
     801
     802                        // Only "float" is needed here
     803                        if ( name.match( /float/i ) )
     804                                name = "float";
     805
     806                        name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
     807
     808                        var computedStyle = defaultView.getComputedStyle( elem, null );
     809
     810                        if ( computedStyle )
     811                                ret = computedStyle.getPropertyValue( name );
     812
     813                        // We should always get a number back from opacity
     814                        if ( name == "opacity" && ret == "" )
     815                                ret = "1";
     816
     817                } else if ( elem.currentStyle ) {
     818                        var camelCase = name.replace(/\-(\w)/g, function(all, letter){
     819                                return letter.toUpperCase();
     820                        });
     821
     822                        ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
     823
     824                        // From the awesome hack by Dean Edwards
     825                        // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
     826
     827                        // If we're not dealing with a regular pixel number
     828                        // but a number that has a weird ending, we need to convert it to pixels
     829                        if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
     830                                // Remember the original values
     831                                var left = style.left, rsLeft = elem.runtimeStyle.left;
     832
     833                                // Put in the new values to get a computed value out
     834                                elem.runtimeStyle.left = elem.currentStyle.left;
     835                                style.left = ret || 0;
     836                                ret = style.pixelLeft + "px";
     837
     838                                // Revert the changed values
     839                                style.left = left;
     840                                elem.runtimeStyle.left = rsLeft;
     841                        }
     842                }
     843
     844                return ret;
     845        },
     846
     847        clean: function( elems, context, fragment ) {
     848                context = context || document;
     849
     850                // !context.createElement fails in IE with an error but returns typeof 'object'
     851                if ( typeof context.createElement === "undefined" )
     852                        context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
     853
     854                // If a single string is passed in and it's a single tag
     855                // just do a createElement and skip the rest
     856                if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) {
     857                        var match = /^<(\w+)\s*\/?>$/.exec(elems[0]);
     858                        if ( match )
     859                                return [ context.createElement( match[1] ) ];
     860                }
     861
     862                var ret = [], scripts = [], div = context.createElement("div");
     863
     864                jQuery.each(elems, function(i, elem){
     865                        if ( typeof elem === "number" )
     866                                elem += '';
     867
     868                        if ( !elem )
     869                                return;
     870
     871                        // Convert html string into DOM nodes
     872                        if ( typeof elem === "string" ) {
     873                                // Fix "XHTML"-style tags in all browsers
     874                                elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
     875                                        return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
     876                                                all :
     877                                                front + "></" + tag + ">";
     878                                });
     879
     880                                // Trim whitespace, otherwise indexOf won't work as expected
     881                                var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase();
     882
     883                                var wrap =
     884                                        // option or optgroup
     885                                        !tags.indexOf("<opt") &&
     886                                        [ 1, "<select multiple='multiple'>", "</select>" ] ||
     887
     888                                        !tags.indexOf("<leg") &&
     889                                        [ 1, "<fieldset>", "</fieldset>" ] ||
     890
     891                                        tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
     892                                        [ 1, "<table>", "</table>" ] ||
     893
     894                                        !tags.indexOf("<tr") &&
     895                                        [ 2, "<table><tbody>", "</tbody></table>" ] ||
     896
     897                                        // <thead> matched above
     898                                        (!tags.indexOf("<td") || !tags.indexOf("<th")) &&
     899                                        [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
     900
     901                                        !tags.indexOf("<col") &&
     902                                        [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
     903
     904                                        // IE can't serialize <link> and <script> tags normally
     905                                        !jQuery.support.htmlSerialize &&
     906                                        [ 1, "div<div>", "</div>" ] ||
     907
     908                                        [ 0, "", "" ];
     909
     910                                // Go to html and back, then peel off extra wrappers
     911                                div.innerHTML = wrap[1] + elem + wrap[2];
     912
     913                                // Move to the right depth
     914                                while ( wrap[0]-- )
     915                                        div = div.lastChild;
     916
     917                                // Remove IE's autoinserted <tbody> from table fragments
     918                                if ( !jQuery.support.tbody ) {
     919
     920                                        // String was a <table>, *may* have spurious <tbody>
     921                                        var hasBody = /<tbody/i.test(elem),
     922                                                tbody = !tags.indexOf("<table") && !hasBody ?
     923                                                        div.firstChild && div.firstChild.childNodes :
     924
     925                                                // String was a bare <thead> or <tfoot>
     926                                                wrap[1] == "<table>" && !hasBody ?
     927                                                        div.childNodes :
     928                                                        [];
     929
     930                                        for ( var j = tbody.length - 1; j >= 0 ; --j )
     931                                                if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
     932                                                        tbody[ j ].parentNode.removeChild( tbody[ j ] );
     933
     934                                        }
     935
     936                                // IE completely kills leading whitespace when innerHTML is used
     937                                if ( !jQuery.support.leadingWhitespace && /^\s/.test( elem ) )
     938                                        div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
     939                               
     940                                elem = jQuery.makeArray( div.childNodes );
     941                        }
     942
     943                        if ( elem.nodeType )
     944                                ret.push( elem );
     945                        else
     946                                ret = jQuery.merge( ret, elem );
     947
     948                });
     949
     950                if ( fragment ) {
     951                        for ( var i = 0; ret[i]; i++ ) {
     952                                if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
     953                                        scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
     954                                } else {
     955                                        if ( ret[i].nodeType === 1 )
     956                                                ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
     957                                        fragment.appendChild( ret[i] );
     958                                }
     959                        }
     960                       
     961                        return scripts;
     962                }
     963
     964                return ret;
     965        },
     966
     967        attr: function( elem, name, value ) {
     968                // don't set attributes on text and comment nodes
     969                if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
     970                        return undefined;
     971
     972                var notxml = !jQuery.isXMLDoc( elem ),
     973                        // Whether we are setting (or getting)
     974                        set = value !== undefined;
     975
     976                // Try to normalize/fix the name
     977                name = notxml && jQuery.props[ name ] || name;
     978
     979                // Only do all the following if this is a node (faster for style)
     980                // IE elem.getAttribute passes even for style
     981                if ( elem.tagName ) {
     982
     983                        // These attributes require special treatment
     984                        var special = /href|src|style/.test( name );
     985
     986                        // Safari mis-reports the default selected property of a hidden option
     987                        // Accessing the parent's selectedIndex property fixes it
     988                        if ( name == "selected" && elem.parentNode )
     989                                elem.parentNode.selectedIndex;
     990
     991                        // If applicable, access the attribute via the DOM 0 way
     992                        if ( name in elem && notxml && !special ) {
     993                                if ( set ){
     994                                        // We can't allow the type property to be changed (since it causes problems in IE)
     995                                        if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
     996                                                throw "type property can't be changed";
     997
     998                                        elem[ name ] = value;
     999                                }
     1000
     1001                                // browsers index elements by id/name on forms, give priority to attributes.
     1002                                if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
     1003                                        return elem.getAttributeNode( name ).nodeValue;
     1004
     1005                                // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
     1006                                // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
     1007                                if ( name == "tabIndex" ) {
     1008                                        var attributeNode = elem.getAttributeNode( "tabIndex" );
     1009                                        return attributeNode && attributeNode.specified
     1010                                                ? attributeNode.value
     1011                                                : elem.nodeName.match(/(button|input|object|select|textarea)/i)
     1012                                                        ? 0
     1013                                                        : elem.nodeName.match(/^(a|area)$/i) && elem.href
     1014                                                                ? 0
     1015                                                                : undefined;
     1016                                }
     1017
     1018                                return elem[ name ];
     1019                        }
     1020
     1021                        if ( !jQuery.support.style && notxml &&  name == "style" )
     1022                                return jQuery.attr( elem.style, "cssText", value );
     1023
     1024                        if ( set )
     1025                                // convert the value to a string (all browsers do this but IE) see #1070
     1026                                elem.setAttribute( name, "" + value );
     1027
     1028                        var attr = !jQuery.support.hrefNormalized && notxml && special
     1029                                        // Some attributes require a special call on IE
     1030                                        ? elem.getAttribute( name, 2 )
     1031                                        : elem.getAttribute( name );
     1032
     1033                        // Non-existent attributes return null, we normalize to undefined
     1034                        return attr === null ? undefined : attr;
     1035                }
     1036
     1037                // elem is actually elem.style ... set the style
     1038
     1039                // IE uses filters for opacity
     1040                if ( !jQuery.support.opacity && name == "opacity" ) {
     1041                        if ( set ) {
     1042                                // IE has trouble with opacity if it does not have layout
     1043                                // Force it by setting the zoom level
     1044                                elem.zoom = 1;
     1045
     1046                                // Set the alpha filter to set the opacity
     1047                                elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
     1048                                        (parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
     1049                        }
     1050
     1051                        return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
     1052                                (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
     1053                                "";
     1054                }
     1055
     1056                name = name.replace(/-([a-z])/ig, function(all, letter){
     1057                        return letter.toUpperCase();
     1058                });
     1059
     1060                if ( set )
     1061                        elem[ name ] = value;
     1062
     1063                return elem[ name ];
     1064        },
     1065
     1066        trim: function( text ) {
     1067                return (text || "").replace( /^\s+|\s+$/g, "" );
     1068        },
     1069
     1070        makeArray: function( array ) {
     1071                var ret = [];
     1072
     1073                if( array != null ){
     1074                        var i = array.length;
     1075                        // The window, strings (and functions) also have 'length'
     1076                        if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
     1077                                ret[0] = array;
     1078                        else
     1079                                while( i )
     1080                                        ret[--i] = array[i];
     1081                }
     1082
     1083                return ret;
     1084        },
     1085
     1086        inArray: function( elem, array ) {
     1087                for ( var i = 0, length = array.length; i < length; i++ )
     1088                // Use === because on IE, window == document
     1089                        if ( array[ i ] === elem )
     1090                                return i;
     1091
     1092                return -1;
     1093        },
     1094
     1095        merge: function( first, second ) {
     1096                // We have to loop this way because IE & Opera overwrite the length
     1097                // expando of getElementsByTagName
     1098                var i = 0, elem, pos = first.length;
     1099                // Also, we need to make sure that the correct elements are being returned
     1100                // (IE returns comment nodes in a '*' query)
     1101                if ( !jQuery.support.getAll ) {
     1102                        while ( (elem = second[ i++ ]) != null )
     1103                                if ( elem.nodeType != 8 )
     1104                                        first[ pos++ ] = elem;
     1105
     1106                } else
     1107                        while ( (elem = second[ i++ ]) != null )
     1108                                first[ pos++ ] = elem;
     1109
     1110                return first;
     1111        },
     1112
     1113        unique: function( array ) {
     1114                var ret = [], done = {};
     1115
     1116                try {
     1117
     1118                        for ( var i = 0, length = array.length; i < length; i++ ) {
     1119                                var id = jQuery.data( array[ i ] );
     1120
     1121                                if ( !done[ id ] ) {
     1122                                        done[ id ] = true;
     1123                                        ret.push( array[ i ] );
     1124                                }
     1125                        }
     1126
     1127                } catch( e ) {
     1128                        ret = array;
     1129                }
     1130
     1131                return ret;
     1132        },
     1133
     1134        grep: function( elems, callback, inv ) {
     1135                var ret = [];
     1136
     1137                // Go through the array, only saving the items
     1138                // that pass the validator function
     1139                for ( var i = 0, length = elems.length; i < length; i++ )
     1140                        if ( !inv != !callback( elems[ i ], i ) )
     1141                                ret.push( elems[ i ] );
     1142
     1143                return ret;
     1144        },
     1145
     1146        map: function( elems, callback ) {
     1147                var ret = [];
     1148
     1149                // Go through the array, translating each of the items to their
     1150                // new value (or values).
     1151                for ( var i = 0, length = elems.length; i < length; i++ ) {
     1152                        var value = callback( elems[ i ], i );
     1153
     1154                        if ( value != null )
     1155                                ret[ ret.length ] = value;
     1156                }
     1157
     1158                return ret.concat.apply( [], ret );
     1159        }
     1160});
     1161
     1162// Use of jQuery.browser is deprecated.
     1163// It's included for backwards compatibility and plugins,
     1164// although they should work to migrate away.
     1165
     1166var userAgent = navigator.userAgent.toLowerCase();
     1167
     1168// Figure out what browser is being used
     1169jQuery.browser = {
     1170        version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
     1171        safari: /webkit/.test( userAgent ),
     1172        opera: /opera/.test( userAgent ),
     1173        msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
     1174        mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
     1175};
     1176
     1177jQuery.each({
     1178        parent: function(elem){return elem.parentNode;},
     1179        parents: function(elem){return jQuery.dir(elem,"parentNode");},
     1180        next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
     1181        prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
     1182        nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
     1183        prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
     1184        siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
     1185        children: function(elem){return jQuery.sibling(elem.firstChild);},
     1186        contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
     1187}, function(name, fn){
     1188        jQuery.fn[ name ] = function( selector ) {
     1189                var ret = jQuery.map( this, fn );
     1190
     1191                if ( selector && typeof selector == "string" )
     1192                        ret = jQuery.multiFilter( selector, ret );
     1193
     1194                return this.pushStack( jQuery.unique( ret ), name, selector );
     1195        };
     1196});
     1197
     1198jQuery.each({
     1199        appendTo: "append",
     1200        prependTo: "prepend",
     1201        insertBefore: "before",
     1202        insertAfter: "after",
     1203        replaceAll: "replaceWith"
     1204}, function(name, original){
     1205        jQuery.fn[ name ] = function( selector ) {
     1206                var ret = [], insert = jQuery( selector );
     1207
     1208                for ( var i = 0, l = insert.length; i < l; i++ ) {
     1209                        var elems = (i > 0 ? this.clone(true) : this).get();
     1210                        jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
     1211                        ret = ret.concat( elems );
     1212                }
     1213
     1214                return this.pushStack( ret, name, selector );
     1215        };
     1216});
     1217
     1218jQuery.each({
     1219        removeAttr: function( name ) {
     1220                jQuery.attr( this, name, "" );
     1221                if (this.nodeType == 1)
     1222                        this.removeAttribute( name );
     1223        },
     1224
     1225        addClass: function( classNames ) {
     1226                jQuery.className.add( this, classNames );
     1227        },
     1228
     1229        removeClass: function( classNames ) {
     1230                jQuery.className.remove( this, classNames );
     1231        },
     1232
     1233        toggleClass: function( classNames, state ) {
     1234                if( typeof state !== "boolean" )
     1235                        state = !jQuery.className.has( this, classNames );
     1236                jQuery.className[ state ? "add" : "remove" ]( this, classNames );
     1237        },
     1238
     1239        remove: function( selector ) {
     1240                if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
     1241                        // Prevent memory leaks
     1242                        jQuery( "*", this ).add([this]).each(function(){
     1243                                jQuery.event.remove(this);
     1244                                jQuery.removeData(this);
     1245                        });
     1246                        if (this.parentNode)
     1247                                this.parentNode.removeChild( this );
     1248                }
     1249        },
     1250
     1251        empty: function() {
     1252                // Remove element nodes and prevent memory leaks
     1253                jQuery(this).children().remove();
     1254
     1255                // Remove any remaining nodes
     1256                while ( this.firstChild )
     1257                        this.removeChild( this.firstChild );
     1258        }
     1259}, function(name, fn){
     1260        jQuery.fn[ name ] = function(){
     1261                return this.each( fn, arguments );
     1262        };
     1263});
     1264
     1265// Helper function used by the dimensions and offset modules
     1266function num(elem, prop) {
     1267        return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
     1268}
     1269var expando = "jQuery" + now(), uuid = 0, windowData = {};
     1270
     1271jQuery.extend({
     1272        cache: {},
     1273
     1274        data: function( elem, name, data ) {
     1275                elem = elem == window ?
     1276                        windowData :
     1277                        elem;
     1278
     1279                var id = elem[ expando ];
     1280
     1281                // Compute a unique ID for the element
     1282                if ( !id )
     1283                        id = elem[ expando ] = ++uuid;
     1284
     1285                // Only generate the data cache if we're
     1286                // trying to access or manipulate it
     1287                if ( name && !jQuery.cache[ id ] )
     1288                        jQuery.cache[ id ] = {};
     1289
     1290                // Prevent overriding the named cache with undefined values
     1291                if ( data !== undefined )
     1292                        jQuery.cache[ id ][ name ] = data;
     1293
     1294                // Return the named cache data, or the ID for the element
     1295                return name ?
     1296                        jQuery.cache[ id ][ name ] :
     1297                        id;
     1298        },
     1299
     1300        removeData: function( elem, name ) {
     1301                elem = elem == window ?
     1302                        windowData :
     1303                        elem;
     1304
     1305                var id = elem[ expando ];
     1306
     1307                // If we want to remove a specific section of the element's data
     1308                if ( name ) {
     1309                        if ( jQuery.cache[ id ] ) {
     1310                                // Remove the section of cache data
     1311                                delete jQuery.cache[ id ][ name ];
     1312
     1313                                // If we've removed all the data, remove the element's cache
     1314                                name = "";
     1315
     1316                                for ( name in jQuery.cache[ id ] )
     1317                                        break;
     1318
     1319                                if ( !name )
     1320                                        jQuery.removeData( elem );
     1321                        }
     1322
     1323                // Otherwise, we want to remove all of the element's data
     1324                } else {
     1325                        // Clean up the element expando
     1326                        try {
     1327                                delete elem[ expando ];
     1328                        } catch(e){
     1329                                // IE has trouble directly removing the expando
     1330                                // but it's ok with using removeAttribute
     1331                                if ( elem.removeAttribute )
     1332                                        elem.removeAttribute( expando );
     1333                        }
     1334
     1335                        // Completely remove the data cache
     1336                        delete jQuery.cache[ id ];
     1337                }
     1338        },
     1339        queue: function( elem, type, data ) {
     1340                if ( elem ){
     1341       
     1342                        type = (type || "fx") + "queue";
     1343       
     1344                        var q = jQuery.data( elem, type );
     1345       
     1346                        if ( !q || jQuery.isArray(data) )
     1347                                q = jQuery.data( elem, type, jQuery.makeArray(data) );
     1348                        else if( data )
     1349                                q.push( data );
     1350       
     1351                }
     1352                return q;
     1353        },
     1354
     1355        dequeue: function( elem, type ){
     1356                var queue = jQuery.queue( elem, type ),
     1357                        fn = queue.shift();
     1358               
     1359                if( !type || type === "fx" )
     1360                        fn = queue[0];
     1361                       
     1362                if( fn !== undefined )
     1363                        fn.call(elem);
     1364        }
     1365});
     1366
     1367jQuery.fn.extend({
     1368        data: function( key, value ){
     1369                var parts = key.split(".");
     1370                parts[1] = parts[1] ? "." + parts[1] : "";
     1371
     1372                if ( value === undefined ) {
     1373                        var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
     1374
     1375                        if ( data === undefined && this.length )
     1376                                data = jQuery.data( this[0], key );
     1377
     1378                        return data === undefined && parts[1] ?
     1379                                this.data( parts[0] ) :
     1380                                data;
     1381                } else
     1382                        return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
     1383                                jQuery.data( this, key, value );
     1384                        });
     1385        },
     1386
     1387        removeData: function( key ){
     1388                return this.each(function(){
     1389                        jQuery.removeData( this, key );
     1390                });
     1391        },
     1392        queue: function(type, data){
     1393                if ( typeof type !== "string" ) {
     1394                        data = type;
     1395                        type = "fx";
     1396                }
     1397
     1398                if ( data === undefined )
     1399                        return jQuery.queue( this[0], type );
     1400
     1401                return this.each(function(){
     1402                        var queue = jQuery.queue( this, type, data );
     1403                       
     1404                         if( type == "fx" && queue.length == 1 )
     1405                                queue[0].call(this);
     1406                });
     1407        },
     1408        dequeue: function(type){
     1409                return this.each(function(){
     1410                        jQuery.dequeue( this, type );
     1411                });
     1412        }
     1413});/*!
     1414 * Sizzle CSS Selector Engine - v0.9.3
     1415 *  Copyright 2009, The Dojo Foundation
     1416 *  Released under the MIT, BSD, and GPL Licenses.
     1417 *  More information: http://sizzlejs.com/
     1418 */
     1419(function(){
     1420
     1421var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
     1422        done = 0,
     1423        toString = Object.prototype.toString;
     1424
     1425var Sizzle = function(selector, context, results, seed) {
     1426        results = results || [];
     1427        context = context || document;
     1428
     1429        if ( context.nodeType !== 1 && context.nodeType !== 9 )
     1430                return [];
     1431       
     1432        if ( !selector || typeof selector !== "string" ) {
     1433                return results;
     1434        }
     1435
     1436        var parts = [], m, set, checkSet, check, mode, extra, prune = true;
     1437       
     1438        // Reset the position of the chunker regexp (start from head)
     1439        chunker.lastIndex = 0;
     1440       
     1441        while ( (m = chunker.exec(selector)) !== null ) {
     1442                parts.push( m[1] );
     1443               
     1444                if ( m[2] ) {
     1445                        extra = RegExp.rightContext;
     1446                        break;
     1447                }
     1448        }
     1449
     1450        if ( parts.length > 1 && origPOS.exec( selector ) ) {
     1451                if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
     1452                        set = posProcess( parts[0] + parts[1], context );
     1453                } else {
     1454                        set = Expr.relative[ parts[0] ] ?
     1455                                [ context ] :
     1456                                Sizzle( parts.shift(), context );
     1457
     1458                        while ( parts.length ) {
     1459                                selector = parts.shift();
     1460
     1461                                if ( Expr.relative[ selector ] )
     1462                                        selector += parts.shift();
     1463
     1464                                set = posProcess( selector, set );
     1465                        }
     1466                }
     1467        } else {
     1468                var ret = seed ?
     1469                        { expr: parts.pop(), set: makeArray(seed) } :
     1470                        Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
     1471                set = Sizzle.filter( ret.expr, ret.set );
     1472
     1473                if ( parts.length > 0 ) {
     1474                        checkSet = makeArray(set);
     1475                } else {
     1476                        prune = false;
     1477                }
     1478
     1479                while ( parts.length ) {
     1480                        var cur = parts.pop(), pop = cur;
     1481
     1482                        if ( !Expr.relative[ cur ] ) {
     1483                                cur = "";
     1484                        } else {
     1485                                pop = parts.pop();
     1486                        }
     1487
     1488                        if ( pop == null ) {
     1489                                pop = context;
     1490                        }
     1491
     1492                        Expr.relative[ cur ]( checkSet, pop, isXML(context) );
     1493                }
     1494        }
     1495
     1496        if ( !checkSet ) {
     1497                checkSet = set;
     1498        }
     1499
     1500        if ( !checkSet ) {
     1501                throw "Syntax error, unrecognized expression: " + (cur || selector);
     1502        }
     1503
     1504        if ( toString.call(checkSet) === "[object Array]" ) {
     1505                if ( !prune ) {
     1506                        results.push.apply( results, checkSet );
     1507                } else if ( context.nodeType === 1 ) {
     1508                        for ( var i = 0; checkSet[i] != null; i++ ) {
     1509                                if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
     1510                                        results.push( set[i] );
     1511                                }
     1512                        }
     1513                } else {
     1514                        for ( var i = 0; checkSet[i] != null; i++ ) {
     1515                                if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
     1516                                        results.push( set[i] );
     1517                                }
     1518                        }
     1519                }
     1520        } else {
     1521                makeArray( checkSet, results );
     1522        }
     1523
     1524        if ( extra ) {
     1525                Sizzle( extra, context, results, seed );
     1526
     1527                if ( sortOrder ) {
     1528                        hasDuplicate = false;
     1529                        results.sort(sortOrder);
     1530
     1531                        if ( hasDuplicate ) {
     1532                                for ( var i = 1; i < results.length; i++ ) {
     1533                                        if ( results[i] === results[i-1] ) {
     1534                                                results.splice(i--, 1);
     1535                                        }
     1536                                }
     1537                        }
     1538                }
     1539        }
     1540
     1541        return results;
     1542};
     1543
     1544Sizzle.matches = function(expr, set){
     1545        return Sizzle(expr, null, null, set);
     1546};
     1547
     1548Sizzle.find = function(expr, context, isXML){
     1549        var set, match;
     1550
     1551        if ( !expr ) {
     1552                return [];
     1553        }
     1554
     1555        for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
     1556                var type = Expr.order[i], match;
     1557               
     1558                if ( (match = Expr.match[ type ].exec( expr )) ) {
     1559                        var left = RegExp.leftContext;
     1560
     1561                        if ( left.substr( left.length - 1 ) !== "\\" ) {
     1562                                match[1] = (match[1] || "").replace(/\\/g, "");
     1563                                set = Expr.find[ type ]( match, context, isXML );
     1564                                if ( set != null ) {
     1565                                        expr = expr.replace( Expr.match[ type ], "" );
     1566                                        break;
     1567                                }
     1568                        }
     1569                }
     1570        }
     1571
     1572        if ( !set ) {
     1573                set = context.getElementsByTagName("*");
     1574        }
     1575
     1576        return {set: set, expr: expr};
     1577};
     1578
     1579Sizzle.filter = function(expr, set, inplace, not){
     1580        var old = expr, result = [], curLoop = set, match, anyFound,
     1581                isXMLFilter = set && set[0] && isXML(set[0]);
     1582
     1583        while ( expr && set.length ) {
     1584                for ( var type in Expr.filter ) {
     1585                        if ( (match = Expr.match[ type ].exec( expr )) != null ) {
     1586                                var filter = Expr.filter[ type ], found, item;
     1587                                anyFound = false;
     1588
     1589                                if ( curLoop == result ) {
     1590                                        result = [];
     1591                                }
     1592
     1593                                if ( Expr.preFilter[ type ] ) {
     1594                                        match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
     1595
     1596                                        if ( !match ) {
     1597                                                anyFound = found = true;
     1598                                        } else if ( match === true ) {
     1599                                                continue;
     1600                                        }
     1601                                }
     1602
     1603                                if ( match ) {
     1604                                        for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
     1605                                                if ( item ) {
     1606                                                        found = filter( item, match, i, curLoop );
     1607                                                        var pass = not ^ !!found;
     1608
     1609                                                        if ( inplace && found != null ) {
     1610                                                                if ( pass ) {
     1611                                                                        anyFound = true;
     1612                                                                } else {
     1613                                                                        curLoop[i] = false;
     1614                                                                }
     1615                                                        } else if ( pass ) {
     1616                                                                result.push( item );
     1617                                                                anyFound = true;
     1618                                                        }
     1619                                                }
     1620                                        }
     1621                                }
     1622
     1623                                if ( found !== undefined ) {
     1624                                        if ( !inplace ) {
     1625                                                curLoop = result;
     1626                                        }
     1627
     1628                                        expr = expr.replace( Expr.match[ type ], "" );
     1629
     1630                                        if ( !anyFound ) {
     1631                                                return [];
     1632                                        }
     1633
     1634                                        break;
     1635                                }
     1636                        }
     1637                }
     1638
     1639                // Improper expression
     1640                if ( expr == old ) {
     1641                        if ( anyFound == null ) {
     1642                                throw "Syntax error, unrecognized expression: " + expr;
     1643                        } else {
     1644                                break;
     1645                        }
     1646                }
     1647
     1648                old = expr;
     1649        }
     1650
     1651        return curLoop;
     1652};
     1653
     1654var Expr = Sizzle.selectors = {
     1655        order: [ "ID", "NAME", "TAG" ],
     1656        match: {
     1657                ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
     1658                CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
     1659                NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
     1660                ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
     1661                TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
     1662                CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
     1663                POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
     1664                PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
     1665        },
     1666        attrMap: {
     1667                "class": "className",
     1668                "for": "htmlFor"
     1669        },
     1670        attrHandle: {
     1671                href: function(elem){
     1672                        return elem.getAttribute("href");
     1673                }
     1674        },
     1675        relative: {
     1676                "+": function(checkSet, part, isXML){
     1677                        var isPartStr = typeof part === "string",
     1678                                isTag = isPartStr && !/\W/.test(part),
     1679                                isPartStrNotTag = isPartStr && !isTag;
     1680
     1681                        if ( isTag && !isXML ) {
     1682                                part = part.toUpperCase();
     1683                        }
     1684
     1685                        for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
     1686                                if ( (elem = checkSet[i]) ) {
     1687                                        while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
     1688
     1689                                        checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
     1690                                                elem || false :
     1691                                                elem === part;
     1692                                }
     1693                        }
     1694
     1695                        if ( isPartStrNotTag ) {
     1696                                Sizzle.filter( part, checkSet, true );
     1697                        }
     1698                },
     1699                ">": function(checkSet, part, isXML){
     1700                        var isPartStr = typeof part === "string";
     1701
     1702                        if ( isPartStr && !/\W/.test(part) ) {
     1703                                part = isXML ? part : part.toUpperCase();
     1704
     1705                                for ( var i = 0, l = checkSet.length; i < l; i++ ) {
     1706                                        var elem = checkSet[i];
     1707                                        if ( elem ) {
     1708                                                var parent = elem.parentNode;
     1709                                                checkSet[i] = parent.nodeName === part ? parent : false;
     1710                                        }
     1711                                }
     1712                        } else {
     1713                                for ( var i = 0, l = checkSet.length; i < l; i++ ) {
     1714                                        var elem = checkSet[i];
     1715                                        if ( elem ) {
     1716                                                checkSet[i] = isPartStr ?
     1717                                                        elem.parentNode :
     1718                                                        elem.parentNode === part;
     1719                                        }
     1720                                }
     1721
     1722                                if ( isPartStr ) {
     1723                                        Sizzle.filter( part, checkSet, true );
     1724                                }
     1725                        }
     1726                },
     1727                "": function(checkSet, part, isXML){
     1728                        var doneName = done++, checkFn = dirCheck;
     1729
     1730                        if ( !part.match(/\W/) ) {
     1731                                var nodeCheck = part = isXML ? part : part.toUpperCase();
     1732                                checkFn = dirNodeCheck;
     1733                        }
     1734
     1735                        checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
     1736                },
     1737                "~": function(checkSet, part, isXML){
     1738                        var doneName = done++, checkFn = dirCheck;
     1739
     1740                        if ( typeof part === "string" && !part.match(/\W/) ) {
     1741                                var nodeCheck = part = isXML ? part : part.toUpperCase();
     1742                                checkFn = dirNodeCheck;
     1743                        }
     1744
     1745                        checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
     1746                }
     1747        },
     1748        find: {
     1749                ID: function(match, context, isXML){
     1750                        if ( typeof context.getElementById !== "undefined" && !isXML ) {
     1751                                var m = context.getElementById(match[1]);
     1752                                return m ? [m] : [];
     1753                        }
     1754                },
     1755                NAME: function(match, context, isXML){
     1756                        if ( typeof context.getElementsByName !== "undefined" ) {
     1757                                var ret = [], results = context.getElementsByName(match[1]);
     1758
     1759                                for ( var i = 0, l = results.length; i < l; i++ ) {
     1760                                        if ( results[i].getAttribute("name") === match[1] ) {
     1761                                                ret.push( results[i] );
     1762                                        }
     1763                                }
     1764
     1765                                return ret.length === 0 ? null : ret;
     1766                        }
     1767                },
     1768                TAG: function(match, context){
     1769                        return context.getElementsByTagName(match[1]);
     1770                }
     1771        },
     1772        preFilter: {
     1773                CLASS: function(match, curLoop, inplace, result, not, isXML){
     1774                        match = " " + match[1].replace(/\\/g, "") + " ";
     1775
     1776                        if ( isXML ) {
     1777                                return match;
     1778                        }
     1779
     1780                        for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
     1781                                if ( elem ) {
     1782                                        if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
     1783                                                if ( !inplace )
     1784                                                        result.push( elem );
     1785                                        } else if ( inplace ) {
     1786                                                curLoop[i] = false;
     1787                                        }
     1788                                }
     1789                        }
     1790
     1791                        return false;
     1792                },
     1793                ID: function(match){
     1794                        return match[1].replace(/\\/g, "");
     1795                },
     1796                TAG: function(match, curLoop){
     1797                        for ( var i = 0; curLoop[i] === false; i++ ){}
     1798                        return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
     1799                },
     1800                CHILD: function(match){
     1801                        if ( match[1] == "nth" ) {
     1802                                // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
     1803                                var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
     1804                                        match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
     1805                                        !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
     1806
     1807                                // calculate the numbers (first)n+(last) including if they are negative
     1808                                match[2] = (test[1] + (test[2] || 1)) - 0;
     1809                                match[3] = test[3] - 0;
     1810                        }
     1811
     1812                        // TODO: Move to normal caching system
     1813                        match[0] = done++;
     1814
     1815                        return match;
     1816                },
     1817                ATTR: function(match, curLoop, inplace, result, not, isXML){
     1818                        var name = match[1].replace(/\\/g, "");
     1819                       
     1820                        if ( !isXML && Expr.attrMap[name] ) {
     1821                                match[1] = Expr.attrMap[name];
     1822                        }
     1823
     1824                        if ( match[2] === "~=" ) {
     1825                                match[4] = " " + match[4] + " ";
     1826                        }
     1827
     1828                        return match;
     1829                },
     1830                PSEUDO: function(match, curLoop, inplace, result, not){
     1831                        if ( match[1] === "not" ) {
     1832                                // If we're dealing with a complex expression, or a simple one
     1833                                if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
     1834                                        match[3] = Sizzle(match[3], null, null, curLoop);
     1835                                } else {
     1836                                        var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
     1837                                        if ( !inplace ) {
     1838                                                result.push.apply( result, ret );
     1839                                        }
     1840                                        return false;
     1841                                }
     1842                        } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
     1843                                return true;
     1844                        }
     1845                       
     1846                        return match;
     1847                },
     1848                POS: function(match){
     1849                        match.unshift( true );
     1850                        return match;
     1851                }
     1852        },
     1853        filters: {
     1854                enabled: function(elem){
     1855                        return elem.disabled === false && elem.type !== "hidden";
     1856                },
     1857                disabled: function(elem){
     1858                        return elem.disabled === true;
     1859                },
     1860                checked: function(elem){
     1861                        return elem.checked === true;
     1862                },
     1863                selected: function(elem){
     1864                        // Accessing this property makes selected-by-default
     1865                        // options in Safari work properly
     1866                        elem.parentNode.selectedIndex;
     1867                        return elem.selected === true;
     1868                },
     1869                parent: function(elem){
     1870                        return !!elem.firstChild;
     1871                },
     1872                empty: function(elem){
     1873                        return !elem.firstChild;
     1874                },
     1875                has: function(elem, i, match){
     1876                        return !!Sizzle( match[3], elem ).length;
     1877                },
     1878                header: function(elem){
     1879                        return /h\d/i.test( elem.nodeName );
     1880                },
     1881                text: function(elem){
     1882                        return "text" === elem.type;
     1883                },
     1884                radio: function(elem){
     1885                        return "radio" === elem.type;
     1886                },
     1887                checkbox: function(elem){
     1888                        return "checkbox" === elem.type;
     1889                },
     1890                file: function(elem){
     1891                        return "file" === elem.type;
     1892                },
     1893                password: function(elem){
     1894                        return "password" === elem.type;
     1895                },
     1896                submit: function(elem){
     1897                        return "submit" === elem.type;
     1898                },
     1899                image: function(elem){
     1900                        return "image" === elem.type;
     1901                },
     1902                reset: function(elem){
     1903                        return "reset" === elem.type;
     1904                },
     1905                button: function(elem){
     1906                        return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
     1907                },
     1908                input: function(elem){
     1909                        return /input|select|textarea|button/i.test(elem.nodeName);
     1910                }
     1911        },
     1912        setFilters: {
     1913                first: function(elem, i){
     1914                        return i === 0;
     1915                },
     1916                last: function(elem, i, match, array){
     1917                        return i === array.length - 1;
     1918                },
     1919                even: function(elem, i){
     1920                        return i % 2 === 0;
     1921                },
     1922                odd: function(elem, i){
     1923                        return i % 2 === 1;
     1924                },
     1925                lt: function(elem, i, match){
     1926                        return i < match[3] - 0;
     1927                },
     1928                gt: function(elem, i, match){
     1929                        return i > match[3] - 0;
     1930                },
     1931                nth: function(elem, i, match){
     1932                        return match[3] - 0 == i;
     1933                },
     1934                eq: function(elem, i, match){
     1935                        return match[3] - 0 == i;
     1936                }
     1937        },
     1938        filter: {
     1939                PSEUDO: function(elem, match, i, array){
     1940                        var name = match[1], filter = Expr.filters[ name ];
     1941
     1942                        if ( filter ) {
     1943                                return filter( elem, i, match, array );
     1944                        } else if ( name === "contains" ) {
     1945                                return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
     1946                        } else if ( name === "not" ) {
     1947                                var not = match[3];
     1948
     1949                                for ( var i = 0, l = not.length; i < l; i++ ) {
     1950                                        if ( not[i] === elem ) {
     1951                                                return false;
     1952                                        }
     1953                                }
     1954
     1955                                return true;
     1956                        }
     1957                },
     1958                CHILD: function(elem, match){
     1959                        var type = match[1], node = elem;
     1960                        switch (type) {
     1961                                case 'only':
     1962                                case 'first':
     1963                                        while (node = node.previousSibling)  {
     1964                                                if ( node.nodeType === 1 ) return false;
     1965                                        }
     1966                                        if ( type == 'first') return true;
     1967                                        node = elem;
     1968                                case 'last':
     1969                                        while (node = node.nextSibling)  {
     1970                                                if ( node.nodeType === 1 ) return false;
     1971                                        }
     1972                                        return true;
     1973                                case 'nth':
     1974                                        var first = match[2], last = match[3];
     1975
     1976                                        if ( first == 1 && last == 0 ) {
     1977                                                return true;
     1978                                        }
     1979                                       
     1980                                        var doneName = match[0],
     1981                                                parent = elem.parentNode;
     1982       
     1983                                        if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
     1984                                                var count = 0;
     1985                                                for ( node = parent.firstChild; node; node = node.nextSibling ) {
     1986                                                        if ( node.nodeType === 1 ) {
     1987                                                                node.nodeIndex = ++count;
     1988                                                        }
     1989                                                }
     1990                                                parent.sizcache = doneName;
     1991                                        }
     1992                                       
     1993                                        var diff = elem.nodeIndex - last;
     1994                                        if ( first == 0 ) {
     1995                                                return diff == 0;
     1996                                        } else {
     1997                                                return ( diff % first == 0 && diff / first >= 0 );
     1998                                        }
     1999                        }
     2000                },
     2001                ID: function(elem, match){
     2002                        return elem.nodeType === 1 && elem.getAttribute("id") === match;
     2003                },
     2004                TAG: function(elem, match){
     2005                        return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
     2006                },
     2007                CLASS: function(elem, match){
     2008                        return (" " + (elem.className || elem.getAttribute("class")) + " ")
     2009                                .indexOf( match ) > -1;
     2010                },
     2011                ATTR: function(elem, match){
     2012                        var name = match[1],
     2013                                result = Expr.attrHandle[ name ] ?
     2014                                        Expr.attrHandle[ name ]( elem ) :
     2015                                        elem[ name ] != null ?
     2016                                                elem[ name ] :
     2017                                                elem.getAttribute( name ),
     2018                                value = result + "",
     2019                                type = match[2],
     2020                                check = match[4];
     2021
     2022                        return result == null ?
     2023                                type === "!=" :
     2024                                type === "=" ?
     2025                                value === check :
     2026                                type === "*=" ?
     2027                                value.indexOf(check) >= 0 :
     2028                                type === "~=" ?
     2029                                (" " + value + " ").indexOf(check) >= 0 :
     2030                                !check ?
     2031                                value && result !== false :
     2032                                type === "!=" ?
     2033                                value != check :
     2034                                type === "^=" ?
     2035                                value.indexOf(check) === 0 :
     2036                                type === "$=" ?
     2037                                value.substr(value.length - check.length) === check :
     2038                                type === "|=" ?
     2039                                value === check || value.substr(0, check.length + 1) === check + "-" :
     2040                                false;
     2041                },
     2042                POS: function(elem, match, i, array){
     2043                        var name = match[2], filter = Expr.setFilters[ name ];
     2044
     2045                        if ( filter ) {
     2046                                return filter( elem, i, match, array );
     2047                        }
     2048                }
     2049        }
     2050};
     2051
     2052var origPOS = Expr.match.POS;
     2053
     2054for ( var type in Expr.match ) {
     2055        Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
     2056}
     2057
     2058var makeArray = function(array, results) {
     2059        array = Array.prototype.slice.call( array );
     2060
     2061        if ( results ) {
     2062                results.push.apply( results, array );
     2063                return results;
     2064        }
     2065       
     2066        return array;
     2067};
     2068
     2069// Perform a simple check to determine if the browser is capable of
     2070// converting a NodeList to an array using builtin methods.
     2071try {
     2072        Array.prototype.slice.call( document.documentElement.childNodes );
     2073
     2074// Provide a fallback method if it does not work
     2075} catch(e){
     2076        makeArray = function(array, results) {
     2077                var ret = results || [];
     2078
     2079                if ( toString.call(array) === "[object Array]" ) {
     2080                        Array.prototype.push.apply( ret, array );
     2081                } else {
     2082                        if ( typeof array.length === "number" ) {
     2083                                for ( var i = 0, l = array.length; i < l; i++ ) {
     2084                                        ret.push( array[i] );
     2085                                }
     2086                        } else {
     2087                                for ( var i = 0; array[i]; i++ ) {
     2088                                        ret.push( array[i] );
     2089                                }
     2090                        }
     2091                }
     2092
     2093                return ret;
     2094        };
     2095}
     2096
     2097var sortOrder;
     2098
     2099if ( document.documentElement.compareDocumentPosition ) {
     2100        sortOrder = function( a, b ) {
     2101                var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
     2102                if ( ret === 0 ) {
     2103                        hasDuplicate = true;
     2104                }
     2105                return ret;
     2106        };
     2107} else if ( "sourceIndex" in document.documentElement ) {
     2108        sortOrder = function( a, b ) {
     2109                var ret = a.sourceIndex - b.sourceIndex;
     2110                if ( ret === 0 ) {
     2111                        hasDuplicate = true;
     2112                }
     2113                return ret;
     2114        };
     2115} else if ( document.createRange ) {
     2116        sortOrder = function( a, b ) {
     2117                var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
     2118                aRange.selectNode(a);
     2119                aRange.collapse(true);
     2120                bRange.selectNode(b);
     2121                bRange.collapse(true);
     2122                var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
     2123                if ( ret === 0 ) {
     2124                        hasDuplicate = true;
     2125                }
     2126                return ret;
     2127        };
     2128}
     2129
     2130// Check to see if the browser returns elements by name when
     2131// querying by getElementById (and provide a workaround)
     2132(function(){
     2133        // We're going to inject a fake input element with a specified name
     2134        var form = document.createElement("form"),
     2135                id = "script" + (new Date).getTime();
     2136        form.innerHTML = "<input name='" + id + "'/>";
     2137
     2138        // Inject it into the root element, check its status, and remove it quickly
     2139        var root = document.documentElement;
     2140        root.insertBefore( form, root.firstChild );
     2141
     2142        // The workaround has to do additional checks after a getElementById
     2143        // Which slows things down for other browsers (hence the branching)
     2144        if ( !!document.getElementById( id ) ) {
     2145                Expr.find.ID = function(match, context, isXML){
     2146                        if ( typeof context.getElementById !== "undefined" && !isXML ) {
     2147                                var m = context.getElementById(match[1]);
     2148                                return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
     2149                        }
     2150                };
     2151
     2152                Expr.filter.ID = function(elem, match){
     2153                        var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
     2154                        return elem.nodeType === 1 && node && node.nodeValue === match;
     2155                };
     2156        }
     2157
     2158        root.removeChild( form );
     2159})();
     2160
     2161(function(){
     2162        // Check to see if the browser returns only elements
     2163        // when doing getElementsByTagName("*")
     2164
     2165        // Create a fake element
     2166        var div = document.createElement("div");
     2167        div.appendChild( document.createComment("") );
     2168
     2169        // Make sure no comments are found
     2170        if ( div.getElementsByTagName("*").length > 0 ) {
     2171                Expr.find.TAG = function(match, context){
     2172                        var results = context.getElementsByTagName(match[1]);
     2173
     2174                        // Filter out possible comments
     2175                        if ( match[1] === "*" ) {
     2176                                var tmp = [];
     2177
     2178                                for ( var i = 0; results[i]; i++ ) {
     2179                                        if ( results[i].nodeType === 1 ) {
     2180                                                tmp.push( results[i] );
     2181                                        }
     2182                                }
     2183
     2184                                results = tmp;
     2185                        }
     2186
     2187                        return results;
     2188                };
     2189        }
     2190
     2191        // Check to see if an attribute returns normalized href attributes
     2192        div.innerHTML = "<a href='#'></a>";
     2193        if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
     2194                        div.firstChild.getAttribute("href") !== "#" ) {
     2195                Expr.attrHandle.href = function(elem){
     2196                        return elem.getAttribute("href", 2);
     2197                };
     2198        }
     2199})();
     2200
     2201if ( document.querySelectorAll ) (function(){
     2202        var oldSizzle = Sizzle, div = document.createElement("div");
     2203        div.innerHTML = "<p class='TEST'></p>";
     2204
     2205        // Safari can't handle uppercase or unicode characters when
     2206        // in quirks mode.
     2207        if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
     2208                return;
     2209        }
     2210       
     2211        Sizzle = function(query, context, extra, seed){
     2212                context = context || document;
     2213
     2214                // Only use querySelectorAll on non-XML documents
     2215                // (ID selectors don't work in non-HTML documents)
     2216                if ( !seed && context.nodeType === 9 && !isXML(context) ) {
     2217                        try {
     2218                                return makeArray( context.querySelectorAll(query), extra );
     2219                        } catch(e){}
     2220                }
     2221               
     2222                return oldSizzle(query, context, extra, seed);
     2223        };
     2224
     2225        Sizzle.find = oldSizzle.find;
     2226        Sizzle.filter = oldSizzle.filter;
     2227        Sizzle.selectors = oldSizzle.selectors;
     2228        Sizzle.matches = oldSizzle.matches;
     2229})();
     2230
     2231if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
     2232        var div = document.createElement("div");
     2233        div.innerHTML = "<div class='test e'></div><div class='test'></div>";
     2234
     2235        // Opera can't find a second classname (in 9.6)
     2236        if ( div.getElementsByClassName("e").length === 0 )
     2237                return;
     2238
     2239        // Safari caches class attributes, doesn't catch changes (in 3.2)
     2240        div.lastChild.className = "e";
     2241
     2242        if ( div.getElementsByClassName("e").length === 1 )
     2243                return;
     2244
     2245        Expr.order.splice(1, 0, "CLASS");
     2246        Expr.find.CLASS = function(match, context, isXML) {
     2247                if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
     2248                        return context.getElementsByClassName(match[1]);
     2249                }
     2250        };
     2251})();
     2252
     2253function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
     2254        var sibDir = dir == "previousSibling" && !isXML;
     2255        for ( var i = 0, l = checkSet.length; i < l; i++ ) {
     2256                var elem = checkSet[i];
     2257                if ( elem ) {
     2258                        if ( sibDir && elem.nodeType === 1 ){
     2259                                elem.sizcache = doneName;
     2260                                elem.sizset = i;
     2261                        }
     2262                        elem = elem[dir];
     2263                        var match = false;
     2264
     2265                        while ( elem ) {
     2266                                if ( elem.sizcache === doneName ) {
     2267                                        match = checkSet[elem.sizset];
     2268                                        break;
     2269                                }
     2270
     2271                                if ( elem.nodeType === 1 && !isXML ){
     2272                                        elem.sizcache = doneName;
     2273                                        elem.sizset = i;
     2274                                }
     2275
     2276                                if ( elem.nodeName === cur ) {
     2277                                        match = elem;
     2278                                        break;
     2279                                }
     2280
     2281                                elem = elem[dir];
     2282                        }
     2283
     2284                        checkSet[i] = match;
     2285                }
     2286        }
     2287}
     2288
     2289function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
     2290        var sibDir = dir == "previousSibling" && !isXML;
     2291        for ( var i = 0, l = checkSet.length; i < l; i++ ) {
     2292                var elem = checkSet[i];
     2293                if ( elem ) {
     2294                        if ( sibDir && elem.nodeType === 1 ) {
     2295                                elem.sizcache = doneName;
     2296                                elem.sizset = i;
     2297                        }
     2298                        elem = elem[dir];
     2299                        var match = false;
     2300
     2301                        while ( elem ) {
     2302                                if ( elem.sizcache === doneName ) {
     2303                                        match = checkSet[elem.sizset];
     2304                                        break;
     2305                                }
     2306
     2307                                if ( elem.nodeType === 1 ) {
     2308                                        if ( !isXML ) {
     2309                                                elem.sizcache = doneName;
     2310                                                elem.sizset = i;
     2311                                        }
     2312                                        if ( typeof cur !== "string" ) {
     2313                                                if ( elem === cur ) {
     2314                                                        match = true;
     2315                                                        break;
     2316                                                }
     2317
     2318                                        } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
     2319                                                match = elem;
     2320                                                break;
     2321                                        }
     2322                                }
     2323
     2324                                elem = elem[dir];
     2325                        }
     2326
     2327                        checkSet[i] = match;
     2328                }
     2329        }
     2330}
     2331
     2332var contains = document.compareDocumentPosition ?  function(a, b){
     2333        return a.compareDocumentPosition(b) & 16;
     2334} : function(a, b){
     2335        return a !== b && (a.contains ? a.contains(b) : true);
     2336};
     2337
     2338var isXML = function(elem){
     2339        return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
     2340                !!elem.ownerDocument && isXML( elem.ownerDocument );
     2341};
     2342
     2343var posProcess = function(selector, context){
     2344        var tmpSet = [], later = "", match,
     2345                root = context.nodeType ? [context] : context;
     2346
     2347        // Position selectors must be done after the filter
     2348        // And so must :not(positional) so we move all PSEUDOs to the end
     2349        while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
     2350                later += match[0];
     2351                selector = selector.replace( Expr.match.PSEUDO, "" );
     2352        }
     2353
     2354        selector = Expr.relative[selector] ? selector + "*" : selector;
     2355
     2356        for ( var i = 0, l = root.length; i < l; i++ ) {
     2357                Sizzle( selector, root[i], tmpSet );
     2358        }
     2359
     2360        return Sizzle.filter( later, tmpSet );
     2361};
     2362
     2363// EXPOSE
     2364jQuery.find = Sizzle;
     2365jQuery.filter = Sizzle.filter;
     2366jQuery.expr = Sizzle.selectors;
     2367jQuery.expr[":"] = jQuery.expr.filters;
     2368
     2369Sizzle.selectors.filters.hidden = function(elem){
     2370        return elem.offsetWidth === 0 || elem.offsetHeight === 0;
     2371};
     2372
     2373Sizzle.selectors.filters.visible = function(elem){
     2374        return elem.offsetWidth > 0 || elem.offsetHeight > 0;
     2375};
     2376
     2377Sizzle.selectors.filters.animated = function(elem){
     2378        return jQuery.grep(jQuery.timers, function(fn){
     2379                return elem === fn.elem;
     2380        }).length;
     2381};
     2382
     2383jQuery.multiFilter = function( expr, elems, not ) {
     2384        if ( not ) {
     2385                expr = ":not(" + expr + ")";
     2386        }
     2387
     2388        return Sizzle.matches(expr, elems);
     2389};
     2390
     2391jQuery.dir = function( elem, dir ){
     2392        var matched = [], cur = elem[dir];
     2393        while ( cur && cur != document ) {
     2394                if ( cur.nodeType == 1 )
     2395                        matched.push( cur );
     2396                cur = cur[dir];
     2397        }
     2398        return matched;
     2399};
     2400
     2401jQuery.nth = function(cur, result, dir, elem){
     2402        result = result || 1;
     2403        var num = 0;
     2404
     2405        for ( ; cur; cur = cur[dir] )
     2406                if ( cur.nodeType == 1 && ++num == result )
     2407                        break;
     2408
     2409        return cur;
     2410};
     2411
     2412jQuery.sibling = function(n, elem){
     2413        var r = [];
     2414
     2415        for ( ; n; n = n.nextSibling ) {
     2416                if ( n.nodeType == 1 && n != elem )
     2417                        r.push( n );
     2418        }
     2419
     2420        return r;
     2421};
     2422
     2423return;
     2424
     2425window.Sizzle = Sizzle;
     2426
     2427})();
     2428/*
     2429 * A number of helper functions used for managing events.
     2430 * Many of the ideas behind this code originated from
     2431 * Dean Edwards' addEvent library.
     2432 */
     2433jQuery.event = {
     2434
     2435        // Bind an event to an element
     2436        // Original by Dean Edwards
     2437        add: function(elem, types, handler, data) {
     2438                if ( elem.nodeType == 3 || elem.nodeType == 8 )
     2439                        return;
     2440
     2441                // For whatever reason, IE has trouble passing the window object
     2442                // around, causing it to be cloned in the process
     2443                if ( elem.setInterval && elem != window )
     2444                        elem = window;
     2445
     2446                // Make sure that the function being executed has a unique ID
     2447                if ( !handler.guid )
     2448                        handler.guid = this.guid++;
     2449
     2450                // if data is passed, bind to handler
     2451                if ( data !== undefined ) {
     2452                        // Create temporary function pointer to original handler
     2453                        var fn = handler;
     2454
     2455                        // Create unique handler function, wrapped around original handler
     2456                        handler = this.proxy( fn );
     2457
     2458                        // Store data in unique handler
     2459                        handler.data = data;
     2460                }
     2461
     2462                // Init the element's event structure
     2463                var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
     2464                        handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
     2465                                // Handle the second event of a trigger and when
     2466                                // an event is called after a page has unloaded
     2467                                return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
     2468                                        jQuery.event.handle.apply(arguments.callee.elem, arguments) :
     2469                                        undefined;
     2470                        });
     2471                // Add elem as a property of the handle function
     2472                // This is to prevent a memory leak with non-native
     2473                // event in IE.
     2474                handle.elem = elem;
     2475
     2476                // Handle multiple events separated by a space
     2477                // jQuery(...).bind("mouseover mouseout", fn);
     2478                jQuery.each(types.split(/\s+/), function(index, type) {
     2479                        // Namespaced event handlers
     2480                        var namespaces = type.split(".");
     2481                        type = namespaces.shift();
     2482                        handler.type = namespaces.slice().sort().join(".");
     2483
     2484                        // Get the current list of functions bound to this event
     2485                        var handlers = events[type];
     2486                       
     2487                        if ( jQuery.event.specialAll[type] )
     2488                                jQuery.event.specialAll[type].setup.call(elem, data, namespaces);
     2489
     2490                        // Init the event handler queue
     2491                        if (!handlers) {
     2492                                handlers = events[type] = {};
     2493
     2494                                // Check for a special event handler
     2495                                // Only use addEventListener/attachEvent if the special
     2496                                // events handler returns false
     2497                                if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) {
     2498                                        // Bind the global event handler to the element
     2499                                        if (elem.addEventListener)
     2500                                                elem.addEventListener(type, handle, false);
     2501                                        else if (elem.attachEvent)
     2502                                                elem.attachEvent("on" + type, handle);
     2503                                }
     2504                        }
     2505
     2506                        // Add the function to the element's handler list
     2507                        handlers[handler.guid] = handler;
     2508
     2509                        // Keep track of which events have been used, for global triggering
     2510                        jQuery.event.global[type] = true;
     2511                });
     2512
     2513                // Nullify elem to prevent memory leaks in IE
     2514                elem = null;
     2515        },
     2516
     2517        guid: 1,
     2518        global: {},
     2519
     2520        // Detach an event or set of events from an element
     2521        remove: function(elem, types, handler) {
     2522                // don't do events on text and comment nodes
     2523                if ( elem.nodeType == 3 || elem.nodeType == 8 )
     2524                        return;
     2525
     2526                var events = jQuery.data(elem, "events"), ret, index;
     2527
     2528                if ( events ) {
     2529                        // Unbind all events for the element
     2530                        if ( types === undefined || (typeof types === "string" && types.charAt(0) == ".") )
     2531                                for ( var type in events )
     2532                                        this.remove( elem, type + (types || "") );
     2533                        else {
     2534                                // types is actually an event object here
     2535                                if ( types.type ) {
     2536                                        handler = types.handler;
     2537                                        types = types.type;
     2538                                }
     2539
     2540                                // Handle multiple events seperated by a space
     2541                                // jQuery(...).unbind("mouseover mouseout", fn);
     2542                                jQuery.each(types.split(/\s+/), function(index, type){
     2543                                        // Namespaced event handlers
     2544                                        var namespaces = type.split(".");
     2545                                        type = namespaces.shift();
     2546                                        var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
     2547
     2548                                        if ( events[type] ) {
     2549                                                // remove the given handler for the given type
     2550                                                if ( handler )
     2551                                                        delete events[type][handler.guid];
     2552
     2553                                                // remove all handlers for the given type
     2554                                                else
     2555                                                        for ( var handle in events[type] )
     2556                                                                // Handle the removal of namespaced events
     2557                                                                if ( namespace.test(events[type][handle].type) )
     2558                                                                        delete events[type][handle];
     2559                                                                       
     2560                                                if ( jQuery.event.specialAll[type] )
     2561                                                        jQuery.event.specialAll[type].teardown.call(elem, namespaces);
     2562
     2563                                                // remove generic event handler if no more handlers exist
     2564                                                for ( ret in events[type] ) break;
     2565                                                if ( !ret ) {
     2566                                                        if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem, namespaces) === false ) {
     2567                                                                if (elem.removeEventListener)
     2568                                                                        elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
     2569                                                                else if (elem.detachEvent)
     2570                                                                        elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
     2571                                                        }
     2572                                                        ret = null;
     2573                                                        delete events[type];
     2574                                                }
     2575                                        }
     2576                                });
     2577                        }
     2578
     2579                        // Remove the expando if it's no longer used
     2580                        for ( ret in events ) break;
     2581                        if ( !ret ) {
     2582                                var handle = jQuery.data( elem, "handle" );
     2583                                if ( handle ) handle.elem = null;
     2584                                jQuery.removeData( elem, "events" );
     2585                                jQuery.removeData( elem, "handle" );
     2586                        }
     2587                }
     2588        },
     2589
     2590        // bubbling is internal
     2591        trigger: function( event, data, elem, bubbling ) {
     2592                // Event object or event type
     2593                var type = event.type || event;
     2594
     2595                if( !bubbling ){
     2596                        event = typeof event === "object" ?
     2597                                // jQuery.Event object
     2598                                event[expando] ? event :
     2599                                // Object literal
     2600                                jQuery.extend( jQuery.Event(type), event ) :
     2601                                // Just the event type (string)
     2602                                jQuery.Event(type);
     2603
     2604                        if ( type.indexOf("!") >= 0 ) {
     2605                                event.type = type = type.slice(0, -1);
     2606                                event.exclusive = true;
     2607                        }
     2608
     2609                        // Handle a global trigger
     2610                        if ( !elem ) {
     2611                                // Don't bubble custom events when global (to avoid too much overhead)
     2612                                event.stopPropagation();
     2613                                // Only trigger if we've ever bound an event for it
     2614                                if ( this.global[type] )
     2615                                        jQuery.each( jQuery.cache, function(){
     2616                                                if ( this.events && this.events[type] )
     2617                                                        jQuery.event.trigger( event, data, this.handle.elem );
     2618                                        });
     2619                        }
     2620
     2621                        // Handle triggering a single element
     2622
     2623                        // don't do events on text and comment nodes
     2624                        if ( !elem || elem.nodeType == 3 || elem.nodeType == 8 )
     2625                                return undefined;
     2626                       
     2627                        // Clean up in case it is reused
     2628                        event.result = undefined;
     2629                        event.target = elem;
     2630                       
     2631                        // Clone the incoming data, if any
     2632                        data = jQuery.makeArray(data);
     2633                        data.unshift( event );
     2634                }
     2635
     2636                event.currentTarget = elem;
     2637
     2638                // Trigger the event, it is assumed that "handle" is a function
     2639                var handle = jQuery.data(elem, "handle");
     2640                if ( handle )
     2641                        handle.apply( elem, data );
     2642
     2643                // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
     2644                if ( (!elem[type] || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
     2645                        event.result = false;
     2646
     2647                // Trigger the native events (except for clicks on links)
     2648                if ( !bubbling && elem[type] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
     2649                        this.triggered = true;
     2650                        try {
     2651                                elem[ type ]();
     2652                        // prevent IE from throwing an error for some hidden elements
     2653                        } catch (e) {}
     2654                }
     2655
     2656                this.triggered = false;
     2657
     2658                if ( !event.isPropagationStopped() ) {
     2659                        var parent = elem.parentNode || elem.ownerDocument;
     2660                        if ( parent )
     2661                                jQuery.event.trigger(event, data, parent, true);
     2662                }
     2663        },
     2664
     2665        handle: function(event) {
     2666                // returned undefined or false
     2667                var all, handlers;
     2668
     2669                event = arguments[0] = jQuery.event.fix( event || window.event );
     2670                event.currentTarget = this;
     2671               
     2672                // Namespaced event handlers
     2673                var namespaces = event.type.split(".");
     2674                event.type = namespaces.shift();
     2675
     2676                // Cache this now, all = true means, any handler
     2677                all = !namespaces.length && !event.exclusive;
     2678               
     2679                var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
     2680
     2681                handlers = ( jQuery.data(this, "events") || {} )[event.type];
     2682
     2683                for ( var j in handlers ) {
     2684                        var handler = handlers[j];
     2685
     2686                        // Filter the functions by class
     2687                        if ( all || namespace.test(handler.type) ) {
     2688                                // Pass in a reference to the handler function itself
     2689                                // So that we can later remove it
     2690                                event.handler = handler;
     2691                                event.data = handler.data;
     2692
     2693                                var ret = handler.apply(this, arguments);
     2694
     2695                                if( ret !== undefined ){
     2696                                        event.result = ret;
     2697                                        if ( ret === false ) {
     2698                                                event.preventDefault();
     2699                                                event.stopPropagation();
     2700                                        }
     2701                                }
     2702
     2703                                if( event.isImmediatePropagationStopped() )
     2704                                        break;
     2705
     2706                        }
     2707                }
     2708        },
     2709
     2710        props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
     2711
     2712        fix: function(event) {
     2713                if ( event[expando] )
     2714                        return event;
     2715
     2716                // store a copy of the original event object
     2717                // and "clone" to set read-only properties
     2718                var originalEvent = event;
     2719                event = jQuery.Event( originalEvent );
     2720
     2721                for ( var i = this.props.length, prop; i; ){
     2722                        prop = this.props[ --i ];
     2723                        event[ prop ] = originalEvent[ prop ];
     2724                }
     2725
     2726                // Fix target property, if necessary
     2727                if ( !event.target )
     2728                        event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
     2729
     2730                // check if target is a textnode (safari)
     2731                if ( event.target.nodeType == 3 )
     2732                        event.target = event.target.parentNode;
     2733
     2734                // Add relatedTarget, if necessary
     2735                if ( !event.relatedTarget && event.fromElement )
     2736                        event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
     2737
     2738                // Calculate pageX/Y if missing and clientX/Y available
     2739                if ( event.pageX == null && event.clientX != null ) {
     2740                        var doc = document.documentElement, body = document.body;
     2741                        event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
     2742                        event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
     2743                }
     2744
     2745                // Add which for key events
     2746                if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
     2747                        event.which = event.charCode || event.keyCode;
     2748
     2749                // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
     2750                if ( !event.metaKey && event.ctrlKey )
     2751                        event.metaKey = event.ctrlKey;
     2752
     2753                // Add which for click: 1 == left; 2 == middle; 3 == right
     2754                // Note: button is not normalized, so don't use it
     2755                if ( !event.which && event.button )
     2756                        event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
     2757
     2758                return event;
     2759        },
     2760
     2761        proxy: function( fn, proxy ){
     2762                proxy = proxy || function(){ return fn.apply(this, arguments); };
     2763                // Set the guid of unique handler to the same of original handler, so it can be removed
     2764                proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
     2765                // So proxy can be declared as an argument
     2766                return proxy;
     2767        },
     2768
     2769        special: {
     2770                ready: {
     2771                        // Make sure the ready event is setup
     2772                        setup: bindReady,
     2773                        teardown: function() {}
     2774                }
     2775        },
     2776       
     2777        specialAll: {
     2778                live: {
     2779                        setup: function( selector, namespaces ){
     2780                                jQuery.event.add( this, namespaces[0], liveHandler );
     2781                        },
     2782                        teardown:  function( namespaces ){
     2783                                if ( namespaces.length ) {
     2784                                        var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
     2785                                       
     2786                                        jQuery.each( (jQuery.data(this, "events").live || {}), function(){
     2787                                                if ( name.test(this.type) )
     2788                                                        remove++;
     2789                                        });
     2790                                       
     2791                                        if ( remove < 1 )
     2792                                                jQuery.event.remove( this, namespaces[0], liveHandler );
     2793                                }
     2794                        }
     2795                }
     2796        }
     2797};
     2798
     2799jQuery.Event = function( src ){
     2800        // Allow instantiation without the 'new' keyword
     2801        if( !this.preventDefault )
     2802                return new jQuery.Event(src);
     2803       
     2804        // Event object
     2805        if( src && src.type ){
     2806                this.originalEvent = src;
     2807                this.type = src.type;
     2808        // Event type
     2809        }else
     2810                this.type = src;
     2811
     2812        // timeStamp is buggy for some events on Firefox(#3843)
     2813        // So we won't rely on the native value
     2814        this.timeStamp = now();
     2815       
     2816        // Mark it as fixed
     2817        this[expando] = true;
     2818};
     2819
     2820function returnFalse(){
     2821        return false;
     2822}
     2823function returnTrue(){
     2824        return true;
     2825}
     2826
     2827// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
     2828// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
     2829jQuery.Event.prototype = {
     2830        preventDefault: function() {
     2831                this.isDefaultPrevented = returnTrue;
     2832
     2833                var e = this.originalEvent;
     2834                if( !e )
     2835                        return;
     2836                // if preventDefault exists run it on the original event
     2837                if (e.preventDefault)
     2838                        e.preventDefault();
     2839                // otherwise set the returnValue property of the original event to false (IE)
     2840                e.returnValue = false;
     2841        },
     2842        stopPropagation: function() {
     2843                this.isPropagationStopped = returnTrue;
     2844
     2845                var e = this.originalEvent;
     2846                if( !e )
     2847                        return;
     2848                // if stopPropagation exists run it on the original event
     2849                if (e.stopPropagation)
     2850                        e.stopPropagation();
     2851                // otherwise set the cancelBubble property of the original event to true (IE)
     2852                e.cancelBubble = true;
     2853        },
     2854        stopImmediatePropagation:function(){
     2855                this.isImmediatePropagationStopped = returnTrue;
     2856                this.stopPropagation();
     2857        },
     2858        isDefaultPrevented: returnFalse,
     2859        isPropagationStopped: returnFalse,
     2860        isImmediatePropagationStopped: returnFalse
     2861};
     2862// Checks if an event happened on an element within another element
     2863// Used in jQuery.event.special.mouseenter and mouseleave handlers
     2864var withinElement = function(event) {
     2865        // Check if mouse(over|out) are still within the same parent element
     2866        var parent = event.relatedTarget;
     2867        // Traverse up the tree
     2868        while ( parent && parent != this )
     2869                try { parent = parent.parentNode; }
     2870                catch(e) { parent = this; }
     2871       
     2872        if( parent != this ){
     2873                // set the correct event type
     2874                event.type = event.data;
     2875                // handle event if we actually just moused on to a non sub-element
     2876                jQuery.event.handle.apply( this, arguments );
     2877        }
     2878};
     2879       
     2880jQuery.each({
     2881        mouseover: 'mouseenter',
     2882        mouseout: 'mouseleave'
     2883}, function( orig, fix ){
     2884        jQuery.event.special[ fix ] = {
     2885                setup: function(){
     2886                        jQuery.event.add( this, orig, withinElement, fix );
     2887                },
     2888                teardown: function(){
     2889                        jQuery.event.remove( this, orig, withinElement );
     2890                }
     2891        };                         
     2892});
     2893
     2894jQuery.fn.extend({
     2895        bind: function( type, data, fn ) {
     2896                return type == "unload" ? this.one(type, data, fn) : this.each(function(){
     2897                        jQuery.event.add( this, type, fn || data, fn && data );
     2898                });
     2899        },
     2900
     2901        one: function( type, data, fn ) {
     2902                var one = jQuery.event.proxy( fn || data, function(event) {
     2903                        jQuery(this).unbind(event, one);
     2904                        return (fn || data).apply( this, arguments );
     2905                });
     2906                return this.each(function(){
     2907                        jQuery.event.add( this, type, one, fn && data);
     2908                });
     2909        },
     2910
     2911        unbind: function( type, fn ) {
     2912                return this.each(function(){
     2913                        jQuery.event.remove( this, type, fn );
     2914                });
     2915        },
     2916
     2917        trigger: function( type, data ) {
     2918                return this.each(function(){
     2919                        jQuery.event.trigger( type, data, this );
     2920                });
     2921        },
     2922
     2923        triggerHandler: function( type, data ) {
     2924                if( this[0] ){
     2925                        var event = jQuery.Event(type);
     2926                        event.preventDefault();
     2927                        event.stopPropagation();
     2928                        jQuery.event.trigger( event, data, this[0] );
     2929                        return event.result;
     2930                }               
     2931        },
     2932
     2933        toggle: function( fn ) {
     2934                // Save reference to arguments for access in closure
     2935                var args = arguments, i = 1;
     2936
     2937                // link all the functions, so any of them can unbind this click handler
     2938                while( i < args.length )
     2939                        jQuery.event.proxy( fn, args[i++] );
     2940
     2941                return this.click( jQuery.event.proxy( fn, function(event) {
     2942                        // Figure out which function to execute
     2943                        this.lastToggle = ( this.lastToggle || 0 ) % i;
     2944
     2945                        // Make sure that clicks stop
     2946                        event.preventDefault();
     2947
     2948                        // and execute the function
     2949                        return args[ this.lastToggle++ ].apply( this, arguments ) || false;
     2950                }));
     2951        },
     2952
     2953        hover: function(fnOver, fnOut) {
     2954                return this.mouseenter(fnOver).mouseleave(fnOut);
     2955        },
     2956
     2957        ready: function(fn) {
     2958                // Attach the listeners
     2959                bindReady();
     2960
     2961                // If the DOM is already ready
     2962                if ( jQuery.isReady )
     2963                        // Execute the function immediately
     2964                        fn.call( document, jQuery );
     2965
     2966                // Otherwise, remember the function for later
     2967                else
     2968                        // Add the function to the wait list
     2969                        jQuery.readyList.push( fn );
     2970
     2971                return this;
     2972        },
     2973       
     2974        live: function( type, fn ){
     2975                var proxy = jQuery.event.proxy( fn );
     2976                proxy.guid += this.selector + type;
     2977
     2978                jQuery(document).bind( liveConvert(type, this.selector), this.selector, proxy );
     2979
     2980                return this;
     2981        },
     2982       
     2983        die: function( type, fn ){
     2984                jQuery(document).unbind( liveConvert(type, this.selector), fn ? { guid: fn.guid + this.selector + type } : null );
     2985                return this;
     2986        }
     2987});
     2988
     2989function liveHandler( event ){
     2990        var check = RegExp("(^|\\.)" + event.type + "(\\.|$)"),
     2991                stop = true,
     2992                elems = [];
     2993
     2994        jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){
     2995                if ( check.test(fn.type) ) {
     2996                        var elem = jQuery(event.target).closest(fn.data)[0];
     2997                        if ( elem )
     2998                                elems.push({ elem: elem, fn: fn });
     2999                }
     3000        });
     3001
     3002        elems.sort(function(a,b) {
     3003                return jQuery.data(a.elem, "closest") - jQuery.data(b.elem, "closest");
     3004        });
     3005       
     3006        jQuery.each(elems, function(){
     3007                if ( this.fn.call(this.elem, event, this.fn.data) === false )
     3008                        return (stop = false);
     3009        });
     3010
     3011        return stop;
     3012}
     3013
     3014function liveConvert(type, selector){
     3015        return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join(".");
     3016}
     3017
     3018jQuery.extend({
     3019        isReady: false,
     3020        readyList: [],
     3021        // Handle when the DOM is ready
     3022        ready: function() {
     3023                // Make sure that the DOM is not already loaded
     3024                if ( !jQuery.isReady ) {
     3025                        // Remember that the DOM is ready
     3026                        jQuery.isReady = true;
     3027
     3028                        // If there are functions bound, to execute
     3029                        if ( jQuery.readyList ) {
     3030                                // Execute all of them
     3031                                jQuery.each( jQuery.readyList, function(){
     3032                                        this.call( document, jQuery );
     3033                                });
     3034
     3035                                // Reset the list of functions
     3036                                jQuery.readyList = null;
     3037                        }
     3038
     3039                        // Trigger any bound ready events
     3040                        jQuery(document).triggerHandler("ready");
     3041                }
     3042        }
     3043});
     3044
     3045var readyBound = false;
     3046
     3047function bindReady(){
     3048        if ( readyBound ) return;
     3049        readyBound = true;
     3050
     3051        // Mozilla, Opera and webkit nightlies currently support this event
     3052        if ( document.addEventListener ) {
     3053                // Use the handy event callback
     3054                document.addEventListener( "DOMContentLoaded", function(){
     3055                        document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
     3056                        jQuery.ready();
     3057                }, false );
     3058
     3059        // If IE event model is used
     3060        } else if ( document.attachEvent ) {
     3061                // ensure firing before onload,
     3062                // maybe late but safe also for iframes
     3063                document.attachEvent("onreadystatechange", function(){
     3064                        if ( document.readyState === "complete" ) {
     3065                                document.detachEvent( "onreadystatechange", arguments.callee );
     3066                                jQuery.ready();
     3067                        }
     3068                });
     3069
     3070                // If IE and not an iframe
     3071                // continually check to see if the document is ready
     3072                if ( document.documentElement.doScroll && window == window.top ) (function(){
     3073                        if ( jQuery.isReady ) return;
     3074
     3075                        try {
     3076                                // If IE is used, use the trick by Diego Perini
     3077                                // http://javascript.nwbox.com/IEContentLoaded/
     3078                                document.documentElement.doScroll("left");
     3079                        } catch( error ) {
     3080                                setTimeout( arguments.callee, 0 );
     3081                                return;
     3082                        }
     3083
     3084                        // and execute any waiting functions
     3085                        jQuery.ready();
     3086                })();
     3087        }
     3088
     3089        // A fallback to window.onload, that will always work
     3090        jQuery.event.add( window, "load", jQuery.ready );
     3091}
     3092
     3093jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
     3094        "mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave," +
     3095        "change,select,submit,keydown,keypress,keyup,error").split(","), function(i, name){
     3096
     3097        // Handle event binding
     3098        jQuery.fn[name] = function(fn){
     3099                return fn ? this.bind(name, fn) : this.trigger(name);
     3100        };
     3101});
     3102
     3103// Prevent memory leaks in IE
     3104// And prevent errors on refresh with events like mouseover in other browsers
     3105// Window isn't included so as not to unbind existing unload events
     3106jQuery( window ).bind( 'unload', function(){
     3107        for ( var id in jQuery.cache )
     3108                // Skip the window
     3109                if ( id != 1 && jQuery.cache[ id ].handle )
     3110                        jQuery.event.remove( jQuery.cache[ id ].handle.elem );
     3111});
     3112(function(){
     3113
     3114        jQuery.support = {};
     3115
     3116        var root = document.documentElement,
     3117                script = document.createElement("script"),
     3118                div = document.createElement("div"),
     3119                id = "script" + (new Date).getTime();
     3120
     3121        div.style.display = "none";
     3122        div.innerHTML = '   <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';
     3123
     3124        var all = div.getElementsByTagName("*"),
     3125                a = div.getElementsByTagName("a")[0];
     3126
     3127        // Can't get basic test support
     3128        if ( !all || !all.length || !a ) {
     3129                return;
     3130        }
     3131
     3132        jQuery.support = {
     3133                // IE strips leading whitespace when .innerHTML is used
     3134                leadingWhitespace: div.firstChild.nodeType == 3,
     3135               
     3136                // Make sure that tbody elements aren't automatically inserted
     3137                // IE will insert them into empty tables
     3138                tbody: !div.getElementsByTagName("tbody").length,
     3139               
     3140                // Make sure that you can get all elements in an <object> element
     3141                // IE 7 always returns no results
     3142                objectAll: !!div.getElementsByTagName("object")[0]
     3143                        .getElementsByTagName("*").length,
     3144               
     3145                // Make sure that link elements get serialized correctly by innerHTML
     3146                // This requires a wrapper element in IE
     3147                htmlSerialize: !!div.getElementsByTagName("link").length,
     3148               
     3149                // Get the style information from getAttribute
     3150                // (IE uses .cssText insted)
     3151                style: /red/.test( a.getAttribute("style") ),
     3152               
     3153                // Make sure that URLs aren't manipulated
     3154                // (IE normalizes it by default)
     3155                hrefNormalized: a.getAttribute("href") === "/a",
     3156               
     3157                // Make sure that element opacity exists
     3158                // (IE uses filter instead)
     3159                opacity: a.style.opacity === "0.5",
     3160               
     3161                // Verify style float existence
     3162                // (IE uses styleFloat instead of cssFloat)
     3163                cssFloat: !!a.style.cssFloat,
     3164
     3165                // Will be defined later
     3166                scriptEval: false,
     3167                noCloneEvent: true,
     3168                boxModel: null
     3169        };
     3170       
     3171        script.type = "text/javascript";
     3172        try {
     3173                script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
     3174        } catch(e){}
     3175
     3176        root.insertBefore( script, root.firstChild );
     3177       
     3178        // Make sure that the execution of code works by injecting a script
     3179        // tag with appendChild/createTextNode
     3180        // (IE doesn't support this, fails, and uses .text instead)
     3181        if ( window[ id ] ) {
     3182                jQuery.support.scriptEval = true;
     3183                delete window[ id ];
     3184        }
     3185
     3186        root.removeChild( script );
     3187
     3188        if ( div.attachEvent && div.fireEvent ) {
     3189                div.attachEvent("onclick", function(){
     3190                        // Cloning a node shouldn't copy over any
     3191                        // bound event handlers (IE does this)
     3192                        jQuery.support.noCloneEvent = false;
     3193                        div.detachEvent("onclick", arguments.callee);
     3194                });
     3195                div.cloneNode(true).fireEvent("onclick");
     3196        }
     3197
     3198        // Figure out if the W3C box model works as expected
     3199        // document.body must exist before we can do this
     3200        jQuery(function(){
     3201                var div = document.createElement("div");
     3202                div.style.width = div.style.paddingLeft = "1px";
     3203
     3204                document.body.appendChild( div );
     3205                jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
     3206                document.body.removeChild( div ).style.display = 'none';
     3207        });
     3208})();
     3209
     3210var styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat";
     3211
     3212jQuery.props = {
     3213        "for": "htmlFor",
     3214        "class": "className",
     3215        "float": styleFloat,
     3216        cssFloat: styleFloat,
     3217        styleFloat: styleFloat,
     3218        readonly: "readOnly",
     3219        maxlength: "maxLength",
     3220        cellspacing: "cellSpacing",
     3221        rowspan: "rowSpan",
     3222        tabindex: "tabIndex"
     3223};
     3224jQuery.fn.extend({
     3225        // Keep a copy of the old load
     3226        _load: jQuery.fn.load,
     3227
     3228        load: function( url, params, callback ) {
     3229                if ( typeof url !== "string" )
     3230                        return this._load( url );
     3231
     3232                var off = url.indexOf(" ");
     3233                if ( off >= 0 ) {
     3234                        var selector = url.slice(off, url.length);
     3235                        url = url.slice(0, off);
     3236                }
     3237
     3238                // Default to a GET request
     3239                var type = "GET";
     3240
     3241                // If the second parameter was provided
     3242                if ( params )
     3243                        // If it's a function
     3244                        if ( jQuery.isFunction( params ) ) {
     3245                                // We assume that it's the callback
     3246                                callback = params;
     3247                                params = null;
     3248
     3249                        // Otherwise, build a param string
     3250                        } else if( typeof params === "object" ) {
     3251                                params = jQuery.param( params );
     3252                                type = "POST";
     3253                        }
     3254
     3255                var self = this;
     3256
     3257                // Request the remote document
     3258                jQuery.ajax({
     3259                        url: url,
     3260                        type: type,
     3261                        dataType: "html",
     3262                        data: params,
     3263                        complete: function(res, status){
     3264                                // If successful, inject the HTML into all the matched elements
     3265                                if ( status == "success" || status == "notmodified" )
     3266                                        // See if a selector was specified
     3267                                        self.html( selector ?
     3268                                                // Create a dummy div to hold the results
     3269                                                jQuery("<div/>")
     3270                                                        // inject the contents of the document in, removing the scripts
     3271                                                        // to avoid any 'Permission Denied' errors in IE
     3272                                                        .append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
     3273
     3274                                                        // Locate the specified elements
     3275                                                        .find(selector) :
     3276
     3277                                                // If not, just inject the full result
     3278                                                res.responseText );
     3279
     3280                                if( callback )
     3281                                        self.each( callback, [res.responseText, status, res] );
     3282                        }
     3283                });
     3284                return this;
     3285        },
     3286
     3287        serialize: function() {
     3288                return jQuery.param(this.serializeArray());
     3289        },
     3290        serializeArray: function() {
     3291                return this.map(function(){
     3292                        return this.elements ? jQuery.makeArray(this.elements) : this;
     3293                })
     3294                .filter(function(){
     3295                        return this.name && !this.disabled &&
     3296                                (this.checked || /select|textarea/i.test(this.nodeName) ||
     3297                                        /text|hidden|password|search/i.test(this.type));
     3298                })
     3299                .map(function(i, elem){
     3300                        var val = jQuery(this).val();
     3301                        return val == null ? null :
     3302                                jQuery.isArray(val) ?
     3303                                        jQuery.map( val, function(val, i){
     3304                                                return {name: elem.name, value: val};
     3305                                        }) :
     3306                                        {name: elem.name, value: val};
     3307                }).get();
     3308        }
     3309});
     3310
     3311// Attach a bunch of functions for handling common AJAX events
     3312jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
     3313        jQuery.fn[o] = function(f){
     3314                return this.bind(o, f);
     3315        };
     3316});
     3317
     3318var jsc = now();
     3319
     3320jQuery.extend({
     3321 
     3322        get: function( url, data, callback, type ) {
     3323                // shift arguments if data argument was ommited
     3324                if ( jQuery.isFunction( data ) ) {
     3325                        callback = data;
     3326                        data = null;
     3327                }
     3328
     3329                return jQuery.ajax({
     3330                        type: "GET",
     3331                        url: url,
     3332                        data: data,
     3333                        success: callback,
     3334                        dataType: type
     3335                });
     3336        },
     3337
     3338        getScript: function( url, callback ) {
     3339                return jQuery.get(url, null, callback, "script");
     3340        },
     3341
     3342        getJSON: function( url, data, callback ) {
     3343                return jQuery.get(url, data, callback, "json");
     3344        },
     3345
     3346        post: function( url, data, callback, type ) {
     3347                if ( jQuery.isFunction( data ) ) {
     3348                        callback = data;
     3349                        data = {};
     3350                }
     3351
     3352                return jQuery.ajax({
     3353                        type: "POST",
     3354                        url: url,
     3355                        data: data,
     3356                        success: callback,
     3357                        dataType: type
     3358                });
     3359        },
     3360
     3361        ajaxSetup: function( settings ) {
     3362                jQuery.extend( jQuery.ajaxSettings, settings );
     3363        },
     3364
     3365        ajaxSettings: {
     3366                url: location.href,
     3367                global: true,
     3368                type: "GET",
     3369                contentType: "application/x-www-form-urlencoded",
     3370                processData: true,
     3371                async: true,
     3372                /*
     3373                timeout: 0,
     3374                data: null,
     3375                username: null,
     3376                password: null,
     3377                */
     3378                // Create the request object; Microsoft failed to properly
     3379                // implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
     3380                // This function can be overriden by calling jQuery.ajaxSetup
     3381                xhr:function(){
     3382                        return window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
     3383                },
     3384                accepts: {
     3385                        xml: "application/xml, text/xml",
     3386                        html: "text/html",
     3387                        script: "text/javascript, application/javascript",
     3388                        json: "application/json, text/javascript",
     3389                        text: "text/plain",
     3390                        _default: "*/*"
     3391                }
     3392        },
     3393
     3394        // Last-Modified header cache for next request
     3395        lastModified: {},
     3396
     3397        ajax: function( s ) {
     3398                // Extend the settings, but re-extend 's' so that it can be
     3399                // checked again later (in the test suite, specifically)
     3400                s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
     3401
     3402                var jsonp, jsre = /=\?(&|$)/g, status, data,
     3403                        type = s.type.toUpperCase();
     3404
     3405                // convert data if not already a string
     3406                if ( s.data && s.processData && typeof s.data !== "string" )
     3407                        s.data = jQuery.param(s.data);
     3408
     3409                // Handle JSONP Parameter Callbacks
     3410                if ( s.dataType == "jsonp" ) {
     3411                        if ( type == "GET" ) {
     3412                                if ( !s.url.match(jsre) )
     3413                                        s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
     3414                        } else if ( !s.data || !s.data.match(jsre) )
     3415                                s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
     3416                        s.dataType = "json";
     3417                }
     3418
     3419                // Build temporary JSONP function
     3420                if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
     3421                        jsonp = "jsonp" + jsc++;
     3422
     3423                        // Replace the =? sequence both in the query string and the data
     3424                        if ( s.data )
     3425                                s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
     3426                        s.url = s.url.replace(jsre, "=" + jsonp + "$1");
     3427
     3428                        // We need to make sure
     3429                        // that a JSONP style response is executed properly
     3430                        s.dataType = "script";
     3431
     3432                        // Handle JSONP-style loading
     3433                        window[ jsonp ] = function(tmp){
     3434                                data = tmp;
     3435                                success();
     3436                                complete();
     3437                                // Garbage collect
     3438                                window[ jsonp ] = undefined;
     3439                                try{ delete window[ jsonp ]; } catch(e){}
     3440                                if ( head )
     3441                                        head.removeChild( script );
     3442                        };
     3443                }
     3444
     3445                if ( s.dataType == "script" && s.cache == null )
     3446                        s.cache = false;
     3447
     3448                if ( s.cache === false && type == "GET" ) {
     3449                        var ts = now();
     3450                        // try replacing _= if it is there
     3451                        var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
     3452                        // if nothing was replaced, add timestamp to the end
     3453                        s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
     3454                }
     3455
     3456                // If data is available, append data to url for get requests
     3457                if ( s.data && type == "GET" ) {
     3458                        s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
     3459
     3460                        // IE likes to send both get and post data, prevent this
     3461                        s.data = null;
     3462                }
     3463
     3464                // Watch for a new set of requests
     3465                if ( s.global && ! jQuery.active++ )
     3466                        jQuery.event.trigger( "ajaxStart" );
     3467
     3468                // Matches an absolute URL, and saves the domain
     3469                var parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url );
     3470
     3471                // If we're requesting a remote document
     3472                // and trying to load JSON or Script with a GET
     3473                if ( s.dataType == "script" && type == "GET" && parts
     3474                        && ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){
     3475
     3476                        var head = document.getElementsByTagName("head")[0];
     3477                        var script = document.createElement("script");
     3478                        script.src = s.url;
     3479                        if (s.scriptCharset)
     3480                                script.charset = s.scriptCharset;
     3481
     3482                        // Handle Script loading
     3483                        if ( !jsonp ) {
     3484                                var done = false;
     3485
     3486                                // Attach handlers for all browsers
     3487                                script.onload = script.onreadystatechange = function(){
     3488                                        if ( !done && (!this.readyState ||
     3489                                                        this.readyState == "loaded" || this.readyState == "complete") ) {
     3490                                                done = true;
     3491                                                success();
     3492                                                complete();
     3493
     3494                                                // Handle memory leak in IE
     3495                                                script.onload = script.onreadystatechange = null;
     3496                                                head.removeChild( script );
     3497                                        }
     3498                                };
     3499                        }
     3500
     3501                        head.appendChild(script);
     3502
     3503                        // We handle everything using the script element injection
     3504                        return undefined;
     3505                }
     3506
     3507                var requestDone = false;
     3508
     3509                // Create the request object
     3510                var xhr = s.xhr();
     3511
     3512                // Open the socket
     3513                // Passing null username, generates a login popup on Opera (#2865)
     3514                if( s.username )
     3515                        xhr.open(type, s.url, s.async, s.username, s.password);
     3516                else
     3517                        xhr.open(type, s.url, s.async);
     3518
     3519                // Need an extra try/catch for cross domain requests in Firefox 3
     3520                try {
     3521                        // Set the correct header, if data is being sent
     3522                        if ( s.data )
     3523                                xhr.setRequestHeader("Content-Type", s.contentType);
     3524
     3525                        // Set the If-Modified-Since header, if ifModified mode.
     3526                        if ( s.ifModified )
     3527                                xhr.setRequestHeader("If-Modified-Since",
     3528                                        jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
     3529
     3530                        // Set header so the called script knows that it's an XMLHttpRequest
     3531                        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
     3532
     3533                        // Set the Accepts header for the server, depending on the dataType
     3534                        xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
     3535                                s.accepts[ s.dataType ] + ", */*" :
     3536                                s.accepts._default );
     3537                } catch(e){}
     3538
     3539                // Allow custom headers/mimetypes and early abort
     3540                if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
     3541                        // Handle the global AJAX counter
     3542                        if ( s.global && ! --jQuery.active )
     3543                                jQuery.event.trigger( "ajaxStop" );
     3544                        // close opended socket
     3545                        xhr.abort();
     3546                        return false;
     3547                }
     3548
     3549                if ( s.global )
     3550                        jQuery.event.trigger("ajaxSend", [xhr, s]);
     3551
     3552                // Wait for a response to come back
     3553                var onreadystatechange = function(isTimeout){
     3554                        // The request was aborted, clear the interval and decrement jQuery.active
     3555                        if (xhr.readyState == 0) {
     3556                                if (ival) {
     3557                                        // clear poll interval
     3558                                        clearInterval(ival);
     3559                                        ival = null;
     3560                                        // Handle the global AJAX counter
     3561                                        if ( s.global && ! --jQuery.active )
     3562                                                jQuery.event.trigger( "ajaxStop" );
     3563                                }
     3564                        // The transfer is complete and the data is available, or the request timed out
     3565                        } else if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) {
     3566                                requestDone = true;
     3567
     3568                                // clear poll interval
     3569                                if (ival) {
     3570                                        clearInterval(ival);
     3571                                        ival = null;
     3572                                }
     3573
     3574                                status = isTimeout == "timeout" ? "timeout" :
     3575                                        !jQuery.httpSuccess( xhr ) ? "error" :
     3576                                        s.ifModified && jQuery.httpNotModified( xhr, s.url ) ? "notmodified" :
     3577                                        "success";
     3578
     3579                                if ( status == "success" ) {
     3580                                        // Watch for, and catch, XML document parse errors
     3581                                        try {
     3582                                                // process the data (runs the xml through httpData regardless of callback)
     3583                                                data = jQuery.httpData( xhr, s.dataType, s );
     3584                                        } catch(e) {
     3585                                                status = "parsererror";
     3586                                        }
     3587                                }
     3588
     3589                                // Make sure that the request was successful or notmodified
     3590                                if ( status == "success" ) {
     3591                                        // Cache Last-Modified header, if ifModified mode.
     3592                                        var modRes;
     3593                                        try {
     3594                                                modRes = xhr.getResponseHeader("Last-Modified");
     3595                                        } catch(e) {} // swallow exception thrown by FF if header is not available
     3596
     3597                                        if ( s.ifModified && modRes )
     3598                                                jQuery.lastModified[s.url] = modRes;
     3599
     3600                                        // JSONP handles its own success callback
     3601                                        if ( !jsonp )
     3602                                                success();
     3603                                } else
     3604                                        jQuery.handleError(s, xhr, status);
     3605
     3606                                // Fire the complete handlers
     3607                                complete();
     3608
     3609                                if ( isTimeout )
     3610                                        xhr.abort();
     3611
     3612                                // Stop memory leaks
     3613                                if ( s.async )
     3614                                        xhr = null;
     3615                        }
     3616                };
     3617
     3618                if ( s.async ) {
     3619                        // don't attach the handler to the request, just poll it instead
     3620                        var ival = setInterval(onreadystatechange, 13);
     3621
     3622                        // Timeout checker
     3623                        if ( s.timeout > 0 )
     3624                                setTimeout(function(){
     3625                                        // Check to see if the request is still happening
     3626                                        if ( xhr && !requestDone )
     3627                                                onreadystatechange( "timeout" );
     3628                                }, s.timeout);
     3629                }
     3630
     3631                // Send the data
     3632                try {
     3633                        xhr.send(s.data);
     3634                } catch(e) {
     3635                        jQuery.handleError(s, xhr, null, e);
     3636                }
     3637
     3638                // firefox 1.5 doesn't fire statechange for sync requests
     3639                if ( !s.async )
     3640                        onreadystatechange();
     3641
     3642                function success(){
     3643                        // If a local callback was specified, fire it and pass it the data
     3644                        if ( s.success )
     3645                                s.success( data, status );
     3646
     3647                        // Fire the global callback
     3648                        if ( s.global )
     3649                                jQuery.event.trigger( "ajaxSuccess", [xhr, s] );
     3650                }
     3651
     3652                function complete(){
     3653                        // Process result
     3654                        if ( s.complete )
     3655                                s.complete(xhr, status);
     3656
     3657                        // The request was completed
     3658                        if ( s.global )
     3659                                jQuery.event.trigger( "ajaxComplete", [xhr, s] );
     3660
     3661                        // Handle the global AJAX counter
     3662                        if ( s.global && ! --jQuery.active )
     3663                                jQuery.event.trigger( "ajaxStop" );
     3664                }
     3665
     3666                // return XMLHttpRequest to allow aborting the request etc.
     3667                return xhr;
     3668        },
     3669
     3670        handleError: function( s, xhr, status, e ) {
     3671                // If a local callback was specified, fire it
     3672                if ( s.error ) s.error( xhr, status, e );
     3673
     3674                // Fire the global callback
     3675                if ( s.global )
     3676                        jQuery.event.trigger( "ajaxError", [xhr, s, e] );
     3677        },
     3678
     3679        // Counter for holding the number of active queries
     3680        active: 0,
     3681
     3682        // Determines if an XMLHttpRequest was successful or not
     3683        httpSuccess: function( xhr ) {
     3684                try {
     3685                        // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
     3686                        return !xhr.status && location.protocol == "file:" ||
     3687                                ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status == 304 || xhr.status == 1223;
     3688                } catch(e){}
     3689                return false;
     3690        },
     3691
     3692        // Determines if an XMLHttpRequest returns NotModified
     3693        httpNotModified: function( xhr, url ) {
     3694                try {
     3695                        var xhrRes = xhr.getResponseHeader("Last-Modified");
     3696
     3697                        // Firefox always returns 200. check Last-Modified date
     3698                        return xhr.status == 304 || xhrRes == jQuery.lastModified[url];
     3699                } catch(e){}
     3700                return false;
     3701        },
     3702
     3703        httpData: function( xhr, type, s ) {
     3704                var ct = xhr.getResponseHeader("content-type"),
     3705                        xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0,
     3706                        data = xml ? xhr.responseXML : xhr.responseText;
     3707
     3708                if ( xml && data.documentElement.tagName == "parsererror" )
     3709                        throw "parsererror";
     3710                       
     3711                // Allow a pre-filtering function to sanitize the response
     3712                // s != null is checked to keep backwards compatibility
     3713                if( s && s.dataFilter )
     3714                        data = s.dataFilter( data, type );
     3715
     3716                // The filter can actually parse the response
     3717                if( typeof data === "string" ){
     3718
     3719                        // If the type is "script", eval it in global context
     3720                        if ( type == "script" )
     3721                                jQuery.globalEval( data );
     3722
     3723                        // Get the JavaScript object, if JSON is used.
     3724                        if ( type == "json" )
     3725                                data = window["eval"]("(" + data + ")");
     3726                }
     3727               
     3728                return data;
     3729        },
     3730
     3731        // Serialize an array of form elements or a set of
     3732        // key/values into a query string
     3733        param: function( a ) {
     3734                var s = [ ];
     3735
     3736                function add( key, value ){
     3737                        s[ s.length ] = encodeURIComponent(key) + '=' + encodeURIComponent(value);
     3738                };
     3739
     3740                // If an array was passed in, assume that it is an array
     3741                // of form elements
     3742                if ( jQuery.isArray(a) || a.jquery )
     3743                        // Serialize the form elements
     3744                        jQuery.each( a, function(){
     3745                                add( this.name, this.value );
     3746                        });
     3747
     3748                // Otherwise, assume that it's an object of key/value pairs
     3749                else
     3750                        // Serialize the key/values
     3751                        for ( var j in a )
     3752                                // If the value is an array then the key names need to be repeated
     3753                                if ( jQuery.isArray(a[j]) )
     3754                                        jQuery.each( a[j], function(){
     3755                                                add( j, this );
     3756                                        });
     3757                                else
     3758                                        add( j, jQuery.isFunction(a[j]) ? a[j]() : a[j] );
     3759
     3760                // Return the resulting serialization
     3761                return s.join("&").replace(/%20/g, "+");
     3762        }
     3763
     3764});
     3765var elemdisplay = {},
     3766        timerId,
     3767        fxAttrs = [
     3768                // height animations
     3769                [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
     3770                // width animations
     3771                [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
     3772                // opacity animations
     3773                [ "opacity" ]
     3774        ];
     3775
     3776function genFx( type, num ){
     3777        var obj = {};
     3778        jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function(){
     3779                obj[ this ] = type;
     3780        });
     3781        return obj;
     3782}
     3783
     3784jQuery.fn.extend({
     3785        show: function(speed,callback){
     3786                if ( speed ) {
     3787                        return this.animate( genFx("show", 3), speed, callback);
     3788                } else {
     3789                        for ( var i = 0, l = this.length; i < l; i++ ){
     3790                                var old = jQuery.data(this[i], "olddisplay");
     3791                               
     3792                                this[i].style.display = old || "";
     3793                               
     3794                                if ( jQuery.css(this[i], "display") === "none" ) {
     3795                                        var tagName = this[i].tagName, display;
     3796                                       
     3797                                        if ( elemdisplay[ tagName ] ) {
     3798                                                display = elemdisplay[ tagName ];
     3799                                        } else {
     3800                                                var elem = jQuery("<" + tagName + " />").appendTo("body");
     3801                                               
     3802                                                display = elem.css("display");
     3803                                                if ( display === "none" )
     3804                                                        display = "block";
     3805                                               
     3806                                                elem.remove();
     3807                                               
     3808                                                elemdisplay[ tagName ] = display;
     3809                                        }
     3810                                       
     3811                                        jQuery.data(this[i], "olddisplay", display);
     3812                                }
     3813                        }
     3814
     3815                        // Set the display of the elements in a second loop
     3816                        // to avoid the constant reflow
     3817                        for ( var i = 0, l = this.length; i < l; i++ ){
     3818                                this[i].style.display = jQuery.data(this[i], "olddisplay") || "";
     3819                        }
     3820                       
     3821                        return this;
     3822                }
     3823        },
     3824
     3825        hide: function(speed,callback){
     3826                if ( speed ) {
     3827                        return this.animate( genFx("hide", 3), speed, callback);
     3828                } else {
     3829                        for ( var i = 0, l = this.length; i < l; i++ ){
     3830                                var old = jQuery.data(this[i], "olddisplay");
     3831                                if ( !old && old !== "none" )
     3832                                        jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
     3833                        }
     3834
     3835                        // Set the display of the elements in a second loop
     3836                        // to avoid the constant reflow
     3837                        for ( var i = 0, l = this.length; i < l; i++ ){
     3838                                this[i].style.display = "none";
     3839                        }
     3840
     3841                        return this;
     3842                }
     3843        },
     3844
     3845        // Save the old toggle function
     3846        _toggle: jQuery.fn.toggle,
     3847
     3848        toggle: function( fn, fn2 ){
     3849                var bool = typeof fn === "boolean";
     3850
     3851                return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
     3852                        this._toggle.apply( this, arguments ) :
     3853                        fn == null || bool ?
     3854                                this.each(function(){
     3855                                        var state = bool ? fn : jQuery(this).is(":hidden");
     3856                                        jQuery(this)[ state ? "show" : "hide" ]();
     3857                                }) :
     3858                                this.animate(genFx("toggle", 3), fn, fn2);
     3859        },
     3860
     3861        fadeTo: function(speed,to,callback){
     3862                return this.animate({opacity: to}, speed, callback);
     3863        },
     3864
     3865        animate: function( prop, speed, easing, callback ) {
     3866                var optall = jQuery.speed(speed, easing, callback);
     3867
     3868                return this[ optall.queue === false ? "each" : "queue" ](function(){
     3869               
     3870                        var opt = jQuery.extend({}, optall), p,
     3871                                hidden = this.nodeType == 1 && jQuery(this).is(":hidden"),
     3872                                self = this;
     3873       
     3874                        for ( p in prop ) {
     3875                                if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
     3876                                        return opt.complete.call(this);
     3877
     3878                                if ( ( p == "height" || p == "width" ) && this.style ) {
     3879                                        // Store display property
     3880                                        opt.display = jQuery.css(this, "display");
     3881
     3882                                        // Make sure that nothing sneaks out
     3883                                        opt.overflow = this.style.overflow;
     3884                                }
     3885                        }
     3886
     3887                        if ( opt.overflow != null )
     3888                                this.style.overflow = "hidden";
     3889
     3890                        opt.curAnim = jQuery.extend({}, prop);
     3891
     3892                        jQuery.each( prop, function(name, val){
     3893                                var e = new jQuery.fx( self, opt, name );
     3894
     3895                                if ( /toggle|show|hide/.test(val) )
     3896                                        e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
     3897                                else {
     3898                                        var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
     3899                                                start = e.cur(true) || 0;
     3900
     3901                                        if ( parts ) {
     3902                                                var end = parseFloat(parts[2]),
     3903                                                        unit = parts[3] || "px";
     3904
     3905                                                // We need to compute starting value
     3906                                                if ( unit != "px" ) {
     3907                                                        self.style[ name ] = (end || 1) + unit;
     3908                                                        start = ((end || 1) / e.cur(true)) * start;
     3909                                                        self.style[ name ] = start + unit;
     3910                                                }
     3911
     3912                                                // If a +=/-= token was provided, we're doing a relative animation
     3913                                                if ( parts[1] )
     3914                                                        end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
     3915
     3916                                                e.custom( start, end, unit );
     3917                                        } else
     3918                                                e.custom( start, val, "" );
     3919                                }
     3920                        });
     3921
     3922                        // For JS strict compliance
     3923                        return true;
     3924                });
     3925        },
     3926
     3927        stop: function(clearQueue, gotoEnd){
     3928                var timers = jQuery.timers;
     3929
     3930                if (clearQueue)
     3931                        this.queue([]);
     3932
     3933                this.each(function(){
     3934                        // go in reverse order so anything added to the queue during the loop is ignored
     3935                        for ( var i = timers.length - 1; i >= 0; i-- )
     3936                                if ( timers[i].elem == this ) {
     3937                                        if (gotoEnd)
     3938                                                // force the next step to be the last
     3939                                                timers[i](true);
     3940                                        timers.splice(i, 1);
     3941                                }
     3942                });
     3943
     3944                // start the next in the queue if the last step wasn't forced
     3945                if (!gotoEnd)
     3946                        this.dequeue();
     3947
     3948                return this;
     3949        }
     3950
     3951});
     3952
     3953// Generate shortcuts for custom animations
     3954jQuery.each({
     3955        slideDown: genFx("show", 1),
     3956        slideUp: genFx("hide", 1),
     3957        slideToggle: genFx("toggle", 1),
     3958        fadeIn: { opacity: "show" },
     3959        fadeOut: { opacity: "hide" }
     3960}, function( name, props ){
     3961        jQuery.fn[ name ] = function( speed, callback ){
     3962                return this.animate( props, speed, callback );
     3963        };
     3964});
     3965
     3966jQuery.extend({
     3967
     3968        speed: function(speed, easing, fn) {
     3969                var opt = typeof speed === "object" ? speed : {
     3970                        complete: fn || !fn && easing ||
     3971                                jQuery.isFunction( speed ) && speed,
     3972                        duration: speed,
     3973                        easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
     3974                };
     3975
     3976                opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
     3977                        jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
     3978
     3979                // Queueing
     3980                opt.old = opt.complete;
     3981                opt.complete = function(){
     3982                        if ( opt.queue !== false )
     3983                                jQuery(this).dequeue();
     3984                        if ( jQuery.isFunction( opt.old ) )
     3985                                opt.old.call( this );
     3986                };
     3987
     3988                return opt;
     3989        },
     3990
     3991        easing: {
     3992                linear: function( p, n, firstNum, diff ) {
     3993                        return firstNum + diff * p;
     3994                },
     3995                swing: function( p, n, firstNum, diff ) {
     3996                        return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
     3997                }
     3998        },
     3999
     4000        timers: [],
     4001
     4002        fx: function( elem, options, prop ){
     4003                this.options = options;
     4004                this.elem = elem;
     4005                this.prop = prop;
     4006
     4007                if ( !options.orig )
     4008                        options.orig = {};
     4009        }
     4010
     4011});
     4012
     4013jQuery.fx.prototype = {
     4014
     4015        // Simple function for setting a style value
     4016        update: function(){
     4017                if ( this.options.step )
     4018                        this.options.step.call( this.elem, this.now, this );
     4019
     4020                (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
     4021
     4022                // Set display property to block for height/width animations
     4023                if ( ( this.prop == "height" || this.prop == "width" ) && this.elem.style )
     4024                        this.elem.style.display = "block";
     4025        },
     4026
     4027        // Get the current size
     4028        cur: function(force){
     4029                if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) )
     4030                        return this.elem[ this.prop ];
     4031
     4032                var r = parseFloat(jQuery.css(this.elem, this.prop, force));
     4033                return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
     4034        },
     4035
     4036        // Start an animation from one number to another
     4037        custom: function(from, to, unit){
     4038                this.startTime = now();
     4039                this.start = from;
     4040                this.end = to;
     4041                this.unit = unit || this.unit || "px";
     4042                this.now = this.start;
     4043                this.pos = this.state = 0;
     4044
     4045                var self = this;
     4046                function t(gotoEnd){
     4047                        return self.step(gotoEnd);
     4048                }
     4049
     4050                t.elem = this.elem;
     4051
     4052                if ( t() && jQuery.timers.push(t) && !timerId ) {
     4053                        timerId = setInterval(function(){
     4054                                var timers = jQuery.timers;
     4055
     4056                                for ( var i = 0; i < timers.length; i++ )
     4057                                        if ( !timers[i]() )
     4058                                                timers.splice(i--, 1);
     4059
     4060                                if ( !timers.length ) {
     4061                                        clearInterval( timerId );
     4062                                        timerId = undefined;
     4063                                }
     4064                        }, 13);
     4065                }
     4066        },
     4067
     4068        // Simple 'show' function
     4069        show: function(){
     4070                // Remember where we started, so that we can go back to it later
     4071                this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
     4072                this.options.show = true;
     4073
     4074                // Begin the animation
     4075                // Make sure that we start at a small width/height to avoid any
     4076                // flash of content
     4077                this.custom(this.prop == "width" || this.prop == "height" ? 1 : 0, this.cur());
     4078
     4079                // Start by showing the element
     4080                jQuery(this.elem).show();
     4081        },
     4082
     4083        // Simple 'hide' function
     4084        hide: function(){
     4085                // Remember where we started, so that we can go back to it later
     4086                this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
     4087                this.options.hide = true;
     4088
     4089                // Begin the animation
     4090                this.custom(this.cur(), 0);
     4091        },
     4092
     4093        // Each step of an animation
     4094        step: function(gotoEnd){
     4095                var t = now();
     4096
     4097                if ( gotoEnd || t >= this.options.duration + this.startTime ) {
     4098                        this.now = this.end;
     4099                        this.pos = this.state = 1;
     4100                        this.update();
     4101
     4102                        this.options.curAnim[ this.prop ] = true;
     4103
     4104                        var done = true;
     4105                        for ( var i in this.options.curAnim )
     4106                                if ( this.options.curAnim[i] !== true )
     4107                                        done = false;
     4108
     4109                        if ( done ) {
     4110                                if ( this.options.display != null ) {
     4111                                        // Reset the overflow
     4112                                        this.elem.style.overflow = this.options.overflow;
     4113
     4114                                        // Reset the display
     4115                                        this.elem.style.display = this.options.display;
     4116                                        if ( jQuery.css(this.elem, "display") == "none" )
     4117                                                this.elem.style.display = "block";
     4118                                }
     4119
     4120                                // Hide the element if the "hide" operation was done
     4121                                if ( this.options.hide )
     4122                                        jQuery(this.elem).hide();
     4123
     4124                                // Reset the properties, if the item has been hidden or shown
     4125                                if ( this.options.hide || this.options.show )
     4126                                        for ( var p in this.options.curAnim )
     4127                                                jQuery.attr(this.elem.style, p, this.options.orig[p]);
     4128                                       
     4129                                // Execute the complete function
     4130                                this.options.complete.call( this.elem );
     4131                        }
     4132
     4133                        return false;
     4134                } else {
     4135                        var n = t - this.startTime;
     4136                        this.state = n / this.options.duration;
     4137
     4138                        // Perform the easing function, defaults to swing
     4139                        this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
     4140                        this.now = this.start + ((this.end - this.start) * this.pos);
     4141
     4142                        // Perform the next step of the animation
     4143                        this.update();
     4144                }
     4145
     4146                return true;
     4147        }
     4148
     4149};
     4150
     4151jQuery.extend( jQuery.fx, {
     4152        speeds:{
     4153                slow: 600,
     4154                fast: 200,
     4155                // Default speed
     4156                _default: 400
     4157        },
     4158        step: {
     4159
     4160                opacity: function(fx){
     4161                        jQuery.attr(fx.elem.style, "opacity", fx.now);
     4162                },
     4163
     4164                _default: function(fx){
     4165                        if ( fx.elem.style && fx.elem.style[ fx.prop ] != null )
     4166                                fx.elem.style[ fx.prop ] = fx.now + fx.unit;
     4167                        else
     4168                                fx.elem[ fx.prop ] = fx.now;
     4169                }
     4170        }
     4171});
     4172if ( document.documentElement["getBoundingClientRect"] )
     4173        jQuery.fn.offset = function() {
     4174                if ( !this[0] ) return { top: 0, left: 0 };
     4175                if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
     4176                var box  = this[0].getBoundingClientRect(), doc = this[0].ownerDocument, body = doc.body, docElem = doc.documentElement,
     4177                        clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
     4178                        top  = box.top  + (self.pageYOffset || jQuery.boxModel && docElem.scrollTop  || body.scrollTop ) - clientTop,
     4179                        left = box.left + (self.pageXOffset || jQuery.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
     4180                return { top: top, left: left };
     4181        };
     4182else
     4183        jQuery.fn.offset = function() {
     4184                if ( !this[0] ) return { top: 0, left: 0 };
     4185                if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
     4186                jQuery.offset.initialized || jQuery.offset.initialize();
     4187
     4188                var elem = this[0], offsetParent = elem.offsetParent, prevOffsetParent = elem,
     4189                        doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
     4190                        body = doc.body, defaultView = doc.defaultView,
     4191                        prevComputedStyle = defaultView.getComputedStyle(elem, null),
     4192                        top = elem.offsetTop, left = elem.offsetLeft;
     4193
     4194                while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
     4195                        computedStyle = defaultView.getComputedStyle(elem, null);
     4196                        top -= elem.scrollTop, left -= elem.scrollLeft;
     4197                        if ( elem === offsetParent ) {
     4198                                top += elem.offsetTop, left += elem.offsetLeft;
     4199                                if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.tagName)) )
     4200                                        top  += parseInt( computedStyle.borderTopWidth,  10) || 0,
     4201                                        left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
     4202                                prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
     4203                        }
     4204                        if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" )
     4205                                top  += parseInt( computedStyle.borderTopWidth,  10) || 0,
     4206                                left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
     4207                        prevComputedStyle = computedStyle;
     4208                }
     4209
     4210                if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" )
     4211                        top  += body.offsetTop,
     4212                        left += body.offsetLeft;
     4213
     4214                if ( prevComputedStyle.position === "fixed" )
     4215                        top  += Math.max(docElem.scrollTop, body.scrollTop),
     4216                        left += Math.max(docElem.scrollLeft, body.scrollLeft);
     4217
     4218                return { top: top, left: left };
     4219        };
     4220
     4221jQuery.offset = {
     4222        initialize: function() {
     4223                if ( this.initialized ) return;
     4224                var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, rules, prop, bodyMarginTop = body.style.marginTop,
     4225                        html = '<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';
     4226
     4227                rules = { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' };
     4228                for ( prop in rules ) container.style[prop] = rules[prop];
     4229
     4230                container.innerHTML = html;
     4231                body.insertBefore(container, body.firstChild);
     4232                innerDiv = container.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild;
     4233
     4234                this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
     4235                this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
     4236
     4237                innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative';
     4238                this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
     4239
     4240                body.style.marginTop = '1px';
     4241                this.doesNotIncludeMarginInBodyOffset = (body.offsetTop === 0);
     4242                body.style.marginTop = bodyMarginTop;
     4243
     4244                body.removeChild(container);
     4245                this.initialized = true;
     4246        },
     4247
     4248        bodyOffset: function(body) {
     4249                jQuery.offset.initialized || jQuery.offset.initialize();
     4250                var top = body.offsetTop, left = body.offsetLeft;
     4251                if ( jQuery.offset.doesNotIncludeMarginInBodyOffset )
     4252                        top  += parseInt( jQuery.curCSS(body, 'marginTop',  true), 10 ) || 0,
     4253                        left += parseInt( jQuery.curCSS(body, 'marginLeft', true), 10 ) || 0;
     4254                return { top: top, left: left };
     4255        }
     4256};
     4257
     4258
     4259jQuery.fn.extend({
     4260        position: function() {
     4261                var left = 0, top = 0, results;
     4262
     4263                if ( this[0] ) {
     4264                        // Get *real* offsetParent
     4265                        var offsetParent = this.offsetParent(),
     4266
     4267                        // Get correct offsets
     4268                        offset       = this.offset(),
     4269                        parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset();
     4270
     4271                        // Subtract element margins
     4272                        // note: when an element has margin: auto the offsetLeft and marginLeft
     4273                        // are the same in Safari causing offset.left to incorrectly be 0
     4274                        offset.top  -= num( this, 'marginTop'  );
     4275                        offset.left -= num( this, 'marginLeft' );
     4276
     4277                        // Add offsetParent borders
     4278                        parentOffset.top  += num( offsetParent, 'borderTopWidth'  );
     4279                        parentOffset.left += num( offsetParent, 'borderLeftWidth' );
     4280
     4281                        // Subtract the two offsets
     4282                        results = {
     4283                                top:  offset.top  - parentOffset.top,
     4284                                left: offset.left - parentOffset.left
     4285                        };
     4286                }
     4287
     4288                return results;
     4289        },
     4290
     4291        offsetParent: function() {
     4292                var offsetParent = this[0].offsetParent || document.body;
     4293                while ( offsetParent && (!/^body|html$/i.test(offsetParent.tagName) && jQuery.css(offsetParent, 'position') == 'static') )
     4294                        offsetParent = offsetParent.offsetParent;
     4295                return jQuery(offsetParent);
     4296        }
     4297});
     4298
     4299
     4300// Create scrollLeft and scrollTop methods
     4301jQuery.each( ['Left', 'Top'], function(i, name) {
     4302        var method = 'scroll' + name;
     4303       
     4304        jQuery.fn[ method ] = function(val) {
     4305                if (!this[0]) return null;
     4306
     4307                return val !== undefined ?
     4308
     4309                        // Set the scroll offset
     4310                        this.each(function() {
     4311                                this == window || this == document ?
     4312                                        window.scrollTo(
     4313                                                !i ? val : jQuery(window).scrollLeft(),
     4314                                                 i ? val : jQuery(window).scrollTop()
     4315                                        ) :
     4316                                        this[ method ] = val;
     4317                        }) :
     4318
     4319                        // Return the scroll offset
     4320                        this[0] == window || this[0] == document ?
     4321                                self[ i ? 'pageYOffset' : 'pageXOffset' ] ||
     4322                                        jQuery.boxModel && document.documentElement[ method ] ||
     4323                                        document.body[ method ] :
     4324                                this[0][ method ];
     4325        };
     4326});
     4327// Create innerHeight, innerWidth, outerHeight and outerWidth methods
     4328jQuery.each([ "Height", "Width" ], function(i, name){
     4329
     4330        var tl = i ? "Left"  : "Top",  // top or left
     4331                br = i ? "Right" : "Bottom", // bottom or right
     4332                lower = name.toLowerCase();
     4333
     4334        // innerHeight and innerWidth
     4335        jQuery.fn["inner" + name] = function(){
     4336                return this[0] ?
     4337                        jQuery.css( this[0], lower, false, "padding" ) :
     4338                        null;
     4339        };
     4340
     4341        // outerHeight and outerWidth
     4342        jQuery.fn["outer" + name] = function(margin) {
     4343                return this[0] ?
     4344                        jQuery.css( this[0], lower, false, margin ? "margin" : "border" ) :
     4345                        null;
     4346        };
     4347       
     4348        var type = name.toLowerCase();
     4349
     4350        jQuery.fn[ type ] = function( size ) {
     4351                // Get window width or height
     4352                return this[0] == window ?
     4353                        // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
     4354                        document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] ||
     4355                        document.body[ "client" + name ] :
     4356
     4357                        // Get document width or height
     4358                        this[0] == document ?
     4359                                // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
     4360                                Math.max(
     4361                                        document.documentElement["client" + name],
     4362                                        document.body["scroll" + name], document.documentElement["scroll" + name],
     4363                                        document.body["offset" + name], document.documentElement["offset" + name]
     4364                                ) :
     4365
     4366                                // Get or set width or height on the element
     4367                                size === undefined ?
     4368                                        // Get width or height on the element
     4369                                        (this.length ? jQuery.css( this[0], type ) : null) :
     4370
     4371                                        // Set the width or height on the element (default to pixels if value is unitless)
     4372                                        this.css( type, typeof size === "string" ? size : size + "px" );
     4373        };
     4374
     4375});
     4376})();
  • new file src/allmydata/web/protovis-r3.2.js

    diff --git a/src/allmydata/web/protovis-r3.2.js b/src/allmydata/web/protovis-r3.2.js
    new file mode 100644
    index 0000000..95bb3be
    - +  
     1// fba9dc2
     2var a;if(!Array.prototype.map)Array.prototype.map=function(b,c){for(var d=this.length,f=new Array(d),g=0;g<d;g++)if(g in this)f[g]=b.call(c,this[g],g,this);return f};if(!Array.prototype.filter)Array.prototype.filter=function(b,c){for(var d=this.length,f=[],g=0;g<d;g++)if(g in this){var h=this[g];b.call(c,h,g,this)&&f.push(h)}return f};if(!Array.prototype.forEach)Array.prototype.forEach=function(b,c){for(var d=this.length>>>0,f=0;f<d;f++)f in this&&b.call(c,this[f],f,this)};
     3if(!Array.prototype.reduce)Array.prototype.reduce=function(b,c){var d=this.length;if(!d&&arguments.length==1)throw new Error("reduce: empty array, no initial value");var f=0;if(arguments.length<2)for(;;){if(f in this){c=this[f++];break}if(++f>=d)throw new Error("reduce: no values, no initial value");}for(;f<d;f++)if(f in this)c=b(c,this[f],f,this);return c};var pv={};pv.version={major:3,minor:2};pv.identity=function(b){return b};pv.index=function(){return this.index};pv.child=function(){return this.childIndex};
     4pv.parent=function(){return this.parent.index};pv.extend=function(b){function c(){}c.prototype=b.prototype||b;return new c};
     5try{eval("pv.parse = function(x) x;")}catch(e){pv.parse=function(b){for(var c=new RegExp("function\\s*(\\b\\w+)?\\s*\\([^)]*\\)\\s*","mg"),d,f,g=0,h="";d=c.exec(b);){d=d.index+d[0].length;if(b.charAt(d)!="{"){h+=b.substring(g,d)+"{return ";g=d;for(var i=0;i>=0&&d<b.length;d++){var j=b.charAt(d);switch(j){case '"':case "'":for(;++d<b.length&&(f=b.charAt(d))!=j;)f=="\\"&&d++;break;case "[":case "(":i++;break;case "]":case ")":i--;break;case ";":case ",":i==0&&i--;break}}h+=pv.parse(b.substring(g,--d))+
     6";}";g=d}c.lastIndex=d}h+=b.substring(g);return h}}pv.css=function(b,c){return window.getComputedStyle?window.getComputedStyle(b,null).getPropertyValue(c):b.currentStyle[c]};pv.error=function(b){typeof console=="undefined"?alert(b):console.error(b)};pv.listen=function(b,c,d){d=pv.listener(d);return b.addEventListener?b.addEventListener(c,d,false):b.attachEvent("on"+c,d)};pv.listener=function(b){return b.$listener||(b.$listener=function(c){try{pv.event=c;return b.call(this,c)}finally{delete pv.event}})};
     7pv.ancestor=function(b,c){for(;c;){if(c==b)return true;c=c.parentNode}return false};pv.id=function(){var b=1;return function(){return b++}}();pv.functor=function(b){return typeof b=="function"?b:function(){return b}};pv.listen(window,"load",function(){for(pv.$={i:0,x:document.getElementsByTagName("script")};pv.$.i<pv.$.x.length;pv.$.i++){pv.$.s=pv.$.x[pv.$.i];if(pv.$.s.type=="text/javascript+protovis")try{window.eval(pv.parse(pv.$.s.text))}catch(b){pv.error(b)}}delete pv.$});pv.Format={};
     8pv.Format.re=function(b){return b.replace(/[\\\^\$\*\+\?\[\]\(\)\.\{\}]/g,"\\$&")};pv.Format.pad=function(b,c,d){c=c-String(d).length;return c<1?d:(new Array(c+1)).join(b)+d};
     9pv.Format.date=function(b){function c(f){return b.replace(/%[a-zA-Z0-9]/g,function(g){switch(g){case "%a":return["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][f.getDay()];case "%A":return["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"][f.getDay()];case "%h":case "%b":return["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][f.getMonth()];case "%B":return["January","February","March","April","May","June","July","August","September","October","November","December"][f.getMonth()];
     10case "%c":return f.toLocaleString();case "%C":return d("0",2,Math.floor(f.getFullYear()/100)%100);case "%d":return d("0",2,f.getDate());case "%x":case "%D":return d("0",2,f.getMonth()+1)+"/"+d("0",2,f.getDate())+"/"+d("0",2,f.getFullYear()%100);case "%e":return d(" ",2,f.getDate());case "%H":return d("0",2,f.getHours());case "%I":return(g=f.getHours()%12)?d("0",2,g):12;case "%m":return d("0",2,f.getMonth()+1);case "%M":return d("0",2,f.getMinutes());case "%n":return"\n";case "%p":return f.getHours()<
     1112?"AM":"PM";case "%T":case "%X":case "%r":g=f.getHours()%12;return(g?d("0",2,g):12)+":"+d("0",2,f.getMinutes())+":"+d("0",2,f.getSeconds())+" "+(f.getHours()<12?"AM":"PM");case "%R":return d("0",2,f.getHours())+":"+d("0",2,f.getMinutes());case "%S":return d("0",2,f.getSeconds());case "%Q":return d("0",3,f.getMilliseconds());case "%t":return"\t";case "%u":return(g=f.getDay())?g:1;case "%w":return f.getDay();case "%y":return d("0",2,f.getFullYear()%100);case "%Y":return f.getFullYear();case "%%":return"%"}return g})}
     12var d=pv.Format.pad;c.format=c;c.parse=function(f){var g=1970,h=0,i=1,j=0,l=0,k=0,q=[function(){}],o=pv.Format.re(b).replace(/%[a-zA-Z0-9]/g,function(n){switch(n){case "%b":q.push(function(m){h={Jan:0,Feb:1,Mar:2,Apr:3,May:4,Jun:5,Jul:6,Aug:7,Sep:8,Oct:9,Nov:10,Dec:11}[m]});return"([A-Za-z]+)";case "%h":case "%B":q.push(function(m){h={January:0,February:1,March:2,April:3,May:4,June:5,July:6,August:7,September:8,October:9,November:10,December:11}[m]});return"([A-Za-z]+)";case "%e":case "%d":q.push(function(m){i=
     13m});return"([0-9]+)";case "%I":case "%H":q.push(function(m){j=m});return"([0-9]+)";case "%m":q.push(function(m){h=m-1});return"([0-9]+)";case "%M":q.push(function(m){l=m});return"([0-9]+)";case "%p":q.push(function(m){if(j==12){if(m=="am")j=0}else if(m=="pm")j=Number(j)+12});return"(am|pm)";case "%S":q.push(function(m){k=m});return"([0-9]+)";case "%y":q.push(function(m){m=Number(m);g=m+(0<=m&&m<69?2E3:m>=69&&m<100?1900:0)});return"([0-9]+)";case "%Y":q.push(function(m){g=m});return"([0-9]+)";case "%%":q.push(function(){});
     14return"%"}return n});(f=f.match(o))&&f.forEach(function(n,m){q[m](n)});return new Date(g,h,i,j,l,k)};return c};
     15pv.Format.time=function(b){function c(f){f=Number(f);switch(b){case "short":if(f>=31536E6)return(f/31536E6).toFixed(1)+" years";else if(f>=6048E5)return(f/6048E5).toFixed(1)+" weeks";else if(f>=864E5)return(f/864E5).toFixed(1)+" days";else if(f>=36E5)return(f/36E5).toFixed(1)+" hours";else if(f>=6E4)return(f/6E4).toFixed(1)+" minutes";return(f/1E3).toFixed(1)+" seconds";case "long":var g=[],h=f%36E5/6E4>>0;g.push(d("0",2,f%6E4/1E3>>0));if(f>=36E5){var i=f%864E5/36E5>>0;g.push(d("0",2,h));if(f>=864E5){g.push(d("0",
     162,i));g.push(Math.floor(f/864E5).toFixed())}else g.push(i.toFixed())}else g.push(h.toFixed());return g.reverse().join(":")}}var d=pv.Format.pad;c.format=c;c.parse=function(f){switch(b){case "short":for(var g=/([0-9,.]+)\s*([a-z]+)/g,h,i=0;h=g.exec(f);){var j=parseFloat(h[0].replace(",","")),l=0;switch(h[2].toLowerCase()){case "year":case "years":l=31536E6;break;case "week":case "weeks":l=6048E5;break;case "day":case "days":l=864E5;break;case "hour":case "hours":l=36E5;break;case "minute":case "minutes":l=
     176E4;break;case "second":case "seconds":l=1E3;break}i+=j*l}return i;case "long":h=f.replace(",","").split(":").reverse();i=0;if(h.length)i+=parseFloat(h[0])*1E3;if(h.length>1)i+=parseFloat(h[1])*6E4;if(h.length>2)i+=parseFloat(h[2])*36E5;if(h.length>3)i+=parseFloat(h[3])*864E5;return i}};return c};
     18pv.Format.number=function(){function b(n){if(Infinity>h)n=Math.round(n*i)/i;var m=String(Math.abs(n)).split("."),r=m[0];n=n<0?"-":"";if(r.length>d)r=r.substring(r.length-d);if(k&&r.length<c)r=n+(new Array(c-r.length+1)).join(j)+r;if(r.length>3)r=r.replace(/\B(?=(?:\d{3})+(?!\d))/g,o);if(!k&&r.length<f)r=(new Array(f-r.length+1)).join(j)+n+r;m[0]=r;r=m[1]||"";if(r.length<g)m[1]=r+(new Array(g-r.length+1)).join(l);return m.join(q)}var c=0,d=Infinity,f=0,g=0,h=0,i=1,j="0",l="0",k=true,q=".",o=",";b.format=
     19b;b.parse=function(n){var m=pv.Format.re;n=String(n).replace(new RegExp("^("+m(j)+")*"),"").replace(new RegExp("("+m(l)+")*$"),"").split(q);m=n[0].replace(new RegExp(m(o),"g"),"");if(m.length>d)m=m.substring(m.length-d);n=n[1]?Number("0."+n[1]):0;if(Infinity>h)n=Math.round(n*i)/i;return Math.round(m)+n};b.integerDigits=function(n,m){if(arguments.length){c=Number(n);d=arguments.length>1?Number(m):c;f=c+Math.floor(c/3)*o.length;return this}return[c,d]};b.fractionDigits=function(n,m){if(arguments.length){g=
     20Number(n);h=arguments.length>1?Number(m):g;i=Math.pow(10,h);return this}return[g,h]};b.integerPad=function(n){if(arguments.length){j=String(n);k=/\d/.test(j);return this}return j};b.fractionPad=function(n){if(arguments.length){l=String(n);return this}return l};b.decimal=function(n){if(arguments.length){q=String(n);return this}return q};b.group=function(n){if(arguments.length){o=n?String(n):"";f=c+Math.floor(c/3)*o.length;return this}return o};return b};
     21pv.map=function(b,c){var d={};return c?b.map(function(f,g){d.index=g;return c.call(d,f)}):b.slice()};pv.repeat=function(b,c){if(arguments.length==1)c=2;return pv.blend(pv.range(c).map(function(){return b}))};pv.cross=function(b,c){for(var d=[],f=0,g=b.length,h=c.length;f<g;f++)for(var i=0,j=b[f];i<h;i++)d.push([j,c[i]]);return d};pv.blend=function(b){return Array.prototype.concat.apply([],b)};
     22pv.transpose=function(b){var c=b.length,d=pv.max(b,function(i){return i.length});if(d>c){b.length=d;for(var f=c;f<d;f++)b[f]=new Array(c);for(f=0;f<c;f++)for(var g=f+1;g<d;g++){var h=b[f][g];b[f][g]=b[g][f];b[g][f]=h}}else{for(f=0;f<d;f++)b[f].length=c;for(f=0;f<c;f++)for(g=0;g<f;g++){h=b[f][g];b[f][g]=b[g][f];b[g][f]=h}}b.length=d;for(f=0;f<d;f++)b[f].length=c;return b};pv.normalize=function(b,c){b=pv.map(b,c);c=pv.sum(b);for(var d=0;d<b.length;d++)b[d]/=c;return b};
     23pv.permute=function(b,c,d){if(!d)d=pv.identity;var f=new Array(c.length),g={};c.forEach(function(h,i){g.index=h;f[i]=d.call(g,b[h])});return f};pv.numerate=function(b,c){if(!c)c=pv.identity;var d={},f={};b.forEach(function(g,h){f.index=h;d[c.call(f,g)]=h});return d};pv.uniq=function(b,c){if(!c)c=pv.identity;var d={},f=[],g={},h;b.forEach(function(i,j){g.index=j;h=c.call(g,i);h in d||(d[h]=f.push(h))});return f};pv.naturalOrder=function(b,c){return b<c?-1:b>c?1:0};
     24pv.reverseOrder=function(b,c){return c<b?-1:c>b?1:0};pv.search=function(b,c,d){if(!d)d=pv.identity;for(var f=0,g=b.length-1;f<=g;){var h=f+g>>1,i=d(b[h]);if(i<c)f=h+1;else if(i>c)g=h-1;else return h}return-f-1};pv.search.index=function(b,c,d){b=pv.search(b,c,d);return b<0?-b-1:b};
     25pv.range=function(b,c,d){if(arguments.length==1){c=b;b=0}if(d==undefined)d=1;if((c-b)/d==Infinity)throw new Error("range must be finite");var f=[],g=0,h;if(d<0)for(;(h=b+d*g++)>c;)f.push(h);else for(;(h=b+d*g++)<c;)f.push(h);return f};pv.random=function(b,c,d){if(arguments.length==1){c=b;b=0}if(d==undefined)d=1;return d?Math.floor(Math.random()*(c-b)/d)*d+b:Math.random()*(c-b)+b};
     26pv.sum=function(b,c){var d={};return b.reduce(c?function(f,g,h){d.index=h;return f+c.call(d,g)}:function(f,g){return f+g},0)};pv.max=function(b,c){if(c==pv.index)return b.length-1;return Math.max.apply(null,c?pv.map(b,c):b)};pv.max.index=function(b,c){if(!b.length)return-1;if(c==pv.index)return b.length-1;if(!c)c=pv.identity;for(var d=0,f=-Infinity,g={},h=0;h<b.length;h++){g.index=h;var i=c.call(g,b[h]);if(i>f){f=i;d=h}}return d};
     27pv.min=function(b,c){if(c==pv.index)return 0;return Math.min.apply(null,c?pv.map(b,c):b)};pv.min.index=function(b,c){if(!b.length)return-1;if(c==pv.index)return 0;if(!c)c=pv.identity;for(var d=0,f=Infinity,g={},h=0;h<b.length;h++){g.index=h;var i=c.call(g,b[h]);if(i<f){f=i;d=h}}return d};pv.mean=function(b,c){return pv.sum(b,c)/b.length};
     28pv.median=function(b,c){if(c==pv.index)return(b.length-1)/2;b=pv.map(b,c).sort(pv.naturalOrder);if(b.length%2)return b[Math.floor(b.length/2)];c=b.length/2;return(b[c-1]+b[c])/2};pv.variance=function(b,c){if(b.length<1)return NaN;if(b.length==1)return 0;var d=pv.mean(b,c),f=0,g={};if(!c)c=pv.identity;for(var h=0;h<b.length;h++){g.index=h;var i=c.call(g,b[h])-d;f+=i*i}return f};pv.deviation=function(b,c){return Math.sqrt(pv.variance(b,c)/(b.length-1))};pv.log=function(b,c){return Math.log(b)/Math.log(c)};
     29pv.logSymmetric=function(b,c){return b==0?0:b<0?-pv.log(-b,c):pv.log(b,c)};pv.logAdjusted=function(b,c){if(!isFinite(b))return b;var d=b<0;if(b<c)b+=(c-b)/c;return d?-pv.log(b,c):pv.log(b,c)};pv.logFloor=function(b,c){return b>0?Math.pow(c,Math.floor(pv.log(b,c))):-Math.pow(c,-Math.floor(-pv.log(-b,c)))};pv.logCeil=function(b,c){return b>0?Math.pow(c,Math.ceil(pv.log(b,c))):-Math.pow(c,-Math.ceil(-pv.log(-b,c)))};
     30(function(){var b=Math.PI/180,c=180/Math.PI;pv.radians=function(d){return b*d};pv.degrees=function(d){return c*d}})();pv.keys=function(b){var c=[];for(var d in b)c.push(d);return c};pv.entries=function(b){var c=[];for(var d in b)c.push({key:d,value:b[d]});return c};pv.values=function(b){var c=[];for(var d in b)c.push(b[d]);return c};pv.dict=function(b,c){for(var d={},f={},g=0;g<b.length;g++)if(g in b){var h=b[g];f.index=g;d[h]=c.call(f,h)}return d};pv.dom=function(b){return new pv.Dom(b)};
     31pv.Dom=function(b){this.$map=b};pv.Dom.prototype.$leaf=function(b){return typeof b!="object"};pv.Dom.prototype.leaf=function(b){if(arguments.length){this.$leaf=b;return this}return this.$leaf};pv.Dom.prototype.root=function(b){function c(g){var h=new pv.Dom.Node;for(var i in g){var j=g[i];h.appendChild(d(j)?new pv.Dom.Node(j):c(j)).nodeName=i}return h}var d=this.$leaf,f=c(this.$map);f.nodeName=b;return f};pv.Dom.prototype.nodes=function(){return this.root().nodes()};
     32pv.Dom.Node=function(b){this.nodeValue=b;this.childNodes=[]};a=pv.Dom.Node.prototype;a.parentNode=null;a.firstChild=null;a.lastChild=null;a.previousSibling=null;a.nextSibling=null;
     33a.removeChild=function(b){var c=this.childNodes.indexOf(b);if(c==-1)throw new Error("child not found");this.childNodes.splice(c,1);if(b.previousSibling)b.previousSibling.nextSibling=b.nextSibling;else this.firstChild=b.nextSibling;if(b.nextSibling)b.nextSibling.previousSibling=b.previousSibling;else this.lastChild=b.previousSibling;delete b.nextSibling;delete b.previousSibling;delete b.parentNode;return b};
     34a.appendChild=function(b){b.parentNode&&b.parentNode.removeChild(b);b.parentNode=this;if(b.previousSibling=this.lastChild)this.lastChild.nextSibling=b;else this.firstChild=b;this.lastChild=b;this.childNodes.push(b);return b};
     35a.insertBefore=function(b,c){if(!c)return this.appendChild(b);var d=this.childNodes.indexOf(c);if(d==-1)throw new Error("child not found");b.parentNode&&b.parentNode.removeChild(b);b.parentNode=this;b.nextSibling=c;if(b.previousSibling=c.previousSibling)c.previousSibling.nextSibling=b;else{if(c==this.lastChild)this.lastChild=b;this.firstChild=b}this.childNodes.splice(d,0,b);return b};
     36a.replaceChild=function(b,c){var d=this.childNodes.indexOf(c);if(d==-1)throw new Error("child not found");b.parentNode&&b.parentNode.removeChild(b);b.parentNode=this;b.nextSibling=c.nextSibling;if(b.previousSibling=c.previousSibling)c.previousSibling.nextSibling=b;else this.firstChild=b;if(c.nextSibling)c.nextSibling.previousSibling=b;else this.lastChild=b;this.childNodes[d]=b;return c};a.visitBefore=function(b){function c(d,f){b(d,f);for(d=d.firstChild;d;d=d.nextSibling)c(d,f+1)}c(this,0)};
     37a.visitAfter=function(b){function c(d,f){for(var g=d.firstChild;g;g=g.nextSibling)c(g,f+1);b(d,f)}c(this,0)};a.sort=function(b){if(this.firstChild){this.childNodes.sort(b);var c=this.firstChild=this.childNodes[0],d;delete c.previousSibling;for(var f=1;f<this.childNodes.length;f++){c.sort(b);d=this.childNodes[f];d.previousSibling=c;c=c.nextSibling=d}this.lastChild=c;delete c.nextSibling;c.sort(b)}return this};
     38a.reverse=function(){var b=[];this.visitAfter(function(c){for(;c.lastChild;)b.push(c.removeChild(c.lastChild));for(var d;d=b.pop();)c.insertBefore(d,c.firstChild)});return this};a.nodes=function(){function b(d){c.push(d);d.childNodes.forEach(b)}var c=[];b(this,c);return c};
     39a.toggle=function(b){if(b)return this.toggled?this.visitBefore(function(d){d.toggled&&d.toggle()}):this.visitAfter(function(d){d.toggled||d.toggle()});b=this;if(b.toggled){for(var c;c=b.toggled.pop();)b.appendChild(c);delete b.toggled}else if(b.lastChild)for(b.toggled=[];b.lastChild;)b.toggled.push(b.removeChild(b.lastChild))};pv.nodes=function(b){for(var c=new pv.Dom.Node,d=0;d<b.length;d++)c.appendChild(new pv.Dom.Node(b[d]));return c.nodes()};pv.tree=function(b){return new pv.Tree(b)};
     40pv.Tree=function(b){this.array=b};pv.Tree.prototype.keys=function(b){this.k=b;return this};pv.Tree.prototype.value=function(b){this.v=b;return this};pv.Tree.prototype.map=function(){for(var b={},c={},d=0;d<this.array.length;d++){c.index=d;for(var f=this.array[d],g=this.k.call(c,f),h=b,i=0;i<g.length-1;i++)h=h[g[i]]||(h[g[i]]={});h[g[i]]=this.v?this.v.call(c,f):f}return b};pv.nest=function(b){return new pv.Nest(b)};pv.Nest=function(b){this.array=b;this.keys=[]};a=pv.Nest.prototype;
     41a.key=function(b){this.keys.push(b);return this};a.sortKeys=function(b){this.keys[this.keys.length-1].order=b||pv.naturalOrder;return this};a.sortValues=function(b){this.order=b||pv.naturalOrder;return this};a.map=function(){for(var b={},c=[],d,f=0;f<this.array.length;f++){var g=this.array[f],h=b;for(d=0;d<this.keys.length-1;d++){var i=this.keys[d](g);h[i]||(h[i]={});h=h[i]}i=this.keys[d](g);if(!h[i]){d=[];c.push(d);h[i]=d}h[i].push(g)}if(this.order)for(d=0;d<c.length;d++)c[d].sort(this.order);return b};
     42a.entries=function(){function b(d){var f=[];for(var g in d){var h=d[g];f.push({key:g,values:h instanceof Array?h:b(h)})}return f}function c(d,f){var g=this.keys[f].order;g&&d.sort(function(i,j){return g(i.key,j.key)});if(++f<this.keys.length)for(var h=0;h<d.length;h++)c.call(this,d[h].values,f);return d}return c.call(this,b(this.map()),0)};a.rollup=function(b){function c(d){for(var f in d){var g=d[f];if(g instanceof Array)d[f]=b(g);else c(g)}return d}return c(this.map())};pv.flatten=function(b){return new pv.Flatten(b)};
     43pv.Flatten=function(b){this.map=b;this.keys=[]};pv.Flatten.prototype.key=function(b,c){this.keys.push({name:b,value:c});delete this.$leaf;return this};pv.Flatten.prototype.leaf=function(b){this.keys.length=0;this.$leaf=b;return this};
     44pv.Flatten.prototype.array=function(){function b(i,j){if(j<f.length-1)for(var l in i){d.push(l);b(i[l],j+1);d.pop()}else c.push(d.concat(i))}var c=[],d=[],f=this.keys,g=this.$leaf;if(g){function h(i,j){if(g(i))c.push({keys:d.slice(),value:i});else for(var l in i){d.push(l);h(i[l],j+1);d.pop()}}h(this.map,0);return c}b(this.map,0);return c.map(function(i){for(var j={},l=0;l<f.length;l++){var k=f[l],q=i[l];j[k.name]=k.value?k.value.call(null,q):q}return j})};
     45pv.vector=function(b,c){return new pv.Vector(b,c)};pv.Vector=function(b,c){this.x=b;this.y=c};a=pv.Vector.prototype;a.perp=function(){return new pv.Vector(-this.y,this.x)};a.norm=function(){var b=this.length();return this.times(b?1/b:1)};a.length=function(){return Math.sqrt(this.x*this.x+this.y*this.y)};a.times=function(b){return new pv.Vector(this.x*b,this.y*b)};a.plus=function(b,c){return arguments.length==1?new pv.Vector(this.x+b.x,this.y+b.y):new pv.Vector(this.x+b,this.y+c)};
     46a.minus=function(b,c){return arguments.length==1?new pv.Vector(this.x-b.x,this.y-b.y):new pv.Vector(this.x-b,this.y-c)};a.dot=function(b,c){return arguments.length==1?this.x*b.x+this.y*b.y:this.x*b+this.y*c};pv.Transform=function(){};pv.Transform.prototype={k:1,x:0,y:0};pv.Transform.identity=new pv.Transform;pv.Transform.prototype.translate=function(b,c){var d=new pv.Transform;d.k=this.k;d.x=this.k*b+this.x;d.y=this.k*c+this.y;return d};
     47pv.Transform.prototype.scale=function(b){var c=new pv.Transform;c.k=this.k*b;c.x=this.x;c.y=this.y;return c};pv.Transform.prototype.invert=function(){var b=new pv.Transform,c=1/this.k;b.k=c;b.x=-this.x*c;b.y=-this.y*c;return b};pv.Transform.prototype.times=function(b){var c=new pv.Transform;c.k=this.k*b.k;c.x=this.k*b.x+this.x;c.y=this.k*b.y+this.y;return c};pv.Scale=function(){};
     48pv.Scale.interpolator=function(b,c){if(typeof b=="number")return function(d){return d*(c-b)+b};b=pv.color(b).rgb();c=pv.color(c).rgb();return function(d){var f=b.a*(1-d)+c.a*d;if(f<1.0E-5)f=0;return b.a==0?pv.rgb(c.r,c.g,c.b,f):c.a==0?pv.rgb(b.r,b.g,b.b,f):pv.rgb(Math.round(b.r*(1-d)+c.r*d),Math.round(b.g*(1-d)+c.g*d),Math.round(b.b*(1-d)+c.b*d),f)}};
     49pv.Scale.quantitative=function(){function b(o){return new Date(o)}function c(o){var n=pv.search(d,o);if(n<0)n=-n-2;n=Math.max(0,Math.min(h.length-1,n));return h[n]((l(o)-f[n])/(f[n+1]-f[n]))}var d=[0,1],f=[0,1],g=[0,1],h=[pv.identity],i=Number,j=false,l=pv.identity,k=pv.identity,q=String;c.transform=function(o,n){l=function(m){return j?-o(-m):o(m)};k=function(m){return j?-n(-m):n(m)};f=d.map(l);return this};c.domain=function(o,n,m){if(arguments.length){var r;if(o instanceof Array){if(arguments.length<
     502)n=pv.identity;if(arguments.length<3)m=n;r=o.length&&n(o[0]);d=o.length?[pv.min(o,n),pv.max(o,m)]:[]}else{r=o;d=Array.prototype.slice.call(arguments).map(Number)}if(d.length){if(d.length==1)d=[d[0],d[0]]}else d=[-Infinity,Infinity];j=(d[0]||d[d.length-1])<0;f=d.map(l);i=r instanceof Date?b:Number;return this}return d.map(i)};c.range=function(){if(arguments.length){g=Array.prototype.slice.call(arguments);if(g.length){if(g.length==1)g=[g[0],g[0]]}else g=[-Infinity,Infinity];h=[];for(var o=0;o<g.length-
     511;o++)h.push(pv.Scale.interpolator(g[o],g[o+1]));return this}return g};c.invert=function(o){var n=pv.search(g,o);if(n<0)n=-n-2;n=Math.max(0,Math.min(h.length-1,n));return i(k(f[n]+(o-g[n])/(g[n+1]-g[n])*(f[n+1]-f[n])))};c.ticks=function(o){var n=d[0],m=d[d.length-1],r=m<n,s=r?m:n;m=r?n:m;var u=m-s;if(!u||!isFinite(u)){if(i==b)q=pv.Format.date("%x");return[i(s)]}if(i==b){function x(w,y){switch(y){case 31536E6:w.setMonth(0);case 2592E6:w.setDate(1);case 6048E5:y==6048E5&&w.setDate(w.getDate()-w.getDay());
     52case 864E5:w.setHours(0);case 36E5:w.setMinutes(0);case 6E4:w.setSeconds(0);case 1E3:w.setMilliseconds(0)}}var t,p,v=1;if(u>=94608E6){n=31536E6;t="%Y";p=function(w){w.setFullYear(w.getFullYear()+v)}}else if(u>=7776E6){n=2592E6;t="%m/%Y";p=function(w){w.setMonth(w.getMonth()+v)}}else if(u>=18144E5){n=6048E5;t="%m/%d";p=function(w){w.setDate(w.getDate()+7*v)}}else if(u>=2592E5){n=864E5;t="%m/%d";p=function(w){w.setDate(w.getDate()+v)}}else if(u>=108E5){n=36E5;t="%I:%M %p";p=function(w){w.setHours(w.getHours()+
     53v)}}else if(u>=18E4){n=6E4;t="%I:%M %p";p=function(w){w.setMinutes(w.getMinutes()+v)}}else if(u>=3E3){n=1E3;t="%I:%M:%S";p=function(w){w.setSeconds(w.getSeconds()+v)}}else{n=1;t="%S.%Qs";p=function(w){w.setTime(w.getTime()+v)}}q=pv.Format.date(t);s=new Date(s);t=[];x(s,n);u=u/n;if(u>10)switch(n){case 36E5:v=u>20?6:3;s.setHours(Math.floor(s.getHours()/v)*v);break;case 2592E6:v=3;s.setMonth(Math.floor(s.getMonth()/v)*v);break;case 6E4:v=u>30?15:u>15?10:5;s.setMinutes(Math.floor(s.getMinutes()/v)*v);
     54break;case 1E3:v=u>90?15:u>60?10:5;s.setSeconds(Math.floor(s.getSeconds()/v)*v);break;case 1:v=u>1E3?250:u>200?100:u>100?50:u>50?25:5;s.setMilliseconds(Math.floor(s.getMilliseconds()/v)*v);break;default:v=pv.logCeil(u/15,10);if(u/v<2)v/=5;else if(u/v<5)v/=2;s.setFullYear(Math.floor(s.getFullYear()/v)*v);break}for(;;){p(s);if(s>m)break;t.push(new Date(s))}return r?t.reverse():t}arguments.length||(o=10);v=pv.logFloor(u/o,10);n=o/(u/v);if(n<=0.15)v*=10;else if(n<=0.35)v*=5;else if(n<=0.75)v*=2;n=Math.ceil(s/
     55v)*v;m=Math.floor(m/v)*v;q=pv.Format.number().fractionDigits(Math.max(0,-Math.floor(pv.log(v,10)+0.01)));m=pv.range(n,m+v,v);return r?m.reverse():m};c.tickFormat=function(o){return q(o)};c.nice=function(){if(d.length!=2)return this;var o=d[0],n=d[d.length-1],m=n<o,r=m?n:o;o=m?o:n;n=o-r;if(!n||!isFinite(n))return this;n=Math.pow(10,Math.round(Math.log(n)/Math.log(10))-1);d=[Math.floor(r/n)*n,Math.ceil(o/n)*n];m&&d.reverse();f=d.map(l);return this};c.by=function(o){function n(){return c(o.apply(this,
     56arguments))}for(var m in c)n[m]=c[m];return n};c.domain.apply(c,arguments);return c};pv.Scale.linear=function(){var b=pv.Scale.quantitative();b.domain.apply(b,arguments);return b};
     57pv.Scale.log=function(){var b=pv.Scale.quantitative(1,10),c,d,f=function(h){return Math.log(h)/d},g=function(h){return Math.pow(c,h)};b.ticks=function(){var h=b.domain(),i=h[0]<0,j=Math.floor(i?-f(-h[0]):f(h[0])),l=Math.ceil(i?-f(-h[1]):f(h[1])),k=[];if(i)for(k.push(-g(-j));j++<l;)for(i=c-1;i>0;i--)k.push(-g(-j)*i);else{for(;j<l;j++)for(i=1;i<c;i++)k.push(g(j)*i);k.push(g(j))}for(j=0;k[j]<h[0];j++);for(l=k.length;k[l-1]>h[1];l--);return k.slice(j,l)};b.tickFormat=function(h){return h.toPrecision(1)};
     58b.nice=function(){var h=b.domain();return b.domain(pv.logFloor(h[0],c),pv.logCeil(h[1],c))};b.base=function(h){if(arguments.length){c=Number(h);d=Math.log(c);b.transform(f,g);return this}return c};b.domain.apply(b,arguments);return b.base(10)};pv.Scale.root=function(){var b=pv.Scale.quantitative();b.power=function(c){if(arguments.length){var d=Number(c),f=1/d;b.transform(function(g){return Math.pow(g,f)},function(g){return Math.pow(g,d)});return this}return d};b.domain.apply(b,arguments);return b.power(2)};
     59pv.Scale.ordinal=function(){function b(g){g in d||(d[g]=c.push(g)-1);return f[d[g]%f.length]}var c=[],d={},f=[];b.domain=function(g,h){if(arguments.length){g=g instanceof Array?arguments.length>1?pv.map(g,h):g:Array.prototype.slice.call(arguments);c=[];for(var i={},j=0;j<g.length;j++){var l=g[j];if(!(l in i)){i[l]=true;c.push(l)}}d=pv.numerate(c);return this}return c};b.range=function(g,h){if(arguments.length){f=g instanceof Array?arguments.length>1?pv.map(g,h):g:Array.prototype.slice.call(arguments);
     60if(typeof f[0]=="string")f=f.map(pv.color);return this}return f};b.split=function(g,h){var i=(h-g)/this.domain().length;f=pv.range(g+i/2,h,i);return this};b.splitFlush=function(g,h){var i=this.domain().length,j=(h-g)/(i-1);f=i==1?[(g+h)/2]:pv.range(g,h+j/2,j);return this};b.splitBanded=function(g,h,i){if(arguments.length<3)i=1;if(i<0){var j=this.domain().length;j=(h-g- -i*j)/(j+1);f=pv.range(g+j,h,j-i);f.band=-i}else{j=(h-g)/(this.domain().length+(1-i));f=pv.range(g+j*(1-i),h,j);f.band=j*i}return this};
     61b.by=function(g){function h(){return b(g.apply(this,arguments))}for(var i in b)h[i]=b[i];return h};b.domain.apply(b,arguments);return b};
     62pv.Scale.quantile=function(){function b(i){return h(Math.max(0,Math.min(d,pv.search.index(f,i)-1))/d)}var c=-1,d=-1,f=[],g=[],h=pv.Scale.linear();b.quantiles=function(i){if(arguments.length){c=Number(i);if(c<0){f=[g[0]].concat(g);d=g.length-1}else{f=[];f[0]=g[0];for(var j=1;j<=c;j++)f[j]=g[~~(j*(g.length-1)/c)];d=c-1}return this}return f};b.domain=function(i,j){if(arguments.length){g=i instanceof Array?pv.map(i,j):Array.prototype.slice.call(arguments);g.sort(pv.naturalOrder);b.quantiles(c);return this}return g};
     63b.range=function(){if(arguments.length){h.range.apply(h,arguments);return this}return h.range()};b.by=function(i){function j(){return b(i.apply(this,arguments))}for(var l in b)j[l]=b[l];return j};b.domain.apply(b,arguments);return b};
     64pv.histogram=function(b,c){var d=true;return{bins:function(f){var g=pv.map(b,c),h=[];arguments.length||(f=pv.Scale.linear(g).ticks());for(var i=0;i<f.length-1;i++){var j=h[i]=[];j.x=f[i];j.dx=f[i+1]-f[i];j.y=0}for(i=0;i<g.length;i++){j=pv.search.index(f,g[i])-1;j=h[Math.max(0,Math.min(h.length-1,j))];j.y++;j.push(b[i])}if(!d)for(i=0;i<h.length;i++)h[i].y/=g.length;return h},frequency:function(f){if(arguments.length){d=Boolean(f);return this}return d}}};
     65pv.color=function(b){if(b.rgb)return b.rgb();var c=/([a-z]+)\((.*)\)/i.exec(b);if(c){var d=c[2].split(","),f=1;switch(c[1]){case "hsla":case "rgba":f=parseFloat(d[3]);if(!f)return pv.Color.transparent;break}switch(c[1]){case "hsla":case "hsl":b=parseFloat(d[0]);var g=parseFloat(d[1])/100;d=parseFloat(d[2])/100;return(new pv.Color.Hsl(b,g,d,f)).rgb();case "rgba":case "rgb":function h(l){var k=parseFloat(l);return l[l.length-1]=="%"?Math.round(k*2.55):k}g=h(d[0]);var i=h(d[1]),j=h(d[2]);return pv.rgb(g,
     66i,j,f)}}if(f=pv.Color.names[b])return f;if(b.charAt(0)=="#"){if(b.length==4){g=b.charAt(1);g+=g;i=b.charAt(2);i+=i;j=b.charAt(3);j+=j}else if(b.length==7){g=b.substring(1,3);i=b.substring(3,5);j=b.substring(5,7)}return pv.rgb(parseInt(g,16),parseInt(i,16),parseInt(j,16),1)}return new pv.Color(b,1)};pv.Color=function(b,c){this.color=b;this.opacity=c};pv.Color.prototype.brighter=function(b){return this.rgb().brighter(b)};pv.Color.prototype.darker=function(b){return this.rgb().darker(b)};
     67pv.rgb=function(b,c,d,f){return new pv.Color.Rgb(b,c,d,arguments.length==4?f:1)};pv.Color.Rgb=function(b,c,d,f){pv.Color.call(this,f?"rgb("+b+","+c+","+d+")":"none",f);this.r=b;this.g=c;this.b=d;this.a=f};pv.Color.Rgb.prototype=pv.extend(pv.Color);a=pv.Color.Rgb.prototype;a.red=function(b){return pv.rgb(b,this.g,this.b,this.a)};a.green=function(b){return pv.rgb(this.r,b,this.b,this.a)};a.blue=function(b){return pv.rgb(this.r,this.g,b,this.a)};
     68a.alpha=function(b){return pv.rgb(this.r,this.g,this.b,b)};a.rgb=function(){return this};a.brighter=function(b){b=Math.pow(0.7,arguments.length?b:1);var c=this.r,d=this.g,f=this.b;if(!c&&!d&&!f)return pv.rgb(30,30,30,this.a);if(c&&c<30)c=30;if(d&&d<30)d=30;if(f&&f<30)f=30;return pv.rgb(Math.min(255,Math.floor(c/b)),Math.min(255,Math.floor(d/b)),Math.min(255,Math.floor(f/b)),this.a)};
     69a.darker=function(b){b=Math.pow(0.7,arguments.length?b:1);return pv.rgb(Math.max(0,Math.floor(b*this.r)),Math.max(0,Math.floor(b*this.g)),Math.max(0,Math.floor(b*this.b)),this.a)};pv.hsl=function(b,c,d,f){return new pv.Color.Hsl(b,c,d,arguments.length==4?f:1)};pv.Color.Hsl=function(b,c,d,f){pv.Color.call(this,"hsl("+b+","+c*100+"%,"+d*100+"%)",f);this.h=b;this.s=c;this.l=d;this.a=f};pv.Color.Hsl.prototype=pv.extend(pv.Color);a=pv.Color.Hsl.prototype;
     70a.hue=function(b){return pv.hsl(b,this.s,this.l,this.a)};a.saturation=function(b){return pv.hsl(this.h,b,this.l,this.a)};a.lightness=function(b){return pv.hsl(this.h,this.s,b,this.a)};a.alpha=function(b){return pv.hsl(this.h,this.s,this.l,b)};
     71a.rgb=function(){function b(j){if(j>360)j-=360;else if(j<0)j+=360;if(j<60)return i+(h-i)*j/60;if(j<180)return h;if(j<240)return i+(h-i)*(240-j)/60;return i}function c(j){return Math.round(b(j)*255)}var d=this.h,f=this.s,g=this.l;d%=360;if(d<0)d+=360;f=Math.max(0,Math.min(f,1));g=Math.max(0,Math.min(g,1));var h=g<=0.5?g*(1+f):g+f-g*f,i=2*g-h;return pv.rgb(c(d+120),c(d),c(d-120),this.a)};
     72pv.Color.names={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",
     73darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",
     74ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",
     75lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",
     76moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",
     77seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32",transparent:pv.Color.transparent=pv.rgb(0,0,0,0)};(function(){var b=pv.Color.names;for(var c in b)b[c]=pv.color(b[c])})();
     78pv.colors=function(){var b=pv.Scale.ordinal();b.range.apply(b,arguments);return b};pv.Colors={};pv.Colors.category10=function(){var b=pv.colors("#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf");b.domain.apply(b,arguments);return b};
     79pv.Colors.category20=function(){var b=pv.colors("#1f77b4","#aec7e8","#ff7f0e","#ffbb78","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5","#8c564b","#c49c94","#e377c2","#f7b6d2","#7f7f7f","#c7c7c7","#bcbd22","#dbdb8d","#17becf","#9edae5");b.domain.apply(b,arguments);return b};
     80pv.Colors.category19=function(){var b=pv.colors("#9c9ede","#7375b5","#4a5584","#cedb9c","#b5cf6b","#8ca252","#637939","#e7cb94","#e7ba52","#bd9e39","#8c6d31","#e7969c","#d6616b","#ad494a","#843c39","#de9ed6","#ce6dbd","#a55194","#7b4173");b.domain.apply(b,arguments);return b};pv.ramp=function(){var b=pv.Scale.linear();b.range.apply(b,arguments);return b};
     81pv.Scene=pv.SvgScene={svg:"http://www.w3.org/2000/svg",xmlns:"http://www.w3.org/2000/xmlns",xlink:"http://www.w3.org/1999/xlink",xhtml:"http://www.w3.org/1999/xhtml",scale:1,events:["DOMMouseScroll","mousewheel","mousedown","mouseup","mouseover","mouseout","mousemove","click","dblclick"],implicit:{svg:{"shape-rendering":"auto","pointer-events":"painted",x:0,y:0,dy:0,"text-anchor":"start",transform:"translate(0,0)",fill:"none","fill-opacity":1,stroke:"none","stroke-opacity":1,"stroke-width":1.5,"stroke-linejoin":"miter"},
     82css:{font:"10px sans-serif"}}};pv.SvgScene.updateAll=function(b){if(b.length&&b[0].reverse&&b.type!="line"&&b.type!="area"){for(var c=pv.extend(b),d=0,f=b.length-1;f>=0;d++,f--)c[d]=b[f];b=c}this.removeSiblings(this[b.type](b))};pv.SvgScene.create=function(b){return document.createElementNS(this.svg,b)};
     83pv.SvgScene.expect=function(b,c,d,f){if(b){if(b.tagName=="a")b=b.firstChild;if(b.tagName!=c){c=this.create(c);b.parentNode.replaceChild(c,b);b=c}}else b=this.create(c);for(var g in d){c=d[g];if(c==this.implicit.svg[g])c=null;c==null?b.removeAttribute(g):b.setAttribute(g,c)}for(g in f){c=f[g];if(c==this.implicit.css[g])c=null;if(c==null)b.style.removeProperty(g);else b.style[g]=c}return b};
     84pv.SvgScene.append=function(b,c,d){b.$scene={scenes:c,index:d};b=this.title(b,c[d]);b.parentNode||c.$g.appendChild(b);return b.nextSibling};pv.SvgScene.title=function(b,c){var d=b.parentNode;if(d&&d.tagName!="a")d=null;if(c.title){if(!d){d=this.create("a");b.parentNode&&b.parentNode.replaceChild(d,b);d.appendChild(b)}d.setAttributeNS(this.xlink,"title",c.title);return d}d&&d.parentNode.replaceChild(b,d);return b};
     85pv.SvgScene.dispatch=pv.listener(function(b){var c=b.target.$scene;if(c){var d=b.type;switch(d){case "DOMMouseScroll":d="mousewheel";b.wheel=-480*b.detail;break;case "mousewheel":b.wheel=(window.opera?12:1)*b.wheelDelta;break}pv.Mark.dispatch(d,c.scenes,c.index)&&b.preventDefault()}});pv.SvgScene.removeSiblings=function(b){for(;b;){var c=b.nextSibling;b.parentNode.removeChild(b);b=c}};pv.SvgScene.undefined=function(){};
     86pv.SvgScene.pathBasis=function(){function b(f,g,h,i,j){return{x:f[0]*g.left+f[1]*h.left+f[2]*i.left+f[3]*j.left,y:f[0]*g.top+f[1]*h.top+f[2]*i.top+f[3]*j.top}}var c=[[1/6,2/3,1/6,0],[0,2/3,1/3,0],[0,1/3,2/3,0],[0,1/6,2/3,1/6]],d=function(f,g,h,i){var j=b(c[1],f,g,h,i),l=b(c[2],f,g,h,i);f=b(c[3],f,g,h,i);return"C"+j.x+","+j.y+","+l.x+","+l.y+","+f.x+","+f.y};d.segment=function(f,g,h,i){var j=b(c[0],f,g,h,i),l=b(c[1],f,g,h,i),k=b(c[2],f,g,h,i);f=b(c[3],f,g,h,i);return"M"+j.x+","+j.y+"C"+l.x+","+l.y+
     87","+k.x+","+k.y+","+f.x+","+f.y};return d}();pv.SvgScene.curveBasis=function(b){if(b.length<=2)return"";var c="",d=b[0],f=d,g=d,h=b[1];c+=this.pathBasis(d,f,g,h);for(var i=2;i<b.length;i++){d=f;f=g;g=h;h=b[i];c+=this.pathBasis(d,f,g,h)}c+=this.pathBasis(f,g,h,h);c+=this.pathBasis(g,h,h,h);return c};
     88pv.SvgScene.curveBasisSegments=function(b){if(b.length<=2)return"";var c=[],d=b[0],f=d,g=d,h=b[1],i=this.pathBasis.segment(d,f,g,h);d=f;f=g;g=h;h=b[2];c.push(i+this.pathBasis(d,f,g,h));for(i=3;i<b.length;i++){d=f;f=g;g=h;h=b[i];c.push(this.pathBasis.segment(d,f,g,h))}c.push(this.pathBasis.segment(f,g,h,h)+this.pathBasis(g,h,h,h));return c};
     89pv.SvgScene.curveHermite=function(b,c){if(c.length<1||b.length!=c.length&&b.length!=c.length+2)return"";var d=b.length!=c.length,f="",g=b[0],h=b[1],i=c[0],j=i,l=1;if(d){f+="Q"+(h.left-i.x*2/3)+","+(h.top-i.y*2/3)+","+h.left+","+h.top;g=b[1];l=2}if(c.length>1){j=c[1];h=b[l];l++;f+="C"+(g.left+i.x)+","+(g.top+i.y)+","+(h.left-j.x)+","+(h.top-j.y)+","+h.left+","+h.top;for(g=2;g<c.length;g++,l++){h=b[l];j=c[g];f+="S"+(h.left-j.x)+","+(h.top-j.y)+","+h.left+","+h.top}}if(d){b=b[l];f+="Q"+(h.left+j.x*2/
     903)+","+(h.top+j.y*2/3)+","+b.left+","+b.top}return f};
     91pv.SvgScene.curveHermiteSegments=function(b,c){if(c.length<1||b.length!=c.length&&b.length!=c.length+2)return[];var d=b.length!=c.length,f=[],g=b[0],h=g,i=c[0],j=i,l=1;if(d){h=b[1];f.push("M"+g.left+","+g.top+"Q"+(h.left-j.x*2/3)+","+(h.top-j.y*2/3)+","+h.left+","+h.top);l=2}for(var k=1;k<c.length;k++,l++){g=h;i=j;h=b[l];j=c[k];f.push("M"+g.left+","+g.top+"C"+(g.left+i.x)+","+(g.top+i.y)+","+(h.left-j.x)+","+(h.top-j.y)+","+h.left+","+h.top)}if(d){b=b[l];f.push("M"+h.left+","+h.top+"Q"+(h.left+j.x*
     922/3)+","+(h.top+j.y*2/3)+","+b.left+","+b.top)}return f};pv.SvgScene.cardinalTangents=function(b,c){var d=[];c=(1-c)/2;for(var f=b[0],g=b[1],h=b[2],i=3;i<b.length;i++){d.push({x:c*(h.left-f.left),y:c*(h.top-f.top)});f=g;g=h;h=b[i]}d.push({x:c*(h.left-f.left),y:c*(h.top-f.top)});return d};pv.SvgScene.curveCardinal=function(b,c){if(b.length<=2)return"";return this.curveHermite(b,this.cardinalTangents(b,c))};
     93pv.SvgScene.curveCardinalSegments=function(b,c){if(b.length<=2)return"";return this.curveHermiteSegments(b,this.cardinalTangents(b,c))};
     94pv.SvgScene.monotoneTangents=function(b){var c=[],d=[],f=[],g=[],h=0;for(h=0;h<b.length-1;h++)d[h]=(b[h+1].top-b[h].top)/(b[h+1].left-b[h].left);f[0]=d[0];g[0]=b[1].left-b[0].left;for(h=1;h<b.length-1;h++){f[h]=(d[h-1]+d[h])/2;g[h]=(b[h+1].left-b[h-1].left)/2}f[h]=d[h-1];g[h]=b[h].left-b[h-1].left;for(h=0;h<b.length-1;h++)if(d[h]==0){f[h]=0;f[h+1]=0}for(h=0;h<b.length-1;h++)if(!(Math.abs(f[h])<1.0E-5||Math.abs(f[h+1])<1.0E-5)){var i=f[h]/d[h],j=f[h+1]/d[h],l=i*i+j*j;if(l>9){l=3/Math.sqrt(l);f[h]=
     95l*i*d[h];f[h+1]=l*j*d[h]}}for(h=0;h<b.length;h++){d=1+f[h]*f[h];c.push({x:g[h]/3/d,y:f[h]*g[h]/3/d})}return c};pv.SvgScene.curveMonotone=function(b){if(b.length<=2)return"";return this.curveHermite(b,this.monotoneTangents(b))};pv.SvgScene.curveMonotoneSegments=function(b){if(b.length<=2)return"";return this.curveHermiteSegments(b,this.monotoneTangents(b))};
     96pv.SvgScene.area=function(b){function c(o,n){for(var m=[],r=[],s=n;o<=s;o++,n--){var u=b[o],x=b[n];u=u.left+","+u.top;x=x.left+x.width+","+(x.top+x.height);if(o<s){var t=b[o+1],p=b[n-1];switch(g.interpolate){case "step-before":u+="V"+t.top;x+="H"+(p.left+p.width);break;case "step-after":u+="H"+t.left;x+="V"+(p.top+p.height);break}}m.push(u);r.push(x)}return m.concat(r).join("L")}function d(o,n){for(var m=[],r=[],s=n;o<=s;o++,n--){var u=b[n];m.push(b[o]);r.push({left:u.left+u.width,top:u.top+u.height})}if(g.interpolate==
     97"basis"){o=pv.SvgScene.curveBasis(m);n=pv.SvgScene.curveBasis(r)}else if(g.interpolate=="cardinal"){o=pv.SvgScene.curveCardinal(m,g.tension);n=pv.SvgScene.curveCardinal(r,g.tension)}else{o=pv.SvgScene.curveMonotone(m);n=pv.SvgScene.curveMonotone(r)}return m[0].left+","+m[0].top+o+"L"+r[0].left+","+r[0].top+n}var f=b.$g.firstChild;if(!b.length)return f;var g=b[0];if(g.segmented)return this.areaSegment(b);if(!g.visible)return f;var h=g.fillStyle,i=g.strokeStyle;if(!h.opacity&&!i.opacity)return f;for(var j=
     98[],l,k=0;k<b.length;k++){l=b[k];if(l.width||l.height){for(var q=k+1;q<b.length;q++){l=b[q];if(!l.width&&!l.height)break}k&&g.interpolate!="step-after"&&k--;q<b.length&&g.interpolate!="step-before"&&q++;j.push((q-k>2&&(g.interpolate=="basis"||g.interpolate=="cardinal"||g.interpolate=="monotone")?d:c)(k,q-1));k=q-1}}if(!j.length)return f;f=this.expect(f,"path",{"shape-rendering":g.antialias?null:"crispEdges","pointer-events":g.events,cursor:g.cursor,d:"M"+j.join("ZM")+"Z",fill:h.color,"fill-opacity":h.opacity||
     99null,stroke:i.color,"stroke-opacity":i.opacity||null,"stroke-width":i.opacity?g.lineWidth/this.scale:null});return this.append(f,b,0)};
     100pv.SvgScene.areaSegment=function(b){var c=b.$g.firstChild,d=b[0],f,g;if(d.interpolate=="basis"||d.interpolate=="cardinal"||d.interpolate=="monotone"){f=[];g=[];for(var h=0,i=b.length;h<i;h++){var j=b[i-h-1];f.push(b[h]);g.push({left:j.left+j.width,top:j.top+j.height})}if(d.interpolate=="basis"){f=this.curveBasisSegments(f);g=this.curveBasisSegments(g)}else if(d.interpolate=="cardinal"){f=this.curveCardinalSegments(f,d.tension);g=this.curveCardinalSegments(g,d.tension)}else{f=this.curveMonotoneSegments(f);
     101g=this.curveMonotoneSegments(g)}}h=0;for(i=b.length-1;h<i;h++){d=b[h];var l=b[h+1];if(d.visible&&l.visible){var k=d.fillStyle,q=d.strokeStyle;if(k.opacity||q.opacity){if(f){j=f[h];l="L"+g[i-h-1].substr(1);j=j+l+"Z"}else{var o=d;j=l;switch(d.interpolate){case "step-before":o=l;break;case "step-after":j=d;break}j="M"+d.left+","+o.top+"L"+l.left+","+j.top+"L"+(l.left+l.width)+","+(j.top+j.height)+"L"+(d.left+d.width)+","+(o.top+o.height)+"Z"}c=this.expect(c,"path",{"shape-rendering":d.antialias?null:
     102"crispEdges","pointer-events":d.events,cursor:d.cursor,d:j,fill:k.color,"fill-opacity":k.opacity||null,stroke:q.color,"stroke-opacity":q.opacity||null,"stroke-width":q.opacity?d.lineWidth/this.scale:null});c=this.append(c,b,h)}}}return c};
     103pv.SvgScene.bar=function(b){for(var c=b.$g.firstChild,d=0;d<b.length;d++){var f=b[d];if(f.visible){var g=f.fillStyle,h=f.strokeStyle;if(g.opacity||h.opacity){c=this.expect(c,"rect",{"shape-rendering":f.antialias?null:"crispEdges","pointer-events":f.events,cursor:f.cursor,x:f.left,y:f.top,width:Math.max(1.0E-10,f.width),height:Math.max(1.0E-10,f.height),fill:g.color,"fill-opacity":g.opacity||null,stroke:h.color,"stroke-opacity":h.opacity||null,"stroke-width":h.opacity?f.lineWidth/this.scale:null});
     104c=this.append(c,b,d)}}}return c};
     105pv.SvgScene.dot=function(b){for(var c=b.$g.firstChild,d=0;d<b.length;d++){var f=b[d];if(f.visible){var g=f.fillStyle,h=f.strokeStyle;if(g.opacity||h.opacity){var i=f.radius,j=null;switch(f.shape){case "cross":j="M"+-i+","+-i+"L"+i+","+i+"M"+i+","+-i+"L"+-i+","+i;break;case "triangle":j=i;var l=i*1.1547;j="M0,"+j+"L"+l+","+-j+" "+-l+","+-j+"Z";break;case "diamond":i*=Math.SQRT2;j="M0,"+-i+"L"+i+",0 0,"+i+" "+-i+",0Z";break;case "square":j="M"+-i+","+-i+"L"+i+","+-i+" "+i+","+i+" "+-i+","+i+"Z";break;
     106case "tick":j="M0,0L0,"+-f.size;break;case "bar":j="M0,"+f.size/2+"L0,"+-(f.size/2);break}g={"shape-rendering":f.antialias?null:"crispEdges","pointer-events":f.events,cursor:f.cursor,fill:g.color,"fill-opacity":g.opacity||null,stroke:h.color,"stroke-opacity":h.opacity||null,"stroke-width":h.opacity?f.lineWidth/this.scale:null};if(j){g.transform="translate("+f.left+","+f.top+")";if(f.angle)g.transform+=" rotate("+180*f.angle/Math.PI+")";g.d=j;c=this.expect(c,"path",g)}else{g.cx=f.left;g.cy=f.top;g.r=
     107i;c=this.expect(c,"circle",g)}c=this.append(c,b,d)}}}return c};
     108pv.SvgScene.image=function(b){for(var c=b.$g.firstChild,d=0;d<b.length;d++){var f=b[d];if(f.visible){c=this.fill(c,b,d);if(f.image){c=this.expect(c,"foreignObject",{cursor:f.cursor,x:f.left,y:f.top,width:f.width,height:f.height});var g=c.firstChild||c.appendChild(document.createElementNS(this.xhtml,"canvas"));g.$scene={scenes:b,index:d};g.style.width=f.width;g.style.height=f.height;g.width=f.imageWidth;g.height=f.imageHeight;g.getContext("2d").putImageData(f.image,0,0)}else{c=this.expect(c,"image",
     109{preserveAspectRatio:"none",cursor:f.cursor,x:f.left,y:f.top,width:f.width,height:f.height});c.setAttributeNS(this.xlink,"href",f.url)}c=this.append(c,b,d);c=this.stroke(c,b,d)}}return c};
     110pv.SvgScene.label=function(b){for(var c=b.$g.firstChild,d=0;d<b.length;d++){var f=b[d];if(f.visible){var g=f.textStyle;if(g.opacity&&f.text){var h=0,i=0,j=0,l="start";switch(f.textBaseline){case "middle":j=".35em";break;case "top":j=".71em";i=f.textMargin;break;case "bottom":i="-"+f.textMargin;break}switch(f.textAlign){case "right":l="end";h="-"+f.textMargin;break;case "center":l="middle";break;case "left":h=f.textMargin;break}c=this.expect(c,"text",{"pointer-events":f.events,cursor:f.cursor,x:h,
     111y:i,dy:j,transform:"translate("+f.left+","+f.top+")"+(f.textAngle?" rotate("+180*f.textAngle/Math.PI+")":"")+(this.scale!=1?" scale("+1/this.scale+")":""),fill:g.color,"fill-opacity":g.opacity||null,"text-anchor":l},{font:f.font,"text-shadow":f.textShadow,"text-decoration":f.textDecoration});if(c.firstChild)c.firstChild.nodeValue=f.text;else c.appendChild(document.createTextNode(f.text));c=this.append(c,b,d)}}}return c};
     112pv.SvgScene.line=function(b){var c=b.$g.firstChild;if(b.length<2)return c;var d=b[0];if(d.segmented)return this.lineSegment(b);if(!d.visible)return c;var f=d.fillStyle,g=d.strokeStyle;if(!f.opacity&&!g.opacity)return c;var h="M"+d.left+","+d.top;if(b.length>2&&(d.interpolate=="basis"||d.interpolate=="cardinal"||d.interpolate=="monotone"))switch(d.interpolate){case "basis":h+=this.curveBasis(b);break;case "cardinal":h+=this.curveCardinal(b,d.tension);break;case "monotone":h+=this.curveMonotone(b);
     113break}else for(var i=1;i<b.length;i++)h+=this.pathSegment(b[i-1],b[i]);c=this.expect(c,"path",{"shape-rendering":d.antialias?null:"crispEdges","pointer-events":d.events,cursor:d.cursor,d:h,fill:f.color,"fill-opacity":f.opacity||null,stroke:g.color,"stroke-opacity":g.opacity||null,"stroke-width":g.opacity?d.lineWidth/this.scale:null,"stroke-linejoin":d.lineJoin});return this.append(c,b,0)};
     114pv.SvgScene.lineSegment=function(b){var c=b.$g.firstChild,d=b[0],f;switch(d.interpolate){case "basis":f=this.curveBasisSegments(b);break;case "cardinal":f=this.curveCardinalSegments(b,d.tension);break;case "monotone":f=this.curveMonotoneSegments(b);break}d=0;for(var g=b.length-1;d<g;d++){var h=b[d],i=b[d+1];if(h.visible&&i.visible){var j=h.strokeStyle,l=pv.Color.transparent;if(j.opacity){if(h.interpolate=="linear"&&h.lineJoin=="miter"){l=j;j=pv.Color.transparent;i=this.pathJoin(b[d-1],h,i,b[d+2])}else i=
     115f?f[d]:"M"+h.left+","+h.top+this.pathSegment(h,i);c=this.expect(c,"path",{"shape-rendering":h.antialias?null:"crispEdges","pointer-events":h.events,cursor:h.cursor,d:i,fill:l.color,"fill-opacity":l.opacity||null,stroke:j.color,"stroke-opacity":j.opacity||null,"stroke-width":j.opacity?h.lineWidth/this.scale:null,"stroke-linejoin":h.lineJoin});c=this.append(c,b,d)}}}return c};
     116pv.SvgScene.pathSegment=function(b,c){var d=1;switch(b.interpolate){case "polar-reverse":d=0;case "polar":var f=c.left-b.left,g=c.top-b.top;b=1-b.eccentricity;f=Math.sqrt(f*f+g*g)/(2*b);if(b<=0||b>1)break;return"A"+f+","+f+" 0 0,"+d+" "+c.left+","+c.top;case "step-before":return"V"+c.top+"H"+c.left;case "step-after":return"H"+c.left+"V"+c.top}return"L"+c.left+","+c.top};pv.SvgScene.lineIntersect=function(b,c,d,f){return b.plus(c.times(d.minus(b).dot(f.perp())/c.dot(f.perp())))};
     117pv.SvgScene.pathJoin=function(b,c,d,f){var g=pv.vector(c.left,c.top);d=pv.vector(d.left,d.top);var h=d.minus(g),i=h.perp().norm(),j=i.times(c.lineWidth/(2*this.scale));c=g.plus(j);var l=d.plus(j),k=d.minus(j);j=g.minus(j);if(b&&b.visible){b=g.minus(b.left,b.top).perp().norm().plus(i);j=this.lineIntersect(g,b,j,h);c=this.lineIntersect(g,b,c,h)}if(f&&f.visible){f=pv.vector(f.left,f.top).minus(d).perp().norm().plus(i);k=this.lineIntersect(d,f,k,h);l=this.lineIntersect(d,f,l,h)}return"M"+c.x+","+c.y+
     118"L"+l.x+","+l.y+" "+k.x+","+k.y+" "+j.x+","+j.y};
     119pv.SvgScene.panel=function(b){for(var c=b.$g,d=c&&c.firstChild,f=0;f<b.length;f++){var g=b[f];if(g.visible){if(!b.parent){g.canvas.style.display="inline-block";if(c&&c.parentNode!=g.canvas)d=(c=g.canvas.firstChild)&&c.firstChild;if(!c){c=g.canvas.appendChild(this.create("svg"));c.setAttribute("font-size","10px");c.setAttribute("font-family","sans-serif");c.setAttribute("fill","none");c.setAttribute("stroke","none");c.setAttribute("stroke-width",1.5);for(var h=0;h<this.events.length;h++)c.addEventListener(this.events[h],
     120this.dispatch,false);d=c.firstChild}b.$g=c;c.setAttribute("width",g.width+g.left+g.right);c.setAttribute("height",g.height+g.top+g.bottom)}if(g.overflow=="hidden"){h=pv.id().toString(36);var i=this.expect(d,"g",{"clip-path":"url(#"+h+")"});i.parentNode||c.appendChild(i);b.$g=c=i;d=i.firstChild;d=this.expect(d,"clipPath",{id:h});h=d.firstChild||d.appendChild(this.create("rect"));h.setAttribute("x",g.left);h.setAttribute("y",g.top);h.setAttribute("width",g.width);h.setAttribute("height",g.height);d.parentNode||
     121c.appendChild(d);d=d.nextSibling}d=this.fill(d,b,f);var j=this.scale,l=g.transform,k=g.left+l.x,q=g.top+l.y;this.scale*=l.k;for(h=0;h<g.children.length;h++){g.children[h].$g=d=this.expect(d,"g",{transform:"translate("+k+","+q+")"+(l.k!=1?" scale("+l.k+")":"")});this.updateAll(g.children[h]);d.parentNode||c.appendChild(d);d=d.nextSibling}this.scale=j;d=this.stroke(d,b,f);if(g.overflow=="hidden"){b.$g=c=i.parentNode;d=i.nextSibling}}}return d};
     122pv.SvgScene.fill=function(b,c,d){var f=c[d],g=f.fillStyle;if(g.opacity||f.events=="all"){b=this.expect(b,"rect",{"shape-rendering":f.antialias?null:"crispEdges","pointer-events":f.events,cursor:f.cursor,x:f.left,y:f.top,width:f.width,height:f.height,fill:g.color,"fill-opacity":g.opacity,stroke:null});b=this.append(b,c,d)}return b};
     123pv.SvgScene.stroke=function(b,c,d){var f=c[d],g=f.strokeStyle;if(g.opacity||f.events=="all"){b=this.expect(b,"rect",{"shape-rendering":f.antialias?null:"crispEdges","pointer-events":f.events=="all"?"stroke":f.events,cursor:f.cursor,x:f.left,y:f.top,width:Math.max(1.0E-10,f.width),height:Math.max(1.0E-10,f.height),fill:null,stroke:g.color,"stroke-opacity":g.opacity,"stroke-width":f.lineWidth/this.scale});b=this.append(b,c,d)}return b};
     124pv.SvgScene.rule=function(b){for(var c=b.$g.firstChild,d=0;d<b.length;d++){var f=b[d];if(f.visible){var g=f.strokeStyle;if(g.opacity){c=this.expect(c,"line",{"shape-rendering":f.antialias?null:"crispEdges","pointer-events":f.events,cursor:f.cursor,x1:f.left,y1:f.top,x2:f.left+f.width,y2:f.top+f.height,stroke:g.color,"stroke-opacity":g.opacity,"stroke-width":f.lineWidth/this.scale});c=this.append(c,b,d)}}}return c};
     125pv.SvgScene.wedge=function(b){for(var c=b.$g.firstChild,d=0;d<b.length;d++){var f=b[d];if(f.visible){var g=f.fillStyle,h=f.strokeStyle;if(g.opacity||h.opacity){var i=f.innerRadius,j=f.outerRadius,l=Math.abs(f.angle);if(l>=2*Math.PI)i=i?"M0,"+j+"A"+j+","+j+" 0 1,1 0,"+-j+"A"+j+","+j+" 0 1,1 0,"+j+"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"Z":"M0,"+j+"A"+j+","+j+" 0 1,1 0,"+-j+"A"+j+","+j+" 0 1,1 0,"+j+"Z";else{var k=Math.min(f.startAngle,f.endAngle),q=Math.max(f.startAngle,f.endAngle),
     126o=Math.cos(k),n=Math.cos(q);k=Math.sin(k);q=Math.sin(q);i=i?"M"+j*o+","+j*k+"A"+j+","+j+" 0 "+(l<Math.PI?"0":"1")+",1 "+j*n+","+j*q+"L"+i*n+","+i*q+"A"+i+","+i+" 0 "+(l<Math.PI?"0":"1")+",0 "+i*o+","+i*k+"Z":"M"+j*o+","+j*k+"A"+j+","+j+" 0 "+(l<Math.PI?"0":"1")+",1 "+j*n+","+j*q+"L0,0Z"}c=this.expect(c,"path",{"shape-rendering":f.antialias?null:"crispEdges","pointer-events":f.events,cursor:f.cursor,transform:"translate("+f.left+","+f.top+")",d:i,fill:g.color,"fill-rule":"evenodd","fill-opacity":g.opacity||
     127null,stroke:h.color,"stroke-opacity":h.opacity||null,"stroke-width":h.opacity?f.lineWidth/this.scale:null});c=this.append(c,b,d)}}}return c};pv.Mark=function(){this.$properties=[];this.$handlers={}};pv.Mark.prototype.properties={};pv.Mark.cast={};pv.Mark.prototype.property=function(b,c){if(!this.hasOwnProperty("properties"))this.properties=pv.extend(this.properties);this.properties[b]=true;pv.Mark.prototype.propertyMethod(b,false,pv.Mark.cast[b]=c);return this};
     128pv.Mark.prototype.propertyMethod=function(b,c,d){d||(d=pv.Mark.cast[b]);this[b]=function(f){if(c&&this.scene){var g=this.scene.defs;if(arguments.length){g[b]={id:f==null?0:pv.id(),value:f!=null&&d?d(f):f};return this}return g[b]?g[b].value:null}if(arguments.length){g=!c<<1|typeof f=="function";this.propertyValue(b,g&1&&d?function(){var h=f.apply(this,arguments);return h!=null?d(h):null}:f!=null&&d?d(f):f).type=g;return this}return this.instance()[b]}};
     129pv.Mark.prototype.propertyValue=function(b,c){var d=this.$properties;c={name:b,id:pv.id(),value:c};for(var f=0;f<d.length;f++)if(d[f].name==b){d.splice(f,1);break}d.push(c);return c};pv.Mark.prototype.property("data").property("visible",Boolean).property("left",Number).property("right",Number).property("top",Number).property("bottom",Number).property("cursor",String).property("title",String).property("reverse",Boolean).property("antialias",Boolean).property("events",String);a=pv.Mark.prototype;
     130a.childIndex=-1;a.index=-1;a.scale=1;a.defaults=(new pv.Mark).data(function(b){return[b]}).visible(true).antialias(true).events("painted");a.extend=function(b){this.proto=b;return this};a.add=function(b){return this.parent.add(b).extend(this)};a.def=function(b,c){this.propertyMethod(b,true);return this[b](arguments.length>1?c:null)};
     131a.anchor=function(b){function c(g){for(var h=d,i=[];!(f=h.scene);){g=g.parent;i.push({index:g.index,childIndex:h.childIndex});h=h.parent}for(;i.length;){g=i.pop();f=f[g.index].children[g.childIndex]}if(d.hasOwnProperty("index")){i=pv.extend(f[d.index]);i.right=i.top=i.left=i.bottom=0;return[i]}return f}var d=this,f;b||(b="center");return(new pv.Anchor(this)).name(b).def("$mark.anchor",function(){f=this.scene.target=c(this)}).data(function(){return f.map(function(g){return g.data})}).visible(function(){return f[this.index].visible}).left(function(){var g=
     132f[this.index],h=g.width||0;switch(this.name()){case "bottom":case "top":case "center":return g.left+h/2;case "left":return null}return g.left+h}).top(function(){var g=f[this.index],h=g.height||0;switch(this.name()){case "left":case "right":case "center":return g.top+h/2;case "top":return null}return g.top+h}).right(function(){var g=f[this.index];return this.name()=="left"?g.right+(g.width||0):null}).bottom(function(){var g=f[this.index];return this.name()=="top"?g.bottom+(g.height||0):null}).textAlign(function(){switch(this.name()){case "bottom":case "top":case "center":return"center";
     133case "right":return"right"}return"left"}).textBaseline(function(){switch(this.name()){case "right":case "left":case "center":return"middle";case "top":return"top"}return"bottom"})};a.anchorTarget=function(){return this.proto.anchorTarget()};a.margin=function(b){return this.left(b).right(b).top(b).bottom(b)};a.instance=function(b){var c=this.scene||this.parent.instance(-1).children[this.childIndex],d=!arguments.length||this.hasOwnProperty("index")?this.index:b;return c[d<0?c.length-1:d]};a.first=function(){return this.scene[0]};
     134a.last=function(){return this.scene[this.scene.length-1]};a.sibling=function(){return this.index==0?null:this.scene[this.index-1]};a.cousin=function(){var b=this.parent;return(b=b&&b.sibling())&&b.children?b.children[this.childIndex][this.index]:null};
     135a.render=function(){function b(i,j,l){i.scale=l;if(j<g.length){f.unshift(null);if(i.hasOwnProperty("index"))c(i,j,l);else{for(var k=0,q=i.scene.length;k<q;k++){i.index=k;c(i,j,l)}delete i.index}f.shift()}else{i.build();pv.Scene.scale=l;pv.Scene.updateAll(i.scene)}delete i.scale}function c(i,j,l){var k=i.scene[i.index],q;if(k.visible){var o=g[j],n=i.children[o];for(q=0;q<o;q++)i.children[q].scene=k.children[q];f[0]=k.data;if(n.scene)b(n,j+1,l*k.transform.k);else{n.scene=k.children[o];b(n,j+1,l*k.transform.k);
     136delete n.scene}for(q=0;q<o;q++)delete i.children[q].scene}}var d=this.parent,f=pv.Mark.stack;if(d&&!this.root.scene)this.root.render();else{for(var g=[],h=this;h.parent;h=h.parent)g.unshift(h.childIndex);for(this.bind();d&&!d.hasOwnProperty("index");)d=d.parent;this.context(d?d.scene:undefined,d?d.index:-1,function(){b(this.root,0,1)})}};pv.Mark.stack=[];a=pv.Mark.prototype;
     137a.bind=function(){function b(j){do for(var l=j.$properties,k=l.length-1;k>=0;k--){var q=l[k];if(!(q.name in c)){c[q.name]=q;switch(q.name){case "data":f=q;break;case "visible":g=q;break;default:d[q.type].push(q);break}}}while(j=j.proto)}var c={},d=[[],[],[],[]],f,g;b(this);b(this.defaults);d[1].reverse();d[3].reverse();var h=this;do for(var i in h.properties)i in c||d[2].push(c[i]={name:i,type:2,value:null});while(h=h.proto);h=d[0].concat(d[1]);for(i=0;i<h.length;i++)this.propertyMethod(h[i].name,
     138true);this.binds={properties:c,data:f,defs:h,required:[g],optional:pv.blend(d)}};
     139a.build=function(){var b=this.scene,c=pv.Mark.stack;if(!b){b=this.scene=[];b.mark=this;b.type=this.type;b.childIndex=this.childIndex;if(this.parent){b.parent=this.parent.scene;b.parentIndex=this.parent.index}}if(this.binds.defs.length){var d=b.defs;if(!d)b.defs=d={};for(var f=0;f<this.binds.defs.length;f++){var g=this.binds.defs[f],h=d[g.name];if(!h||g.id>h.id)d[g.name]={id:0,value:g.type&1?g.value.apply(this,c):g.value}}}d=this.binds.data;d=d.type&1?d.value.apply(this,c):d.value;c.unshift(null);
     140b.length=d.length;for(f=0;f<d.length;f++){pv.Mark.prototype.index=this.index=f;(g=b[f])||(b[f]=g={});g.data=c[0]=d[f];this.buildInstance(g)}pv.Mark.prototype.index=-1;delete this.index;c.shift();return this};a.buildProperties=function(b,c){for(var d=0,f=c.length;d<f;d++){var g=c[d],h=g.value;switch(g.type){case 0:case 1:h=this.scene.defs[g.name].value;break;case 3:h=h.apply(this,pv.Mark.stack);break}b[g.name]=h}};
     141a.buildInstance=function(b){this.buildProperties(b,this.binds.required);if(b.visible){this.buildProperties(b,this.binds.optional);this.buildImplied(b)}};
     142a.buildImplied=function(b){var c=b.left,d=b.right,f=b.top,g=b.bottom,h=this.properties,i=h.width?b.width:0,j=h.height?b.height:0,l=this.parent?this.parent.width():i+c+d;if(i==null)i=l-(d=d||0)-(c=c||0);else if(d==null)d=l-i-(c=c||0);else if(c==null)c=l-i-(d=d||0);l=this.parent?this.parent.height():j+f+g;if(j==null)j=l-(f=f||0)-(g=g||0);else if(g==null)g=l-j-(f=f||0);else if(f==null)f=l-j-(g=g||0);b.left=c;b.right=d;b.top=f;b.bottom=g;if(h.width)b.width=i;if(h.height)b.height=j;if(h.textStyle&&!b.textStyle)b.textStyle=
     143pv.Color.transparent;if(h.fillStyle&&!b.fillStyle)b.fillStyle=pv.Color.transparent;if(h.strokeStyle&&!b.strokeStyle)b.strokeStyle=pv.Color.transparent};
     144a.mouse=function(){var b=pv.event.pageX||0,c=pv.event.pageY||0,d=this.root.canvas();do{b-=d.offsetLeft;c-=d.offsetTop}while(d=d.offsetParent);d=pv.Transform.identity;var f=this.properties.transform?this:this.parent,g=[];do g.push(f);while(f=f.parent);for(;f=g.pop();)d=d.translate(f.left(),f.top()).times(f.transform());d=d.invert();return pv.vector(b*d.k+d.x,c*d.k+d.y)};a.event=function(b,c){this.$handlers[b]=pv.functor(c);return this};
     145a.context=function(b,c,d){function f(k,q){pv.Mark.scene=k;h.index=q;if(k){var o=k.mark,n=o,m=[];do{m.push(n);i.push(k[q].data);n.index=q;n.scene=k;q=k.parentIndex;k=k.parent}while(n=n.parent);k=m.length-1;for(q=1;k>0;k--){n=m[k];n.scale=q;q*=n.scene[n.index].transform.k}if(o.children){k=0;for(m=o.children.length;k<m;k++){n=o.children[k];n.scene=o.scene[o.index].children[k];n.scale=q}}}}function g(k){if(k){k=k.mark;var q;if(k.children)for(var o=0,n=k.children.length;o<n;o++){q=k.children[o];delete q.scene;
     146delete q.scale}q=k;do{i.pop();if(q.parent){delete q.scene;delete q.scale}delete q.index}while(q=q.parent)}}var h=pv.Mark.prototype,i=pv.Mark.stack,j=pv.Mark.scene,l=h.index;g(j,l);f(b,c);try{d.apply(this,i)}finally{g(b,c);f(j,l)}};pv.Mark.dispatch=function(b,c,d){var f=c.mark,g=c.parent,h=f.$handlers[b];if(!h)return g&&pv.Mark.dispatch(b,g,c.parentIndex);f.context(c,d,function(){(f=h.apply(f,pv.Mark.stack))&&f.render&&f.render()});return true};
     147pv.Anchor=function(b){pv.Mark.call(this);this.target=b;this.parent=b.parent};pv.Anchor.prototype=pv.extend(pv.Mark).property("name",String);pv.Anchor.prototype.anchorTarget=function(){return this.target};pv.Area=function(){pv.Mark.call(this)};
     148pv.Area.prototype=pv.extend(pv.Mark).property("width",Number).property("height",Number).property("lineWidth",Number).property("strokeStyle",pv.color).property("fillStyle",pv.color).property("segmented",Boolean).property("interpolate",String).property("tension",Number);pv.Area.prototype.type="area";pv.Area.prototype.defaults=(new pv.Area).extend(pv.Mark.prototype.defaults).lineWidth(1.5).fillStyle(pv.Colors.category20().by(pv.parent)).interpolate("linear").tension(0.7);
     149pv.Area.prototype.buildImplied=function(b){if(b.height==null)b.height=0;if(b.width==null)b.width=0;pv.Mark.prototype.buildImplied.call(this,b)};pv.Area.fixed={lineWidth:1,lineJoin:1,strokeStyle:1,fillStyle:1,segmented:1,interpolate:1,tension:1};
     150pv.Area.prototype.bind=function(){pv.Mark.prototype.bind.call(this);var b=this.binds,c=b.required;b=b.optional;for(var d=0,f=b.length;d<f;d++){var g=b[d];g.fixed=g.name in pv.Area.fixed;if(g.name=="segmented"){c.push(g);b.splice(d,1);d--;f--}}this.binds.$required=c;this.binds.$optional=b};
     151pv.Area.prototype.buildInstance=function(b){var c=this.binds;if(this.index){var d=c.fixed;if(!d){d=c.fixed=[];function f(i){return!i.fixed||(d.push(i),false)}c.required=c.required.filter(f);if(!this.scene[0].segmented)c.optional=c.optional.filter(f)}c=0;for(var g=d.length;c<g;c++){var h=d[c].name;b[h]=this.scene[0][h]}}else{c.required=c.$required;c.optional=c.$optional;c.fixed=null}pv.Mark.prototype.buildInstance.call(this,b)};
     152pv.Area.prototype.anchor=function(b){var c;return pv.Mark.prototype.anchor.call(this,b).def("$area.anchor",function(){c=this.scene.target}).interpolate(function(){return c[this.index].interpolate}).eccentricity(function(){return c[this.index].eccentricity}).tension(function(){return c[this.index].tension})};pv.Bar=function(){pv.Mark.call(this)};
     153pv.Bar.prototype=pv.extend(pv.Mark).property("width",Number).property("height",Number).property("lineWidth",Number).property("strokeStyle",pv.color).property("fillStyle",pv.color);pv.Bar.prototype.type="bar";pv.Bar.prototype.defaults=(new pv.Bar).extend(pv.Mark.prototype.defaults).lineWidth(1.5).fillStyle(pv.Colors.category20().by(pv.parent));pv.Dot=function(){pv.Mark.call(this)};
     154pv.Dot.prototype=pv.extend(pv.Mark).property("size",Number).property("radius",Number).property("shape",String).property("angle",Number).property("lineWidth",Number).property("strokeStyle",pv.color).property("fillStyle",pv.color);pv.Dot.prototype.type="dot";pv.Dot.prototype.defaults=(new pv.Dot).extend(pv.Mark.prototype.defaults).size(20).shape("circle").lineWidth(1.5).strokeStyle(pv.Colors.category10().by(pv.parent));
     155pv.Dot.prototype.anchor=function(b){var c;return pv.Mark.prototype.anchor.call(this,b).def("$wedge.anchor",function(){c=this.scene.target}).left(function(){var d=c[this.index];switch(this.name()){case "bottom":case "top":case "center":return d.left;case "left":return null}return d.left+d.radius}).right(function(){var d=c[this.index];return this.name()=="left"?d.right+d.radius:null}).top(function(){var d=c[this.index];switch(this.name()){case "left":case "right":case "center":return d.top;case "top":return null}return d.top+
     156d.radius}).bottom(function(){var d=c[this.index];return this.name()=="top"?d.bottom+d.radius:null}).textAlign(function(){switch(this.name()){case "left":return"right";case "bottom":case "top":case "center":return"center"}return"left"}).textBaseline(function(){switch(this.name()){case "right":case "left":case "center":return"middle";case "bottom":return"top"}return"bottom"})};
     157pv.Dot.prototype.buildImplied=function(b){if(b.radius==null)b.radius=Math.sqrt(b.size);else if(b.size==null)b.size=b.radius*b.radius;pv.Mark.prototype.buildImplied.call(this,b)};pv.Label=function(){pv.Mark.call(this)};
     158pv.Label.prototype=pv.extend(pv.Mark).property("text",String).property("font",String).property("textAngle",Number).property("textStyle",pv.color).property("textAlign",String).property("textBaseline",String).property("textMargin",Number).property("textDecoration",String).property("textShadow",String);pv.Label.prototype.type="label";pv.Label.prototype.defaults=(new pv.Label).extend(pv.Mark.prototype.defaults).events("none").text(pv.identity).font("10px sans-serif").textAngle(0).textStyle("black").textAlign("left").textBaseline("bottom").textMargin(3);
     159pv.Line=function(){pv.Mark.call(this)};pv.Line.prototype=pv.extend(pv.Mark).property("lineWidth",Number).property("lineJoin",String).property("strokeStyle",pv.color).property("fillStyle",pv.color).property("segmented",Boolean).property("interpolate",String).property("eccentricity",Number).property("tension",Number);a=pv.Line.prototype;a.type="line";a.defaults=(new pv.Line).extend(pv.Mark.prototype.defaults).lineJoin("miter").lineWidth(1.5).strokeStyle(pv.Colors.category10().by(pv.parent)).interpolate("linear").eccentricity(0).tension(0.7);
     160a.bind=pv.Area.prototype.bind;a.buildInstance=pv.Area.prototype.buildInstance;a.anchor=function(b){return pv.Area.prototype.anchor.call(this,b).textAlign(function(){switch(this.name()){case "left":return"right";case "bottom":case "top":case "center":return"center";case "right":return"left"}}).textBaseline(function(){switch(this.name()){case "right":case "left":case "center":return"middle";case "top":return"bottom";case "bottom":return"top"}})};pv.Rule=function(){pv.Mark.call(this)};
     161pv.Rule.prototype=pv.extend(pv.Mark).property("width",Number).property("height",Number).property("lineWidth",Number).property("strokeStyle",pv.color);pv.Rule.prototype.type="rule";pv.Rule.prototype.defaults=(new pv.Rule).extend(pv.Mark.prototype.defaults).lineWidth(1).strokeStyle("black").antialias(false);pv.Rule.prototype.anchor=pv.Line.prototype.anchor;
     162pv.Rule.prototype.buildImplied=function(b){var c=b.left,d=b.right;if(b.width!=null||c==null&&d==null||d!=null&&c!=null)b.height=0;else b.width=0;pv.Mark.prototype.buildImplied.call(this,b)};pv.Panel=function(){pv.Bar.call(this);this.children=[];this.root=this;this.$dom=pv.$&&pv.$.s};pv.Panel.prototype=pv.extend(pv.Bar).property("transform").property("overflow",String).property("canvas",function(b){return typeof b=="string"?document.getElementById(b):b});a=pv.Panel.prototype;a.type="panel";
     163a.defaults=(new pv.Panel).extend(pv.Bar.prototype.defaults).fillStyle(null).overflow("visible");a.anchor=function(b){b=pv.Bar.prototype.anchor.call(this,b);b.parent=this;return b};a.add=function(b){b=new b;b.parent=this;b.root=this.root;b.childIndex=this.children.length;this.children.push(b);return b};a.bind=function(){pv.Mark.prototype.bind.call(this);for(var b=0;b<this.children.length;b++)this.children[b].bind()};
     164a.buildInstance=function(b){pv.Bar.prototype.buildInstance.call(this,b);if(b.visible){if(!b.children)b.children=[];var c=this.scale*b.transform.k,d,f=this.children.length;pv.Mark.prototype.index=-1;for(var g=0;g<f;g++){d=this.children[g];d.scene=b.children[g];d.scale=c;d.build()}for(g=0;g<f;g++){d=this.children[g];b.children[g]=d.scene;delete d.scene;delete d.scale}b.children.length=f}};
     165a.buildImplied=function(b){if(!this.parent){var c=b.canvas;if(c){if(c.$panel!=this)for(c.$panel=this;c.lastChild;)c.removeChild(c.lastChild);var d;if(b.width==null){d=parseFloat(pv.css(c,"width"));b.width=d-b.left-b.right}if(b.height==null){d=parseFloat(pv.css(c,"height"));b.height=d-b.top-b.bottom}}else{d=this.$canvas||(this.$canvas=[]);if(!(c=d[this.index])){c=d[this.index]=document.createElement("span");if(this.$dom)this.$dom.parentNode.insertBefore(c,this.$dom);else{for(d=document.body;d.lastChild&&
     166d.lastChild.tagName;)d=d.lastChild;if(d!=document.body)d=d.parentNode;d.appendChild(c)}}}b.canvas=c}if(!b.transform)b.transform=pv.Transform.identity;pv.Mark.prototype.buildImplied.call(this,b)};pv.Image=function(){pv.Bar.call(this)};pv.Image.prototype=pv.extend(pv.Bar).property("url",String).property("imageWidth",Number).property("imageHeight",Number);a=pv.Image.prototype;a.type="image";a.defaults=(new pv.Image).extend(pv.Bar.prototype.defaults).fillStyle(null);
     167a.image=function(b){this.$image=function(){var c=b.apply(this,arguments);return c==null?pv.Color.transparent:typeof c=="string"?pv.color(c):c};return this};a.bind=function(){pv.Bar.prototype.bind.call(this);var b=this.binds,c=this;do b.image=c.$image;while(!b.image&&(c=c.proto))};
     168a.buildImplied=function(b){pv.Bar.prototype.buildImplied.call(this,b);if(b.visible){if(b.imageWidth==null)b.imageWidth=b.width;if(b.imageHeight==null)b.imageHeight=b.height;if(b.url==null&&this.binds.image){var c=this.$canvas||(this.$canvas=document.createElement("canvas")),d=c.getContext("2d"),f=b.imageWidth,g=b.imageHeight,h=pv.Mark.stack;c.width=f;c.height=g;b=(b.image=d.createImageData(f,g)).data;h.unshift(null,null);for(d=c=0;c<g;c++){h[1]=c;for(var i=0;i<f;i++){h[0]=i;var j=this.binds.image.apply(this,
     169h);b[d++]=j.r;b[d++]=j.g;b[d++]=j.b;b[d++]=255*j.a}}h.splice(0,2)}}};pv.Wedge=function(){pv.Mark.call(this)};pv.Wedge.prototype=pv.extend(pv.Mark).property("startAngle",Number).property("endAngle",Number).property("angle",Number).property("innerRadius",Number).property("outerRadius",Number).property("lineWidth",Number).property("strokeStyle",pv.color).property("fillStyle",pv.color);a=pv.Wedge.prototype;a.type="wedge";
     170a.defaults=(new pv.Wedge).extend(pv.Mark.prototype.defaults).startAngle(function(){var b=this.sibling();return b?b.endAngle:-Math.PI/2}).innerRadius(0).lineWidth(1.5).strokeStyle(null).fillStyle(pv.Colors.category20().by(pv.index));a.midRadius=function(){return(this.innerRadius()+this.outerRadius())/2};a.midAngle=function(){return(this.startAngle()+this.endAngle())/2};
     171a.anchor=function(b){function c(h){return h.innerRadius||h.angle<2*Math.PI}function d(h){return(h.innerRadius+h.outerRadius)/2}function f(h){return(h.startAngle+h.endAngle)/2}var g;return pv.Mark.prototype.anchor.call(this,b).def("$wedge.anchor",function(){g=this.scene.target}).left(function(){var h=g[this.index];if(c(h))switch(this.name()){case "outer":return h.left+h.outerRadius*Math.cos(f(h));case "inner":return h.left+h.innerRadius*Math.cos(f(h));case "start":return h.left+d(h)*Math.cos(h.startAngle);
     172case "center":return h.left+d(h)*Math.cos(f(h));case "end":return h.left+d(h)*Math.cos(h.endAngle)}return h.left}).top(function(){var h=g[this.index];if(c(h))switch(this.name()){case "outer":return h.top+h.outerRadius*Math.sin(f(h));case "inner":return h.top+h.innerRadius*Math.sin(f(h));case "start":return h.top+d(h)*Math.sin(h.startAngle);case "center":return h.top+d(h)*Math.sin(f(h));case "end":return h.top+d(h)*Math.sin(h.endAngle)}return h.top}).textAlign(function(){var h=g[this.index];if(c(h))switch(this.name()){case "outer":return pv.Wedge.upright(f(h))?
     173"right":"left";case "inner":return pv.Wedge.upright(f(h))?"left":"right"}return"center"}).textBaseline(function(){var h=g[this.index];if(c(h))switch(this.name()){case "start":return pv.Wedge.upright(h.startAngle)?"top":"bottom";case "end":return pv.Wedge.upright(h.endAngle)?"bottom":"top"}return"middle"}).textAngle(function(){var h=g[this.index],i=0;if(c(h))switch(this.name()){case "center":case "inner":case "outer":i=f(h);break;case "start":i=h.startAngle;break;case "end":i=h.endAngle;break}return pv.Wedge.upright(i)?
     174i:i+Math.PI})};pv.Wedge.upright=function(b){b%=2*Math.PI;b=b<0?2*Math.PI+b:b;return b<Math.PI/2||b>=3*Math.PI/2};pv.Wedge.prototype.buildImplied=function(b){if(b.angle==null)b.angle=b.endAngle-b.startAngle;else if(b.endAngle==null)b.endAngle=b.startAngle+b.angle;pv.Mark.prototype.buildImplied.call(this,b)};pv.simulation=function(b){return new pv.Simulation(b)};pv.Simulation=function(b){for(var c=0;c<b.length;c++)this.particle(b[c])};a=pv.Simulation.prototype;
     175a.particle=function(b){b.next=this.particles;if(isNaN(b.px))b.px=b.x;if(isNaN(b.py))b.py=b.y;if(isNaN(b.fx))b.fx=0;if(isNaN(b.fy))b.fy=0;this.particles=b;return this};a.force=function(b){b.next=this.forces;this.forces=b;return this};a.constraint=function(b){b.next=this.constraints;this.constraints=b;return this};
     176a.stabilize=function(b){var c;arguments.length||(b=3);for(var d=0;d<b;d++){var f=new pv.Quadtree(this.particles);for(c=this.constraints;c;c=c.next)c.apply(this.particles,f)}for(c=this.particles;c;c=c.next){c.px=c.x;c.py=c.y}return this};
     177a.step=function(){var b;for(b=this.particles;b;b=b.next){var c=b.px,d=b.py;b.px=b.x;b.py=b.y;b.x+=b.vx=b.x-c+b.fx;b.y+=b.vy=b.y-d+b.fy}c=new pv.Quadtree(this.particles);for(b=this.constraints;b;b=b.next)b.apply(this.particles,c);for(b=this.particles;b;b=b.next)b.fx=b.fy=0;for(b=this.forces;b;b=b.next)b.apply(this.particles,c)};
     178pv.Quadtree=function(b){function c(k,q,o,n,m,r){if(!(isNaN(q.x)||isNaN(q.y)))if(k.leaf)if(k.p){if(!(Math.abs(k.p.x-q.x)+Math.abs(k.p.y-q.y)<0.01)){var s=k.p;k.p=null;d(k,s,o,n,m,r)}d(k,q,o,n,m,r)}else k.p=q;else d(k,q,o,n,m,r)}function d(k,q,o,n,m,r){var s=(o+m)*0.5,u=(n+r)*0.5,x=q.x>=s,t=q.y>=u;k.leaf=false;switch((t<<1)+x){case 0:k=k.c1||(k.c1=new pv.Quadtree.Node);break;case 1:k=k.c2||(k.c2=new pv.Quadtree.Node);break;case 2:k=k.c3||(k.c3=new pv.Quadtree.Node);break;case 3:k=k.c4||(k.c4=new pv.Quadtree.Node);
     179break}if(x)o=s;else m=s;if(t)n=u;else r=u;c(k,q,o,n,m,r)}var f,g=Number.POSITIVE_INFINITY,h=g,i=Number.NEGATIVE_INFINITY,j=i;for(f=b;f;f=f.next){if(f.x<g)g=f.x;if(f.y<h)h=f.y;if(f.x>i)i=f.x;if(f.y>j)j=f.y}f=i-g;var l=j-h;if(f>l)j=h+f;else i=g+l;this.xMin=g;this.yMin=h;this.xMax=i;this.yMax=j;this.root=new pv.Quadtree.Node;for(f=b;f;f=f.next)c(this.root,f,g,h,i,j)};pv.Quadtree.Node=function(){this.leaf=true;this.p=this.c4=this.c3=this.c2=this.c1=null};pv.Force={};
     180pv.Force.charge=function(b){function c(k){function q(m){c(m);k.cn+=m.cn;o+=m.cn*m.cx;n+=m.cn*m.cy}var o=0,n=0;k.cn=0;if(!k.leaf){k.c1&&q(k.c1);k.c2&&q(k.c2);k.c3&&q(k.c3);k.c4&&q(k.c4)}if(k.p){k.cn+=b;o+=b*k.p.x;n+=b*k.p.y}k.cx=o/k.cn;k.cy=n/k.cn}function d(k,q,o,n,m,r){var s=k.cx-q.x,u=k.cy-q.y,x=1/Math.sqrt(s*s+u*u);if(k.leaf&&k.p!=q||(m-o)*x<j){if(!(x<i)){if(x>g)x=g;k=k.cn*x*x*x;s=s*k;u=u*k;q.fx+=s;q.fy+=u}}else if(!k.leaf){var t=(o+m)*0.5,p=(n+r)*0.5;k.c1&&d(k.c1,q,o,n,t,p);k.c2&&d(k.c2,q,t,n,
     181m,p);k.c3&&d(k.c3,q,o,p,t,r);k.c4&&d(k.c4,q,t,p,m,r);if(!(x<i)){if(x>g)x=g;if(k.p&&k.p!=q){k=b*x*x*x;s=s*k;u=u*k;q.fx+=s;q.fy+=u}}}}var f=2,g=1/f,h=500,i=1/h,j=0.9,l={};arguments.length||(b=-40);l.constant=function(k){if(arguments.length){b=Number(k);return l}return b};l.domain=function(k,q){if(arguments.length){f=Number(k);g=1/f;h=Number(q);i=1/h;return l}return[f,h]};l.theta=function(k){if(arguments.length){j=Number(k);return l}return j};l.apply=function(k,q){c(q.root);for(k=k;k;k=k.next)d(q.root,
     182k,q.xMin,q.yMin,q.xMax,q.yMax)};return l};pv.Force.drag=function(b){var c={};arguments.length||(b=0.1);c.constant=function(d){if(arguments.length){b=d;return c}return b};c.apply=function(d){if(b)for(d=d;d;d=d.next){d.fx-=b*d.vx;d.fy-=b*d.vy}};return c};
     183pv.Force.spring=function(b){var c=0.1,d=20,f,g,h={};arguments.length||(b=0.1);h.links=function(i){if(arguments.length){f=i;g=i.map(function(j){return 1/Math.sqrt(Math.max(j.sourceNode.linkDegree,j.targetNode.linkDegree))});return h}return f};h.constant=function(i){if(arguments.length){b=Number(i);return h}return b};h.damping=function(i){if(arguments.length){c=Number(i);return h}return c};h.length=function(i){if(arguments.length){d=Number(i);return h}return d};h.apply=function(){for(var i=0;i<f.length;i++){var j=
     184f[i].sourceNode,l=f[i].targetNode,k=j.x-l.x,q=j.y-l.y,o=Math.sqrt(k*k+q*q),n=o?1/o:1;n=(b*g[i]*(o-d)+c*g[i]*(k*(j.vx-l.vx)+q*(j.vy-l.vy))*n)*n;k=-n*(o?k:0.01*(0.5-Math.random()));q=-n*(o?q:0.01*(0.5-Math.random()));j.fx+=k;j.fy+=q;l.fx-=k;l.fy-=q}};return h};pv.Constraint={};
     185pv.Constraint.collision=function(b){function c(k,q,o,n,m,r){if(!k.leaf){var s=(o+m)*0.5,u=(n+r)*0.5,x=u<j,t=s>g,p=s<i;if(u>h){k.c1&&t&&c(k.c1,q,o,n,s,u);k.c2&&p&&c(k.c2,q,s,n,m,u)}if(x){k.c3&&t&&c(k.c3,q,o,u,s,r);k.c4&&p&&c(k.c4,q,s,u,m,r)}}if(k.p&&k.p!=q){o=q.x-k.p.x;n=q.y-k.p.y;m=Math.sqrt(o*o+n*n);r=f+b(k.p);if(m<r){m=(m-r)/m*0.5;o*=m;n*=m;q.x-=o;q.y-=n;k.p.x+=o;k.p.y+=n}}}var d=1,f,g,h,i,j,l={};arguments.length||(f=10);l.repeat=function(k){if(arguments.length){d=Number(k);return l}return d};l.apply=
     186function(k,q){var o,n,m=-Infinity;for(o=k;o;o=o.next){n=b(o);if(n>m)m=n}for(var r=0;r<d;r++)for(o=k;o;o=o.next){n=(f=b(o))+m;g=o.x-n;i=o.x+n;h=o.y-n;j=o.y+n;c(q.root,o,q.xMin,q.yMin,q.xMax,q.yMax)}};return l};pv.Constraint.position=function(b){var c=1,d={};arguments.length||(b=function(f){return f.fix});d.alpha=function(f){if(arguments.length){c=Number(f);return d}return c};d.apply=function(f){for(f=f;f;f=f.next){var g=b(f);if(g){f.x+=(g.x-f.x)*c;f.y+=(g.y-f.y)*c;f.fx=f.fy=f.vx=f.vy=0}}};return d};
     187pv.Constraint.bound=function(){var b={},c,d;b.x=function(f,g){if(arguments.length){c={min:Math.min(f,g),max:Math.max(f,g)};return this}return c};b.y=function(f,g){if(arguments.length){d={min:Math.min(f,g),max:Math.max(f,g)};return this}return d};b.apply=function(f){if(c)for(var g=f;g;g=g.next)g.x=g.x<c.min?c.min:g.x>c.max?c.max:g.x;if(d)for(g=f;g;g=g.next)g.y=g.y<d.min?d.min:g.y>d.max?d.max:g.y};return b};pv.Layout=function(){pv.Panel.call(this)};pv.Layout.prototype=pv.extend(pv.Panel);
     188pv.Layout.prototype.property=function(b,c){if(!this.hasOwnProperty("properties"))this.properties=pv.extend(this.properties);this.properties[b]=true;this.propertyMethod(b,false,pv.Mark.cast[b]=c);return this};
     189pv.Layout.Network=function(){pv.Layout.call(this);var b=this;this.$id=pv.id();(this.node=(new pv.Mark).data(function(){return b.nodes()}).strokeStyle("#1f77b4").fillStyle("#fff").left(function(c){return c.x}).top(function(c){return c.y})).parent=this;this.link=(new pv.Mark).extend(this.node).data(function(c){return[c.sourceNode,c.targetNode]}).fillStyle(null).lineWidth(function(c,d){return d.linkValue*1.5}).strokeStyle("rgba(0,0,0,.2)");this.link.add=function(c){return b.add(pv.Panel).data(function(){return b.links()}).add(c).extend(this)};
     190(this.label=(new pv.Mark).extend(this.node).textMargin(7).textBaseline("middle").text(function(c){return c.nodeName||c.nodeValue}).textAngle(function(c){c=c.midAngle;return pv.Wedge.upright(c)?c:c+Math.PI}).textAlign(function(c){return pv.Wedge.upright(c.midAngle)?"left":"right"})).parent=this};
     191pv.Layout.Network.prototype=pv.extend(pv.Layout).property("nodes",function(b){return b.map(function(c,d){if(typeof c!="object")c={nodeValue:c};c.index=d;c.linkDegree=0;return c})}).property("links",function(b){return b.map(function(c){if(isNaN(c.linkValue))c.linkValue=isNaN(c.value)?1:c.value;return c})});pv.Layout.Network.prototype.reset=function(){this.$id=pv.id();return this};
     192pv.Layout.Network.prototype.buildProperties=function(b,c){if((b.$id||0)<this.$id)pv.Layout.prototype.buildProperties.call(this,b,c)};pv.Layout.Network.prototype.buildImplied=function(b){pv.Layout.prototype.buildImplied.call(this,b);if(b.$id>=this.$id)return true;b.$id=this.$id;b.links.forEach(function(c){var d=c.linkValue;(c.sourceNode||(c.sourceNode=b.nodes[c.source])).linkDegree+=d;(c.targetNode||(c.targetNode=b.nodes[c.target])).linkDegree+=d})};
     193pv.Layout.Hierarchy=function(){pv.Layout.Network.call(this);this.link.strokeStyle("#ccc")};pv.Layout.Hierarchy.prototype=pv.extend(pv.Layout.Network);pv.Layout.Hierarchy.prototype.buildImplied=function(b){if(!b.links)b.links=pv.Layout.Hierarchy.links.call(this);pv.Layout.Network.prototype.buildImplied.call(this,b)};pv.Layout.Hierarchy.links=function(){return this.nodes().filter(function(b){return b.parentNode}).map(function(b){return{sourceNode:b,targetNode:b.parentNode,linkValue:1}})};
     194pv.Layout.Hierarchy.NodeLink={buildImplied:function(b){function c(m){return m.parentNode?m.depth*(o-q)+q:0}function d(m){return m.parentNode?(m.breadth-0.25)*2*Math.PI:0}function f(m){switch(i){case "left":return m.depth*l;case "right":return l-m.depth*l;case "top":return m.breadth*l;case "bottom":return l-m.breadth*l;case "radial":return l/2+c(m)*Math.cos(m.midAngle)}}function g(m){switch(i){case "left":return m.breadth*k;case "right":return k-m.breadth*k;case "top":return m.depth*k;case "bottom":return k-
     195m.depth*k;case "radial":return k/2+c(m)*Math.sin(m.midAngle)}}var h=b.nodes,i=b.orient,j=/^(top|bottom)$/.test(i),l=b.width,k=b.height;if(i=="radial"){var q=b.innerRadius,o=b.outerRadius;if(q==null)q=0;if(o==null)o=Math.min(l,k)/2}for(b=0;b<h.length;b++){var n=h[b];n.midAngle=i=="radial"?d(n):j?Math.PI/2:0;n.x=f(n);n.y=g(n);if(n.firstChild)n.midAngle+=Math.PI}}};
     196pv.Layout.Hierarchy.Fill={constructor:function(){this.node.strokeStyle("#fff").fillStyle("#ccc").width(function(b){return b.dx}).height(function(b){return b.dy}).innerRadius(function(b){return b.innerRadius}).outerRadius(function(b){return b.outerRadius}).startAngle(function(b){return b.startAngle}).angle(function(b){return b.angle});this.label.textAlign("center").left(function(b){return b.x+b.dx/2}).top(function(b){return b.y+b.dy/2});delete this.link},buildImplied:function(b){function c(p,v){return(p+
     197v)/(1+v)}function d(p){switch(o){case "left":return c(p.minDepth,s)*m;case "right":return(1-c(p.maxDepth,s))*m;case "top":return p.minBreadth*m;case "bottom":return(1-p.maxBreadth)*m;case "radial":return m/2}}function f(p){switch(o){case "left":return p.minBreadth*r;case "right":return(1-p.maxBreadth)*r;case "top":return c(p.minDepth,s)*r;case "bottom":return(1-c(p.maxDepth,s))*r;case "radial":return r/2}}function g(p){switch(o){case "left":case "right":return(p.maxDepth-p.minDepth)/(1+s)*m;case "top":case "bottom":return(p.maxBreadth-
     198p.minBreadth)*m;case "radial":return p.parentNode?(p.innerRadius+p.outerRadius)*Math.cos(p.midAngle):0}}function h(p){switch(o){case "left":case "right":return(p.maxBreadth-p.minBreadth)*r;case "top":case "bottom":return(p.maxDepth-p.minDepth)/(1+s)*r;case "radial":return p.parentNode?(p.innerRadius+p.outerRadius)*Math.sin(p.midAngle):0}}function i(p){return Math.max(0,c(p.minDepth,s/2))*(x-u)+u}function j(p){return c(p.maxDepth,s/2)*(x-u)+u}function l(p){return(p.parentNode?p.minBreadth-0.25:0)*
     1992*Math.PI}function k(p){return(p.parentNode?p.maxBreadth-p.minBreadth:1)*2*Math.PI}var q=b.nodes,o=b.orient,n=/^(top|bottom)$/.test(o),m=b.width,r=b.height,s=-q[0].minDepth;if(o=="radial"){var u=b.innerRadius,x=b.outerRadius;if(u==null)u=0;if(u)s*=2;if(x==null)x=Math.min(m,r)/2}for(b=0;b<q.length;b++){var t=q[b];t.x=d(t);t.y=f(t);if(o=="radial"){t.innerRadius=i(t);t.outerRadius=j(t);t.startAngle=l(t);t.angle=k(t);t.midAngle=t.startAngle+t.angle/2}else t.midAngle=n?-Math.PI/2:0;t.dx=g(t);t.dy=h(t)}}};
     200pv.Layout.Grid=function(){pv.Layout.call(this);var b=this;(this.cell=(new pv.Mark).data(function(){return b.scene[b.index].$grid}).width(function(){return b.width()/b.cols()}).height(function(){return b.height()/b.rows()}).left(function(){return this.width()*(this.index%b.cols())}).top(function(){return this.height()*Math.floor(this.index/b.cols())})).parent=this};pv.Layout.Grid.prototype=pv.extend(pv.Layout).property("rows").property("cols");pv.Layout.Grid.prototype.defaults=(new pv.Layout.Grid).extend(pv.Layout.prototype.defaults).rows(1).cols(1);
     201pv.Layout.Grid.prototype.buildImplied=function(b){pv.Layout.prototype.buildImplied.call(this,b);var c=b.rows,d=b.cols;if(typeof d=="object")c=pv.transpose(d);if(typeof c=="object"){b.$grid=pv.blend(c);b.rows=c.length;b.cols=c[0]?c[0].length:0}else b.$grid=pv.repeat([b.data],c*d)};
     202pv.Layout.Stack=function(){function b(i){return function(){return f[i](this.parent.index,this.index)}}pv.Layout.call(this);var c=this,d=function(){return null},f={t:d,l:d,r:d,b:d,w:d,h:d},g,h=c.buildImplied;this.buildImplied=function(i){h.call(this,i);var j=i.layers,l=j.length,k,q=i.orient,o=/^(top|bottom)\b/.test(q),n=this.parent[o?"height":"width"](),m=[],r=[],s=[],u=pv.Mark.stack,x={parent:{parent:this}};u.unshift(null);g=[];for(var t=0;t<l;t++){s[t]=[];r[t]=[];x.parent.index=t;u[0]=j[t];g[t]=
     203this.$values.apply(x.parent,u);if(!t)k=g[t].length;u.unshift(null);for(var p=0;p<k;p++){u[0]=g[t][p];x.index=p;t||(m[p]=this.$x.apply(x,u));s[t][p]=this.$y.apply(x,u)}u.shift()}u.shift();switch(i.order){case "inside-out":var v=s.map(function(A){return pv.max.index(A)});x=pv.range(l).sort(function(A,D){return v[A]-v[D]});j=s.map(function(A){return pv.sum(A)});var w=u=0,y=[],z=[];for(t=0;t<l;t++){p=x[t];if(u<w){u+=j[p];y.push(p)}else{w+=j[p];z.push(p)}}j=z.reverse().concat(y);break;case "reverse":j=
     204pv.range(l-1,-1,-1);break;default:j=pv.range(l);break}switch(i.offset){case "silohouette":for(p=0;p<k;p++){for(t=x=0;t<l;t++)x+=s[t][p];r[j[0]][p]=(n-x)/2}break;case "wiggle":for(t=x=0;t<l;t++)x+=s[t][0];r[j[0]][0]=x=(n-x)/2;for(p=1;p<k;p++){u=n=0;w=m[p]-m[p-1];for(t=0;t<l;t++)n+=s[t][p];for(t=0;t<l;t++){y=(s[j[t]][p]-s[j[t]][p-1])/(2*w);for(i=0;i<t;i++)y+=(s[j[i]][p]-s[j[i]][p-1])/w;u+=y*s[j[t]][p]}r[j[0]][p]=x-=n?u/n*w:0}break;case "expand":for(p=0;p<k;p++){for(t=i=r[j[0]][p]=0;t<l;t++)i+=s[t][p];
     205if(i){i=n/i;for(t=0;t<l;t++)s[t][p]*=i}else{i=n/l;for(t=0;t<l;t++)s[t][p]=i}}break;default:for(p=0;p<k;p++)r[j[0]][p]=0;break}for(p=0;p<k;p++){x=r[j[0]][p];for(t=1;t<l;t++){x+=s[j[t-1]][p];r[j[t]][p]=x}}t=q.indexOf("-");l=o?"h":"w";o=t<0?o?"l":"b":q.charAt(t+1);q=q.charAt(0);for(var C in f)f[C]=d;f[o]=function(A,D){return m[D]};f[q]=function(A,D){return r[A][D]};f[l]=function(A,D){return s[A][D]}};this.layer=(new pv.Mark).data(function(){return g[this.parent.index]}).top(b("t")).left(b("l")).right(b("r")).bottom(b("b")).width(b("w")).height(b("h"));
     206this.layer.add=function(i){return c.add(pv.Panel).data(function(){return c.layers()}).add(i).extend(this)}};pv.Layout.Stack.prototype=pv.extend(pv.Layout).property("orient",String).property("offset",String).property("order",String).property("layers");a=pv.Layout.Stack.prototype;a.defaults=(new pv.Layout.Stack).extend(pv.Layout.prototype.defaults).orient("bottom-left").offset("zero").layers([[]]);a.$x=pv.Layout.Stack.prototype.$y=function(){return 0};a.x=function(b){this.$x=pv.functor(b);return this};
     207a.y=function(b){this.$y=pv.functor(b);return this};a.$values=pv.identity;a.values=function(b){this.$values=pv.functor(b);return this};
     208pv.Layout.Treemap=function(){pv.Layout.Hierarchy.call(this);this.node.strokeStyle("#fff").fillStyle("rgba(31, 119, 180, .25)").width(function(b){return b.dx}).height(function(b){return b.dy});this.label.visible(function(b){return!b.firstChild}).left(function(b){return b.x+b.dx/2}).top(function(b){return b.y+b.dy/2}).textAlign("center").textAngle(function(b){return b.dx>b.dy?0:-Math.PI/2});(this.leaf=(new pv.Mark).extend(this.node).fillStyle(null).strokeStyle(null).visible(function(b){return!b.firstChild})).parent=
     209this;delete this.link};pv.Layout.Treemap.prototype=pv.extend(pv.Layout.Hierarchy).property("round",Boolean).property("paddingLeft",Number).property("paddingRight",Number).property("paddingTop",Number).property("paddingBottom",Number).property("mode",String).property("order",String);a=pv.Layout.Treemap.prototype;a.defaults=(new pv.Layout.Treemap).extend(pv.Layout.Hierarchy.prototype.defaults).mode("squarify").order("ascending");a.padding=function(b){return this.paddingLeft(b).paddingRight(b).paddingTop(b).paddingBottom(b)};
     210a.$size=function(b){return Number(b.nodeValue)};a.size=function(b){this.$size=pv.functor(b);return this};
     211a.buildImplied=function(b){function c(r,s,u,x,t,p,v){for(var w=0,y=0;w<r.length;w++){var z=r[w];if(u){z.x=x+y;z.y=t;y+=z.dx=n(p*z.size/s);z.dy=v}else{z.x=x;z.y=t+y;z.dx=p;y+=z.dy=n(v*z.size/s)}}if(z)if(u)z.dx+=p-y;else z.dy+=v-y}function d(r,s){for(var u=-Infinity,x=Infinity,t=0,p=0;p<r.length;p++){var v=r[p].size;if(v<x)x=v;if(v>u)u=v;t+=v}t*=t;s*=s;return Math.max(s*u/t,t/(s*x))}function f(r,s){function u(A){var D=p==y,G=pv.sum(A,o),E=y?n(G/y):0;c(A,G,D,x,t,D?p:E,D?E:v);if(D){t+=E;v-=E}else{x+=
     212E;p-=E}y=Math.min(p,v);return D}var x=r.x+j,t=r.y+k,p=r.dx-j-l,v=r.dy-k-q;if(m!="squarify")c(r.childNodes,r.size,m=="slice"?true:m=="dice"?false:s&1,x,t,p,v);else{var w=[];s=Infinity;var y=Math.min(p,v),z=p*v/r.size;if(!(r.size<=0)){r.visitBefore(function(A){A.size*=z});for(r=r.childNodes.slice();r.length;){var C=r[r.length-1];if(C.size){w.push(C);z=d(w,y);if(z<=s){r.pop();s=z}else{w.pop();u(w);w.length=0;s=Infinity}}else r.pop()}if(u(w))for(s=0;s<w.length;s++)w[s].dy+=v;else for(s=0;s<w.length;s++)w[s].dx+=
     213p}}}if(!pv.Layout.Hierarchy.prototype.buildImplied.call(this,b)){var g=this,h=b.nodes[0],i=pv.Mark.stack,j=b.paddingLeft,l=b.paddingRight,k=b.paddingTop,q=b.paddingBottom,o=function(r){return r.size},n=b.round?Math.round:Number,m=b.mode;i.unshift(null);h.visitAfter(function(r,s){r.depth=s;r.x=r.y=r.dx=r.dy=0;r.size=r.firstChild?pv.sum(r.childNodes,function(u){return u.size}):g.$size.apply(g,(i[0]=r,i))});i.shift();switch(b.order){case "ascending":h.sort(function(r,s){return r.size-s.size});break;
     214case "descending":h.sort(function(r,s){return s.size-r.size});break;case "reverse":h.reverse();break}h.x=0;h.y=0;h.dx=b.width;h.dy=b.height;h.visitBefore(f)}};pv.Layout.Tree=function(){pv.Layout.Hierarchy.call(this)};pv.Layout.Tree.prototype=pv.extend(pv.Layout.Hierarchy).property("group",Number).property("breadth",Number).property("depth",Number).property("orient",String);pv.Layout.Tree.prototype.defaults=(new pv.Layout.Tree).extend(pv.Layout.Hierarchy.prototype.defaults).group(1).breadth(15).depth(60).orient("top");
     215pv.Layout.Tree.prototype.buildImplied=function(b){function c(p){var v,w,y;if(p.firstChild){v=p.firstChild;w=p.lastChild;for(var z=y=v;z;z=z.nextSibling){c(z);y=f(z,y)}j(p);w=0.5*(v.prelim+w.prelim);if(v=p.previousSibling){p.prelim=v.prelim+k(p.depth,true);p.mod=p.prelim-w}else p.prelim=w}else if(v=p.previousSibling)p.prelim=v.prelim+k(p.depth,true)}function d(p,v,w){p.breadth=p.prelim+v;v+=p.mod;for(p=p.firstChild;p;p=p.nextSibling)d(p,v,w)}function f(p,v){var w=p.previousSibling;if(w){var y=p,z=
     216p,C=w;w=p.parentNode.firstChild;var A=y.mod,D=z.mod,G=C.mod,E=w.mod;C=h(C);for(y=g(y);C&&y;){C=C;y=y;w=g(w);z=h(z);z.ancestor=p;var B=C.prelim+G-(y.prelim+A)+k(C.depth,false);if(B>0){i(l(C,p,v),p,B);A+=B;D+=B}G+=C.mod;A+=y.mod;E+=w.mod;D+=z.mod;C=h(C);y=g(y)}if(C&&!h(z)){z.thread=C;z.mod+=G-D}if(y&&!g(w)){w.thread=y;w.mod+=A-E;v=p}}return v}function g(p){return p.firstChild||p.thread}function h(p){return p.lastChild||p.thread}function i(p,v,w){var y=v.number-p.number;v.change-=w/y;v.shift+=w;p.change+=
     217w/y;v.prelim+=w;v.mod+=w}function j(p){var v=0,w=0;for(p=p.lastChild;p;p=p.previousSibling){p.prelim+=v;p.mod+=v;w+=p.change;v+=p.shift+w}}function l(p,v,w){return p.ancestor.parentNode==v.parentNode?p.ancestor:w}function k(p,v){return(v?1:u+1)/(m=="radial"?p:1)}function q(p){return m=="radial"?p.breadth/r:0}function o(p){switch(m){case "left":return p.depth;case "right":return x-p.depth;case "top":case "bottom":return p.breadth+x/2;case "radial":return x/2+p.depth*Math.cos(q(p))}}function n(p){switch(m){case "left":case "right":return p.breadth+
     218t/2;case "top":return p.depth;case "bottom":return t-p.depth;case "radial":return t/2+p.depth*Math.sin(q(p))}}if(!pv.Layout.Hierarchy.prototype.buildImplied.call(this,b)){var m=b.orient,r=b.depth,s=b.breadth,u=b.group,x=b.width,t=b.height;b=b.nodes[0];b.visitAfter(function(p,v){p.ancestor=p;p.prelim=0;p.mod=0;p.change=0;p.shift=0;p.number=p.previousSibling?p.previousSibling.number+1:0;p.depth=v});c(b);d(b,-b.prelim,0);b.visitAfter(function(p){p.breadth*=s;p.depth*=r;p.midAngle=q(p);p.x=o(p);p.y=n(p);
     219if(p.firstChild)p.midAngle+=Math.PI;delete p.breadth;delete p.depth;delete p.ancestor;delete p.prelim;delete p.mod;delete p.change;delete p.shift;delete p.number;delete p.thread})}};pv.Layout.Indent=function(){pv.Layout.Hierarchy.call(this);this.link.interpolate("step-after")};pv.Layout.Indent.prototype=pv.extend(pv.Layout.Hierarchy).property("depth",Number).property("breadth",Number);pv.Layout.Indent.prototype.defaults=(new pv.Layout.Indent).extend(pv.Layout.Hierarchy.prototype.defaults).depth(15).breadth(15);
     220pv.Layout.Indent.prototype.buildImplied=function(b){function c(i,j,l){i.x=g+l++*f;i.y=h+j++*d;i.midAngle=0;for(i=i.firstChild;i;i=i.nextSibling)j=c(i,j,l);return j}if(!pv.Layout.Hierarchy.prototype.buildImplied.call(this,b)){var d=b.breadth,f=b.depth,g=0,h=0;c(b.nodes[0],1,1)}};pv.Layout.Pack=function(){pv.Layout.Hierarchy.call(this);this.node.radius(function(b){return b.radius}).strokeStyle("rgb(31, 119, 180)").fillStyle("rgba(31, 119, 180, .25)");this.label.textAlign("center");delete this.link};
     221pv.Layout.Pack.prototype=pv.extend(pv.Layout.Hierarchy).property("spacing",Number).property("order",String);pv.Layout.Pack.prototype.defaults=(new pv.Layout.Pack).extend(pv.Layout.Hierarchy.prototype.defaults).spacing(1).order("ascending");pv.Layout.Pack.prototype.$radius=function(){return 1};pv.Layout.Pack.prototype.size=function(b){this.$radius=typeof b=="function"?function(){return Math.sqrt(b.apply(this,arguments))}:(b=Math.sqrt(b),function(){return b});return this};
     222pv.Layout.Pack.prototype.buildImplied=function(b){function c(o){var n=pv.Mark.stack;n.unshift(null);for(var m=0,r=o.length;m<r;m++){var s=o[m];if(!s.firstChild)s.radius=i.$radius.apply(i,(n[0]=s,n))}n.shift()}function d(o){var n=[];for(o=o.firstChild;o;o=o.nextSibling){if(o.firstChild)o.radius=d(o);o.n=o.p=o;n.push(o)}switch(b.order){case "ascending":n.sort(function(m,r){return m.radius-r.radius});break;case "descending":n.sort(function(m,r){return r.radius-m.radius});break;case "reverse":n.reverse();
     223break}return f(n)}function f(o){function n(B){u=Math.min(B.x-B.radius,u);x=Math.max(B.x+B.radius,x);t=Math.min(B.y-B.radius,t);p=Math.max(B.y+B.radius,p)}function m(B,F){var H=B.n;B.n=F;F.p=B;F.n=H;H.p=F}function r(B,F){B.n=F;F.p=B}function s(B,F){var H=F.x-B.x,I=F.y-B.y;B=B.radius+F.radius;return B*B-H*H-I*I>0.0010}var u=Infinity,x=-Infinity,t=Infinity,p=-Infinity,v,w,y,z,C;v=o[0];v.x=-v.radius;v.y=0;n(v);if(o.length>1){w=o[1];w.x=w.radius;w.y=0;n(w);if(o.length>2){y=o[2];g(v,w,y);n(y);m(v,y);v.p=
     224y;m(y,w);w=v.n;for(var A=3;A<o.length;A++){g(v,w,y=o[A]);var D=0,G=1,E=1;for(z=w.n;z!=w;z=z.n,G++)if(s(z,y)){D=1;break}if(D==1)for(C=v.p;C!=z.p;C=C.p,E++)if(s(C,y)){if(E<G){D=-1;z=C}break}if(D==0){m(v,y);w=y;n(y)}else if(D>0){r(v,z);w=z;A--}else if(D<0){r(z,w);v=z;A--}}}}v=(u+x)/2;w=(t+p)/2;for(A=y=0;A<o.length;A++){z=o[A];z.x-=v;z.y-=w;y=Math.max(y,z.radius+Math.sqrt(z.x*z.x+z.y*z.y))}return y+b.spacing}function g(o,n,m){var r=n.radius+m.radius,s=o.radius+m.radius,u=n.x-o.x;n=n.y-o.y;var x=Math.sqrt(u*
     225u+n*n),t=(s*s+x*x-r*r)/(2*s*x);r=Math.acos(t);t=t*s;s=Math.sin(r)*s;u/=x;n/=x;m.x=o.x+t*u+s*n;m.y=o.y+t*n-s*u}function h(o,n,m,r){for(var s=o.firstChild;s;s=s.nextSibling){s.x+=o.x;s.y+=o.y;h(s,n,m,r)}o.x=n+r*o.x;o.y=m+r*o.y;o.radius*=r}if(!pv.Layout.Hierarchy.prototype.buildImplied.call(this,b)){var i=this,j=b.nodes,l=j[0];c(j);l.x=0;l.y=0;l.radius=d(l);j=this.width();var k=this.height(),q=1/Math.max(2*l.radius/j,2*l.radius/k);h(l,j/2,k/2,q)}};
     226pv.Layout.Force=function(){pv.Layout.Network.call(this);this.link.lineWidth(function(b,c){return Math.sqrt(c.linkValue)*1.5});this.label.textAlign("center")};
     227pv.Layout.Force.prototype=pv.extend(pv.Layout.Network).property("bound",Boolean).property("iterations",Number).property("dragConstant",Number).property("chargeConstant",Number).property("chargeMinDistance",Number).property("chargeMaxDistance",Number).property("chargeTheta",Number).property("springConstant",Number).property("springDamping",Number).property("springLength",Number);pv.Layout.Force.prototype.defaults=(new pv.Layout.Force).extend(pv.Layout.Network.prototype.defaults).dragConstant(0.1).chargeConstant(-40).chargeMinDistance(2).chargeMaxDistance(500).chargeTheta(0.9).springConstant(0.1).springDamping(0.3).springLength(20);
     228pv.Layout.Force.prototype.buildImplied=function(b){function c(q){return q.fix?1:q.vx*q.vx+q.vy*q.vy}if(pv.Layout.Network.prototype.buildImplied.call(this,b)){if(b=b.$force){b.next=this.binds.$force;this.binds.$force=b}}else{for(var d=this,f=b.nodes,g=b.links,h=b.iterations,i=b.width,j=b.height,l=0,k;l<f.length;l++){k=f[l];if(isNaN(k.x))k.x=i/2+40*Math.random()-20;if(isNaN(k.y))k.y=j/2+40*Math.random()-20}k=pv.simulation(f);k.force(pv.Force.drag(b.dragConstant));k.force(pv.Force.charge(b.chargeConstant).domain(b.chargeMinDistance,
     229b.chargeMaxDistance).theta(b.chargeTheta));k.force(pv.Force.spring(b.springConstant).damping(b.springDamping).length(b.springLength).links(g));k.constraint(pv.Constraint.position());b.bound&&k.constraint(pv.Constraint.bound().x(6,i-6).y(6,j-6));if(h==null){k.step();k.step();b.$force=this.binds.$force={next:this.binds.$force,nodes:f,min:1.0E-4*(g.length+1),sim:k};if(!this.$timer)this.$timer=setInterval(function(){for(var q=false,o=d.binds.$force;o;o=o.next)if(pv.max(o.nodes,c)>o.min){o.sim.step();
     230q=true}q&&d.render()},42)}else for(l=0;l<h;l++)k.step()}};pv.Layout.Cluster=function(){pv.Layout.Hierarchy.call(this);var b,c=this.buildImplied;this.buildImplied=function(d){c.call(this,d);b=/^(top|bottom)$/.test(d.orient)?"step-before":/^(left|right)$/.test(d.orient)?"step-after":"linear"};this.link.interpolate(function(){return b})};
     231pv.Layout.Cluster.prototype=pv.extend(pv.Layout.Hierarchy).property("group",Number).property("orient",String).property("innerRadius",Number).property("outerRadius",Number);pv.Layout.Cluster.prototype.defaults=(new pv.Layout.Cluster).extend(pv.Layout.Hierarchy.prototype.defaults).group(0).orient("top");
     232pv.Layout.Cluster.prototype.buildImplied=function(b){if(!pv.Layout.Hierarchy.prototype.buildImplied.call(this,b)){var c=b.nodes[0],d=b.group,f,g,h=0,i=0.5-d/2,j=undefined;c.visitAfter(function(l){if(l.firstChild)l.depth=1+pv.max(l.childNodes,function(k){return k.depth});else{if(d&&j!=l.parentNode){j=l.parentNode;h+=d}h++;l.depth=0}});f=1/h;g=1/c.depth;j=undefined;c.visitAfter(function(l){if(l.firstChild)l.breadth=pv.mean(l.childNodes,function(k){return k.breadth});else{if(d&&j!=l.parentNode){j=l.parentNode;
     233i+=d}l.breadth=f*i++}l.depth=1-l.depth*g});c.visitAfter(function(l){l.minBreadth=l.firstChild?l.firstChild.minBreadth:l.breadth-f/2;l.maxBreadth=l.firstChild?l.lastChild.maxBreadth:l.breadth+f/2});c.visitBefore(function(l){l.minDepth=l.parentNode?l.parentNode.maxDepth:0;l.maxDepth=l.parentNode?l.depth+c.depth:l.minDepth+2*c.depth});c.minDepth=-g;pv.Layout.Hierarchy.NodeLink.buildImplied.call(this,b)}};pv.Layout.Cluster.Fill=function(){pv.Layout.Cluster.call(this);pv.Layout.Hierarchy.Fill.constructor.call(this)};
     234pv.Layout.Cluster.Fill.prototype=pv.extend(pv.Layout.Cluster);pv.Layout.Cluster.Fill.prototype.buildImplied=function(b){pv.Layout.Cluster.prototype.buildImplied.call(this,b)||pv.Layout.Hierarchy.Fill.buildImplied.call(this,b)};pv.Layout.Partition=function(){pv.Layout.Hierarchy.call(this)};pv.Layout.Partition.prototype=pv.extend(pv.Layout.Hierarchy).property("order",String).property("orient",String).property("innerRadius",Number).property("outerRadius",Number);
     235pv.Layout.Partition.prototype.defaults=(new pv.Layout.Partition).extend(pv.Layout.Hierarchy.prototype.defaults).orient("top");pv.Layout.Partition.prototype.$size=function(){return 1};pv.Layout.Partition.prototype.size=function(b){this.$size=b;return this};
     236pv.Layout.Partition.prototype.buildImplied=function(b){if(!pv.Layout.Hierarchy.prototype.buildImplied.call(this,b)){var c=this,d=b.nodes[0],f=pv.Mark.stack,g=0;f.unshift(null);d.visitAfter(function(i,j){if(j>g)g=j;i.size=i.firstChild?pv.sum(i.childNodes,function(l){return l.size}):c.$size.apply(c,(f[0]=i,f))});f.shift();switch(b.order){case "ascending":d.sort(function(i,j){return i.size-j.size});break;case "descending":d.sort(function(i,j){return j.size-i.size});break}var h=1/g;d.minBreadth=0;d.breadth=
     2370.5;d.maxBreadth=1;d.visitBefore(function(i){for(var j=i.minBreadth,l=i.maxBreadth-j,k=i.firstChild;k;k=k.nextSibling){k.minBreadth=j;k.maxBreadth=j+=k.size/i.size*l;k.breadth=(j+k.minBreadth)/2}});d.visitAfter(function(i,j){i.minDepth=(j-1)*h;i.maxDepth=i.depth=j*h});pv.Layout.Hierarchy.NodeLink.buildImplied.call(this,b)}};pv.Layout.Partition.Fill=function(){pv.Layout.Partition.call(this);pv.Layout.Hierarchy.Fill.constructor.call(this)};pv.Layout.Partition.Fill.prototype=pv.extend(pv.Layout.Partition);
     238pv.Layout.Partition.Fill.prototype.buildImplied=function(b){pv.Layout.Partition.prototype.buildImplied.call(this,b)||pv.Layout.Hierarchy.Fill.buildImplied.call(this,b)};pv.Layout.Arc=function(){pv.Layout.Network.call(this);var b,c,d,f=this.buildImplied;this.buildImplied=function(g){f.call(this,g);c=g.directed;b=g.orient=="radial"?"linear":"polar";d=g.orient=="right"||g.orient=="top"};this.link.data(function(g){var h=g.sourceNode;g=g.targetNode;return d!=(c||h.breadth<g.breadth)?[h,g]:[g,h]}).interpolate(function(){return b})};
     239pv.Layout.Arc.prototype=pv.extend(pv.Layout.Network).property("orient",String).property("directed",Boolean);pv.Layout.Arc.prototype.defaults=(new pv.Layout.Arc).extend(pv.Layout.Network.prototype.defaults).orient("bottom");pv.Layout.Arc.prototype.sort=function(b){this.$sort=b;return this};
     240pv.Layout.Arc.prototype.buildImplied=function(b){function c(m){switch(h){case "top":return-Math.PI/2;case "bottom":return Math.PI/2;case "left":return Math.PI;case "right":return 0;case "radial":return(m-0.25)*2*Math.PI}}function d(m){switch(h){case "top":case "bottom":return m*l;case "left":return 0;case "right":return l;case "radial":return l/2+q*Math.cos(c(m))}}function f(m){switch(h){case "top":return 0;case "bottom":return k;case "left":case "right":return m*k;case "radial":return k/2+q*Math.sin(c(m))}}
     241if(!pv.Layout.Network.prototype.buildImplied.call(this,b)){var g=b.nodes,h=b.orient,i=this.$sort,j=pv.range(g.length),l=b.width,k=b.height,q=Math.min(l,k)/2;i&&j.sort(function(m,r){return i(g[m],g[r])});for(b=0;b<g.length;b++){var o=g[j[b]],n=o.breadth=(b+0.5)/g.length;o.x=d(n);o.y=f(n);o.midAngle=c(n)}}};
     242pv.Layout.Horizon=function(){pv.Layout.call(this);var b=this,c,d,f,g,h,i,j=this.buildImplied;this.buildImplied=function(l){j.call(this,l);c=l.bands;d=l.mode;f=Math.round((d=="color"?0.5:1)*l.height);g=l.backgroundStyle;h=pv.ramp(g,l.negativeStyle).domain(0,c);i=pv.ramp(g,l.positiveStyle).domain(0,c)};c=(new pv.Panel).data(function(){return pv.range(c*2)}).overflow("hidden").height(function(){return f}).top(function(l){return d=="color"?(l&1)*f:0}).fillStyle(function(l){return l?null:g});this.band=
     243(new pv.Mark).top(function(l,k){return d=="mirror"&&k&1?(k+1>>1)*f:null}).bottom(function(l,k){return d=="mirror"?k&1?null:(k+1>>1)*-f:(k&1||-1)*(k+1>>1)*f}).fillStyle(function(l,k){return(k&1?h:i)((k>>1)+1)});this.band.add=function(l){return b.add(pv.Panel).extend(c).add(l).extend(this)}};pv.Layout.Horizon.prototype=pv.extend(pv.Layout).property("bands",Number).property("mode",String).property("backgroundStyle",pv.color).property("positiveStyle",pv.color).property("negativeStyle",pv.color);
     244pv.Layout.Horizon.prototype.defaults=(new pv.Layout.Horizon).extend(pv.Layout.prototype.defaults).bands(2).mode("offset").backgroundStyle("white").positiveStyle("#1f77b4").negativeStyle("#d62728");
     245pv.Layout.Rollup=function(){pv.Layout.Network.call(this);var b=this,c,d,f=b.buildImplied;this.buildImplied=function(g){f.call(this,g);c=g.$rollup.nodes;d=g.$rollup.links};this.node.data(function(){return c}).size(function(g){return g.nodes.length*20});this.link.interpolate("polar").eccentricity(0.8);this.link.add=function(g){return b.add(pv.Panel).data(function(){return d}).add(g).extend(this)}};pv.Layout.Rollup.prototype=pv.extend(pv.Layout.Network).property("directed",Boolean);
     246pv.Layout.Rollup.prototype.x=function(b){this.$x=pv.functor(b);return this};pv.Layout.Rollup.prototype.y=function(b){this.$y=pv.functor(b);return this};
     247pv.Layout.Rollup.prototype.buildImplied=function(b){function c(r){return i[r]+","+j[r]}if(!pv.Layout.Network.prototype.buildImplied.call(this,b)){var d=b.nodes,f=b.links,g=b.directed,h=d.length,i=[],j=[],l=0,k={},q={},o=pv.Mark.stack,n={parent:this};o.unshift(null);for(var m=0;m<h;m++){n.index=m;o[0]=d[m];i[m]=this.$x.apply(n,o);j[m]=this.$y.apply(n,o)}o.shift();for(m=0;m<d.length;m++){h=c(m);o=k[h];if(!o){o=k[h]=pv.extend(d[m]);o.index=l++;o.x=i[m];o.y=j[m];o.nodes=[]}o.nodes.push(d[m])}for(m=0;m<
     248f.length;m++){l=f[m].targetNode;d=k[c(f[m].sourceNode.index)];l=k[c(l.index)];h=!g&&d.index>l.index?l.index+","+d.index:d.index+","+l.index;(o=q[h])||(o=q[h]={sourceNode:d,targetNode:l,linkValue:0,links:[]});o.links.push(f[m]);o.linkValue+=f[m].linkValue}b.$rollup={nodes:pv.values(k),links:pv.values(q)}}};
     249pv.Layout.Matrix=function(){pv.Layout.Network.call(this);var b,c,d,f,g,h=this.buildImplied;this.buildImplied=function(i){h.call(this,i);b=i.nodes.length;c=i.width/b;d=i.height/b;f=i.$matrix.labels;g=i.$matrix.pairs};this.link.data(function(){return g}).left(function(){return c*(this.index%b)}).top(function(){return d*Math.floor(this.index/b)}).width(function(){return c}).height(function(){return d}).lineWidth(1.5).strokeStyle("#fff").fillStyle(function(i){return i.linkValue?"#555":"#eee"}).parent=
     250this;delete this.link.add;this.label.data(function(){return f}).left(function(){return this.index&1?c*((this.index>>1)+0.5):null}).top(function(){return this.index&1?null:d*((this.index>>1)+0.5)}).textMargin(4).textAlign(function(){return this.index&1?"left":"right"}).textAngle(function(){return this.index&1?-Math.PI/2:0});delete this.node};pv.Layout.Matrix.prototype=pv.extend(pv.Layout.Network).property("directed",Boolean);pv.Layout.Matrix.prototype.sort=function(b){this.$sort=b;return this};
     251pv.Layout.Matrix.prototype.buildImplied=function(b){if(!pv.Layout.Network.prototype.buildImplied.call(this,b)){var c=b.nodes,d=b.links,f=this.$sort,g=c.length,h=pv.range(g),i=[],j=[],l={};b.$matrix={labels:i,pairs:j};f&&h.sort(function(m,r){return f(c[m],c[r])});for(var k=0;k<g;k++)for(var q=0;q<g;q++){var o=h[k],n=h[q];j.push(l[o+"."+n]={row:k,col:q,sourceNode:c[o],targetNode:c[n],linkValue:0})}for(k=0;k<g;k++){o=h[k];i.push(c[o],c[o])}for(k=0;k<d.length;k++){i=d[k];g=i.sourceNode.index;h=i.targetNode.index;
     252i=i.linkValue;l[g+"."+h].linkValue+=i;b.directed||(l[h+"."+g].linkValue+=i)}}};
     253pv.Layout.Bullet=function(){pv.Layout.call(this);var b=this,c=b.buildImplied,d=b.x=pv.Scale.linear(),f,g,h,i,j;this.buildImplied=function(l){c.call(this,j=l);f=l.orient;g=/^left|right$/.test(f);h=pv.ramp("#bbb","#eee").domain(0,Math.max(1,j.ranges.length-1));i=pv.ramp("steelblue","lightsteelblue").domain(0,Math.max(1,j.measures.length-1))};(this.range=new pv.Mark).data(function(){return j.ranges}).reverse(true).left(function(){return f=="left"?0:null}).top(function(){return f=="top"?0:null}).right(function(){return f==
     254"right"?0:null}).bottom(function(){return f=="bottom"?0:null}).width(function(l){return g?d(l):null}).height(function(l){return g?null:d(l)}).fillStyle(function(){return h(this.index)}).antialias(false).parent=b;(this.measure=new pv.Mark).extend(this.range).data(function(){return j.measures}).left(function(){return f=="left"?0:g?null:this.parent.width()/3.25}).top(function(){return f=="top"?0:g?this.parent.height()/3.25:null}).right(function(){return f=="right"?0:g?null:this.parent.width()/3.25}).bottom(function(){return f==
     255"bottom"?0:g?this.parent.height()/3.25:null}).fillStyle(function(){return i(this.index)}).parent=b;(this.marker=new pv.Mark).data(function(){return j.markers}).left(function(l){return f=="left"?d(l):g?null:this.parent.width()/2}).top(function(l){return f=="top"?d(l):g?this.parent.height()/2:null}).right(function(l){return f=="right"?d(l):null}).bottom(function(l){return f=="bottom"?d(l):null}).strokeStyle("black").shape("bar").angle(function(){return g?0:Math.PI/2}).parent=b;(this.tick=new pv.Mark).data(function(){return d.ticks(7)}).left(function(l){return f==
     256"left"?d(l):null}).top(function(l){return f=="top"?d(l):null}).right(function(l){return f=="right"?d(l):g?null:-6}).bottom(function(l){return f=="bottom"?d(l):g?-8:null}).height(function(){return g?6:null}).width(function(){return g?null:6}).parent=b};pv.Layout.Bullet.prototype=pv.extend(pv.Layout).property("orient",String).property("ranges").property("markers").property("measures").property("maximum",Number);pv.Layout.Bullet.prototype.defaults=(new pv.Layout.Bullet).extend(pv.Layout.prototype.defaults).orient("left").ranges([]).markers([]).measures([]);
     257pv.Layout.Bullet.prototype.buildImplied=function(b){pv.Layout.prototype.buildImplied.call(this,b);var c=this.parent[/^left|right$/.test(b.orient)?"width":"height"]();b.maximum=b.maximum||pv.max([].concat(b.ranges,b.markers,b.measures));this.x.domain(0,b.maximum).range(0,c)};pv.Behavior={};
     258pv.Behavior.drag=function(){function b(l){g=this.index;f=this.scene;var k=this.mouse();i=((h=l).fix=pv.vector(l.x,l.y)).minus(k);j={x:this.parent.width()-(l.dx||0),y:this.parent.height()-(l.dy||0)};f.mark.context(f,g,function(){this.render()});pv.Mark.dispatch("dragstart",f,g)}function c(){if(f){f.mark.context(f,g,function(){var l=this.mouse();h.x=h.fix.x=Math.max(0,Math.min(i.x+l.x,j.x));h.y=h.fix.y=Math.max(0,Math.min(i.y+l.y,j.y));this.render()});pv.Mark.dispatch("drag",f,g)}}function d(){if(f){h.fix=
     259null;f.mark.context(f,g,function(){this.render()});pv.Mark.dispatch("dragend",f,g);f=null}}var f,g,h,i,j;pv.listen(window,"mousemove",c);pv.listen(window,"mouseup",d);return b};
     260pv.Behavior.point=function(b){function c(k,q){k=k[q];q={cost:Infinity};for(var o=0,n=k.visible&&k.children.length;o<n;o++){var m=k.children[o],r=m.mark,s;if(r.type=="panel"){r.scene=m;for(var u=0,x=m.length;u<x;u++){r.index=u;s=c(m,u);if(s.cost<q.cost)q=s}delete r.scene;delete r.index}else if(r.$handlers.point){r=r.mouse();u=0;for(x=m.length;u<x;u++){var t=m[u];s=r.x-t.left-(t.width||0)/2;t=r.y-t.top-(t.height||0)/2;var p=i*s*s+j*t*t;if(p<q.cost){q.distance=s*s+t*t;q.cost=p;q.scene=m;q.index=u}}}}return q}
     261function d(){var k=c(this.scene,this.index);if(k.cost==Infinity||k.distance>l)k=null;if(g){if(k&&g.scene==k.scene&&g.index==k.index)return;pv.Mark.dispatch("unpoint",g.scene,g.index)}if(g=k){pv.Mark.dispatch("point",k.scene,k.index);pv.listen(this.root.canvas(),"mouseout",f)}}function f(k){if(g&&!pv.ancestor(this,k.relatedTarget)){pv.Mark.dispatch("unpoint",g.scene,g.index);g=null}}var g,h=null,i=1,j=1,l=arguments.length?b*b:900;d.collapse=function(k){if(arguments.length){h=String(k);switch(h){case "y":i=
     2621;j=0;break;case "x":i=0;j=1;break;default:j=i=1;break}return d}return h};return d};
     263pv.Behavior.select=function(){function b(j){g=this.index;f=this.scene;i=this.mouse();h=j;h.x=i.x;h.y=i.y;h.dx=h.dy=0;pv.Mark.dispatch("selectstart",f,g)}function c(){if(f){f.mark.context(f,g,function(){var j=this.mouse();h.x=Math.max(0,Math.min(i.x,j.x));h.y=Math.max(0,Math.min(i.y,j.y));h.dx=Math.min(this.width(),Math.max(j.x,i.x))-h.x;h.dy=Math.min(this.height(),Math.max(j.y,i.y))-h.y;this.render()});pv.Mark.dispatch("select",f,g)}}function d(){if(f){pv.Mark.dispatch("selectend",f,g);f=null}}var f,
     264g,h,i;pv.listen(window,"mousemove",c);pv.listen(window,"mouseup",d);return b};
     265pv.Behavior.resize=function(b){function c(l){h=this.index;g=this.scene;j=this.mouse();i=l;switch(b){case "left":j.x=i.x+i.dx;break;case "right":j.x=i.x;break;case "top":j.y=i.y+i.dy;break;case "bottom":j.y=i.y;break}pv.Mark.dispatch("resizestart",g,h)}function d(){if(g){g.mark.context(g,h,function(){var l=this.mouse();i.x=Math.max(0,Math.min(j.x,l.x));i.y=Math.max(0,Math.min(j.y,l.y));i.dx=Math.min(this.parent.width(),Math.max(l.x,j.x))-i.x;i.dy=Math.min(this.parent.height(),Math.max(l.y,j.y))-i.y;
     266this.render()});pv.Mark.dispatch("resize",g,h)}}function f(){if(g){pv.Mark.dispatch("resizeend",g,h);g=null}}var g,h,i,j;pv.listen(window,"mousemove",d);pv.listen(window,"mouseup",f);return c};
     267pv.Behavior.pan=function(){function b(){g=this.index;f=this.scene;i=pv.vector(pv.event.pageX,pv.event.pageY);h=this.transform();j=1/(h.k*this.scale);if(l)l={x:(1-h.k)*this.width(),y:(1-h.k)*this.height()}}function c(){if(f){f.mark.context(f,g,function(){var k=h.translate((pv.event.pageX-i.x)*j,(pv.event.pageY-i.y)*j);if(l){k.x=Math.max(l.x,Math.min(0,k.x));k.y=Math.max(l.y,Math.min(0,k.y))}this.transform(k).render()});pv.Mark.dispatch("pan",f,g)}}function d(){f=null}var f,g,h,i,j,l;b.bound=function(k){if(arguments.length){l=
     268Boolean(k);return this}return Boolean(l)};pv.listen(window,"mousemove",c);pv.listen(window,"mouseup",d);return b};
     269pv.Behavior.zoom=function(b){function c(){var f=this.mouse(),g=pv.event.wheel*b;f=this.transform().translate(f.x,f.y).scale(g<0?1E3/(1E3-g):(1E3+g)/1E3).translate(-f.x,-f.y);if(d){f.k=Math.max(1,f.k);f.x=Math.max((1-f.k)*this.width(),Math.min(0,f.x));f.y=Math.max((1-f.k)*this.height(),Math.min(0,f.y))}this.transform(f).render();pv.Mark.dispatch("zoom",this.scene,this.index)}var d;arguments.length||(b=1/48);c.bound=function(f){if(arguments.length){d=Boolean(f);return this}return Boolean(d)};return c};
     270pv.Geo=function(){};
     271pv.Geo.projections={mercator:{project:function(b){return{x:b.lng/180,y:b.lat>85?1:b.lat<-85?-1:Math.log(Math.tan(Math.PI/4+pv.radians(b.lat)/2))/Math.PI}},invert:function(b){return{lng:b.x*180,lat:pv.degrees(2*Math.atan(Math.exp(b.y*Math.PI))-Math.PI/2)}}},"gall-peters":{project:function(b){return{x:b.lng/180,y:Math.sin(pv.radians(b.lat))}},invert:function(b){return{lng:b.x*180,lat:pv.degrees(Math.asin(b.y))}}},sinusoidal:{project:function(b){return{x:pv.radians(b.lng)*Math.cos(pv.radians(b.lat))/Math.PI,
     272y:b.lat/90}},invert:function(b){return{lng:pv.degrees(b.x*Math.PI/Math.cos(b.y*Math.PI/2)),lat:b.y*90}}},aitoff:{project:function(b){var c=pv.radians(b.lng);b=pv.radians(b.lat);var d=Math.acos(Math.cos(b)*Math.cos(c/2));return{x:2*(d?Math.cos(b)*Math.sin(c/2)*d/Math.sin(d):0)/Math.PI,y:2*(d?Math.sin(b)*d/Math.sin(d):0)/Math.PI}},invert:function(b){var c=b.y*Math.PI/2;return{lng:pv.degrees(b.x*Math.PI/2/Math.cos(c)),lat:pv.degrees(c)}}},hammer:{project:function(b){var c=pv.radians(b.lng);b=pv.radians(b.lat);
     273var d=Math.sqrt(1+Math.cos(b)*Math.cos(c/2));return{x:2*Math.SQRT2*Math.cos(b)*Math.sin(c/2)/d/3,y:Math.SQRT2*Math.sin(b)/d/1.5}},invert:function(b){var c=b.x*3;b=b.y*1.5;var d=Math.sqrt(1-c*c/16-b*b/4);return{lng:pv.degrees(2*Math.atan2(d*c,2*(2*d*d-1))),lat:pv.degrees(Math.asin(d*b))}}},identity:{project:function(b){return{x:b.lng/180,y:b.lat/90}},invert:function(b){return{lng:b.x*180,lat:b.y*90}}}};
     274pv.Geo.scale=function(b){function c(m){if(!o||m.lng!=o.lng||m.lat!=o.lat){o=m;m=d(m);n={x:l(m.x),y:k(m.y)}}return n}function d(m){return j.project({lng:m.lng-q.lng,lat:m.lat})}function f(m){m=j.invert(m);m.lng+=q.lng;return m}var g={x:0,y:0},h={x:1,y:1},i=[],j=pv.Geo.projections.identity,l=pv.Scale.linear(-1,1).range(0,1),k=pv.Scale.linear(-1,1).range(1,0),q={lng:0,lat:0},o,n;c.x=function(m){return c(m).x};c.y=function(m){return c(m).y};c.ticks={lng:function(m){var r;if(i.length>1){var s=pv.Scale.linear();
     275if(m==undefined)m=10;r=s.domain(i,function(u){return u.lat}).ticks(m);m=s.domain(i,function(u){return u.lng}).ticks(m)}else{r=pv.range(-80,81,10);m=pv.range(-180,181,10)}return m.map(function(u){return r.map(function(x){return{lat:x,lng:u}})})},lat:function(m){return pv.transpose(c.ticks.lng(m))}};c.invert=function(m){return f({x:l.invert(m.x),y:k.invert(m.y)})};c.domain=function(m,r){if(arguments.length){i=m instanceof Array?arguments.length>1?pv.map(m,r):m:Array.prototype.slice.call(arguments);
     276if(i.length>1){var s=i.map(function(x){return x.lng}),u=i.map(function(x){return x.lat});q={lng:(pv.max(s)+pv.min(s))/2,lat:(pv.max(u)+pv.min(u))/2};s=i.map(d);l.domain(s,function(x){return x.x});k.domain(s,function(x){return x.y})}else{q={lng:0,lat:0};l.domain(-1,1);k.domain(-1,1)}o=null;return this}return i};c.range=function(m,r){if(arguments.length){if(typeof m=="object"){g={x:Number(m.x),y:Number(m.y)};h={x:Number(r.x),y:Number(r.y)}}else{g={x:0,y:0};h={x:Number(m),y:Number(r)}}l.range(g.x,h.x);
     277k.range(h.y,g.y);o=null;return this}return[g,h]};c.projection=function(m){if(arguments.length){j=typeof m=="string"?pv.Geo.projections[m]||pv.Geo.projections.identity:m;return this.domain(i)}return m};c.by=function(m){function r(){return c(m.apply(this,arguments))}for(var s in c)r[s]=c[s];return r};arguments.length&&c.projection(b);return c};
  • src/allmydata/web/root.py

    diff --git a/src/allmydata/web/root.py b/src/allmydata/web/root.py
    index 3af15d9..fe15a8f 100644
    a b class Root(rend.Page): 
    164164        self.child_named = FileHandler(client)
    165165        self.child_status = status.Status(client.get_history())
    166166        self.child_statistics = status.Statistics(client.stats_provider)
     167        def f(name):
     168            return nevow_File(resource_filename('allmydata.web', name))
     169        self.putChild("jquery.js", f("jquery.js"))
     170        self.putChild("download_status_timeline.js", f("download_status_timeline.js"))
     171        self.putChild("protovis-r3.2.js", f("protovis-r3.2.js"))
    167172
    168173    def child_helper_status(self, ctx):
    169174        # the Helper isn't attached until after the Tub starts, so this child
  • src/allmydata/web/status.py

    diff --git a/src/allmydata/web/status.py b/src/allmydata/web/status.py
    index 9f96f1d..5d28bbe 100644
    a b class DownloadResultsRendererMixin(RateAndTimeMixin): 
    331331        d.addCallback(_render)
    332332        return d
    333333
     334def tfmt(when):
     335    #return when * 1000.0 # stupid JS timestamps
     336    return "%.6f" % when
     337    # the timeline markers represent UTC. To make these events line up, we
     338    # must supply the "Z" suffix.
     339    #return "%.2f" % (1000*when)
     340    t = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(when))
     341    fract = "%.6f" % (when % 1.0)
     342    if fract.startswith("0."):
     343        fract = fract[2:]
     344    return t+"."+fract+"Z"
     345
    334346class DownloadStatusPage(DownloadResultsRendererMixin, rend.Page):
    335347    docFactory = getxmlfile("download-status.xhtml")
    336348
    class DownloadStatusPage(DownloadResultsRendererMixin, rend.Page): 
    338350        rend.Page.__init__(self, data)
    339351        self.download_status = data
    340352
     353    def child_timeline(self, ctx):
     354        return DownloadStatusTimelinePage(self.download_status)
     355
    341356    def download_results(self):
    342357        return defer.maybeDeferred(self.download_status.get_results)
    343358
    344359    def relative_time(self, t):
    345360        if t is None:
    346361            return t
    347         if self.download_status.started is not None:
    348             return t - self.download_status.started
     362        if self.download_status.first_timestamp is not None:
     363            return t - self.download_status.first_timestamp
    349364        return t
    350365    def short_relative_time(self, t):
    351366        t = self.relative_time(t)
    class DownloadStatusPage(DownloadResultsRendererMixin, rend.Page): 
    353368            return ""
    354369        return "+%.6fs" % t
    355370
    356     def renderHTTP(self, ctx):
    357         req = inevow.IRequest(ctx)
    358         t = get_arg(req, "t")
    359         if t == "json":
    360             return self.json(req)
    361         return rend.Page.renderHTTP(self, ctx)
    362 
    363     def json(self, req):
    364         req.setHeader("content-type", "text/plain")
    365         data = {}
    366         dyhb_events = []
    367         for serverid,requests in self.download_status.dyhb_requests.iteritems():
    368             for req in requests:
    369                 dyhb_events.append( (base32.b2a(serverid),) + req )
    370         dyhb_events.sort(key=lambda req: req[1])
    371         data["dyhb"] = dyhb_events
    372         request_events = []
    373         for serverid,requests in self.download_status.requests.iteritems():
    374             for req in requests:
    375                 request_events.append( (base32.b2a(serverid),) + req )
    376         request_events.sort(key=lambda req: (req[4],req[1]))
    377         data["requests"] = request_events
    378         data["segment"] = self.download_status.segment_events
    379         data["read"] = self.download_status.read_events
     371    def _find_overlap(self, events, start_key, end_key):
     372        # given a list of event dicts, return a new list in which each event
     373        # has an extra "row" key (an int, starting at 0). This is a hint to
     374        # our JS frontend about how to overlap the parts of the graph it is
     375        # drawing.
     376
     377        # we must always make a copy, since we're going to be adding "row"
     378        # keys and don't want to change the original objects. If we're
     379        # stringifying serverids, we'll also be changing the serverid keys.
     380        new_events = []
     381        rows = []
     382        for ev in events:
     383            ev = ev.copy()
     384            if "serverid" in ev:
     385                ev["serverid"] = base32.b2a(ev["serverid"])
     386            # find an empty slot in the rows
     387            free_slot = None
     388            for row,finished in enumerate(rows):
     389                if finished is not None:
     390                    if ev[start_key] > finished:
     391                        free_slot = row
     392                        break
     393            if free_slot is None:
     394                free_slot = len(rows)
     395                rows.append(ev[end_key])
     396            else:
     397                rows[free_slot] = ev[end_key]
     398            ev["row"] = free_slot
     399            new_events.append(ev)
     400        return new_events
     401
     402    def _find_overlap_requests(self, events):
     403        """We compute a three-element 'row tuple' for each event: (serverid,
     404        shnum, row). All elements are ints. The first is a mapping from
     405        serverid to group number, the second is a mapping from shnum to
     406        subgroup number. The third is a row within the subgroup.
     407
     408        We also return a list of lists of rowcounts, so renderers can decide
     409        how much vertical space to give to each row.
     410        """
     411
     412        serverid_to_group = {}
     413        groupnum_to_rows = {} # maps groupnum to a table of rows. Each table
     414                              # is a list with an element for each row number
     415                              # (int starting from 0) that contains a
     416                              # finish_time, indicating that the row is empty
     417                              # beyond that time. If finish_time is None, it
     418                              # indicate a response that has not yet
     419                              # completed, so the row cannot be reused.
     420        new_events = []
     421        for ev in events:
     422            # DownloadStatus promises to give us events in temporal order
     423            ev = ev.copy()
     424            ev["serverid"] = base32.b2a(ev["serverid"])
     425            if ev["serverid"] not in serverid_to_group:
     426                groupnum = len(serverid_to_group)
     427                serverid_to_group[ev["serverid"]] = groupnum
     428            groupnum = serverid_to_group[ev["serverid"]]
     429            if groupnum not in groupnum_to_rows:
     430                groupnum_to_rows[groupnum] = []
     431            rows = groupnum_to_rows[groupnum]
     432            # find an empty slot in the rows
     433            free_slot = None
     434            for row,finished in enumerate(rows):
     435                if finished is not None:
     436                    if ev["start_time"] > finished:
     437                        free_slot = row
     438                        break
     439            if free_slot is None:
     440                free_slot = len(rows)
     441                rows.append(ev["finish_time"])
     442            else:
     443                rows[free_slot] = ev["finish_time"]
     444            ev["row"] = (groupnum, free_slot)
     445            new_events.append(ev)
     446        # maybe also return serverid_to_group, groupnum_to_rows, and some
     447        # indication of the highest finish_time
     448        #
     449        # actually, return the highest rownum for each groupnum
     450        highest_rownums = [len(groupnum_to_rows[groupnum])
     451                           for groupnum in range(len(serverid_to_group))]
     452        return new_events, highest_rownums
     453
     454    def child_timeline_parameters(self, ctx):
     455        ds = self.download_status
     456        d = { "start": tfmt(ds.started),
     457              "end": tfmt(ds.started+2.0),
     458              }
     459        return simplejson.dumps(d, indent=1) + "\n"
     460
     461    def child_event_json(self, ctx):
     462        inevow.IRequest(ctx).setHeader("content-type", "text/plain")
     463        data = { } # this will be returned to the GET
     464        ds = self.download_status
     465
     466        data["read"] = self._find_overlap(ds.read_events,
     467                                          "start_time", "finish_time")
     468        data["segment"] = self._find_overlap(ds.segment_events,
     469                                             "start_time", "finish_time")
     470        data["dyhb"] = self._find_overlap(ds.dyhb_requests,
     471                                          "start_time", "finish_time")
     472        data["block"],data["block_rownums"] = self._find_overlap_requests(ds.block_requests)
     473
     474        servernums = {}
     475        serverid_strings = {}
     476        for d_ev in data["dyhb"]:
     477            if d_ev["serverid"] not in servernums:
     478                servernum = len(servernums)
     479                servernums[d_ev["serverid"]] = servernum
     480                #title= "%s: %s" % ( ",".join([str(shnum) for shnum in shnums]))
     481                serverid_strings[servernum] = d_ev["serverid"][:4]
     482        data["server_info"] = dict([(serverid, {"num": servernums[serverid],
     483                                                "color": self.color(base32.a2b(serverid)),
     484                                                "short": serverid_strings[servernum],
     485                                                })
     486                                   for serverid in servernums.keys()])
     487        data["num_serverids"] = len(serverid_strings)
     488        data["serverids"] = serverid_strings;
     489        data["bounds"] = {"min": ds.first_timestamp,
     490                          "max": ds.last_timestamp,
     491                          }
     492        # for testing
     493        ## data["bounds"]["max"] = tfmt(max([d_ev["finish_time"]
     494        ##                                   for d_ev in data["dyhb"]
     495        ##                                   if d_ev["finish_time"] is not None]
     496        ##                                  ))
    380497        return simplejson.dumps(data, indent=1) + "\n"
    381498
     499    def render_timeline_link(self, ctx, data):
     500        from nevow import url
     501        return T.a(href=url.URL.fromContext(ctx).child("timeline"))["timeline"]
     502
     503    def _rate_and_time(self, bytes, seconds):
     504        time_s = self.render_time(None, seconds)
     505        if seconds != 0:
     506            rate = self.render_rate(None, 1.0 * bytes / seconds)
     507            return T.span(title=rate)[time_s]
     508        return T.span[time_s]
     509
    382510    def render_events(self, ctx, data):
    383511        if not self.download_status.storage_index:
    384512            return
    class DownloadStatusPage(DownloadResultsRendererMixin, rend.Page): 
    388516        t = T.table(class_="status-download-events")
    389517        t[T.tr[T.td["serverid"], T.td["sent"], T.td["received"],
    390518               T.td["shnums"], T.td["RTT"]]]
    391         dyhb_events = []
    392         for serverid,requests in self.download_status.dyhb_requests.iteritems():
    393             for req in requests:
    394                 dyhb_events.append( (serverid,) + req )
    395         dyhb_events.sort(key=lambda req: req[1])
    396         for d_ev in dyhb_events:
    397             (serverid, sent, shnums, received) = d_ev
     519        for d_ev in self.download_status.dyhb_requests:
     520            serverid = d_ev["serverid"]
     521            sent = d_ev["start_time"]
     522            shnums = d_ev["response_shnums"]
     523            received = d_ev["finish_time"]
    398524            serverid_s = idlib.shortnodeid_b2a(serverid)
    399525            rtt = None
    400526            if received is not None:
    class DownloadStatusPage(DownloadResultsRendererMixin, rend.Page): 
    413539               T.td["time"], T.td["decrypttime"], T.td["pausedtime"],
    414540               T.td["speed"]]]
    415541        for r_ev in self.download_status.read_events:
    416             (start, length, requesttime, finishtime, bytes, decrypt, paused) = r_ev
    417             if finishtime is not None:
    418                 rtt = finishtime - requesttime - paused
     542            start = r_ev["start"]
     543            length = r_ev["length"]
     544            bytes = r_ev["bytes_returned"]
     545            decrypt_time = ""
     546            if bytes:
     547                decrypt_time = self._rate_and_time(bytes, r_ev["decrypt_time"])
     548            speed, rtt = "",""
     549            if r_ev["finish_time"] is not None:
     550                rtt = r_ev["finish_time"] - r_ev["start_time"] - r_ev["paused_time"]
    419551                speed = self.render_rate(None, compute_rate(bytes, rtt))
    420552                rtt = self.render_time(None, rtt)
    421                 decrypt = self.render_time(None, decrypt)
    422                 paused = self.render_time(None, paused)
    423             else:
    424                 speed, rtt, decrypt, paused = "","","",""
     553            paused = self.render_time(None, r_ev["paused_time"])
     554
    425555            t[T.tr[T.td["[%d:+%d]" % (start, length)],
    426                    T.td[srt(requesttime)], T.td[srt(finishtime)],
    427                    T.td[bytes], T.td[rtt], T.td[decrypt], T.td[paused],
     556                   T.td[srt(r_ev["start_time"])], T.td[srt(r_ev["finish_time"])],
     557                   T.td[bytes], T.td[rtt],
     558                   T.td[decrypt_time], T.td[paused],
    428559                   T.td[speed],
    429560                   ]]
    430561        l["Read Events:", t]
    431562
    432563        t = T.table(class_="status-download-events")
    433         t[T.tr[T.td["type"], T.td["segnum"], T.td["when"], T.td["range"],
     564        t[T.tr[T.td["segnum"], T.td["start"], T.td["active"], T.td["finish"],
     565               T.td["range"],
    434566               T.td["decodetime"], T.td["segtime"], T.td["speed"]]]
    435         reqtime = (None, None)
    436567        for s_ev in self.download_status.segment_events:
    437             (etype, segnum, when, segstart, seglen, decodetime) = s_ev
    438             if etype == "request":
    439                 t[T.tr[T.td["request"], T.td["seg%d" % segnum],
    440                        T.td[srt(when)]]]
    441                 reqtime = (segnum, when)
    442             elif etype == "delivery":
    443                 if reqtime[0] == segnum:
    444                     segtime = when - reqtime[1]
     568            range_s = ""
     569            segtime_s = ""
     570            speed = ""
     571            decode_time = ""
     572            if s_ev["finish_time"] is not None:
     573                if s_ev["success"]:
     574                    segtime = s_ev["finish_time"] - s_ev["active_time"]
     575                    segtime_s = self.render_time(None, segtime)
     576                    seglen = s_ev["segment_length"]
     577                    range_s = "[%d:+%d]" % (s_ev["segment_start"], seglen)
    445578                    speed = self.render_rate(None, compute_rate(seglen, segtime))
    446                     segtime = self.render_time(None, segtime)
     579                    decode_time = self._rate_and_time(seglen, s_ev["decode_time"])
    447580                else:
    448                     segtime, speed = "", ""
    449                 t[T.tr[T.td["delivery"], T.td["seg%d" % segnum],
    450                        T.td[srt(when)],
    451                        T.td["[%d:+%d]" % (segstart, seglen)],
    452                        T.td[self.render_time(None,decodetime)],
    453                        T.td[segtime], T.td[speed]]]
    454             elif etype == "error":
    455                 t[T.tr[T.td["error"], T.td["seg%d" % segnum]]]
     581                    # error
     582                    range_s = "error"
     583            else:
     584                # not finished yet
     585                pass
     586
     587            t[T.tr[T.td["seg%d" % s_ev["segment_number"]],
     588                   T.td[srt(s_ev["start_time"])],
     589                   T.td[srt(s_ev["active_time"])],
     590                   T.td[srt(s_ev["finish_time"])],
     591                   T.td[range_s],
     592                   T.td[decode_time],
     593                   T.td[segtime_s], T.td[speed]]]
    456594        l["Segment Events:", t]
    457595
    458596        t = T.table(border="1")
    459597        t[T.tr[T.td["serverid"], T.td["shnum"], T.td["range"],
    460                T.td["txtime"], T.td["rxtime"], T.td["received"], T.td["RTT"]]]
    461         reqtime = (None, None)
    462         request_events = []
    463         for serverid,requests in self.download_status.requests.iteritems():
    464             for req in requests:
    465                 request_events.append( (serverid,) + req )
    466         request_events.sort(key=lambda req: (req[4],req[1]))
    467         for r_ev in request_events:
    468             (peerid, shnum, start, length, sent, receivedlen, received) = r_ev
     598               T.td["txtime"], T.td["rxtime"],
     599               T.td["received"], T.td["RTT"]]]
     600        for r_ev in self.download_status.block_requests:
    469601            rtt = None
    470             if received is not None:
    471                 rtt = received - sent
    472             peerid_s = idlib.shortnodeid_b2a(peerid)
    473             t[T.tr(style="background: %s" % self.color(peerid))[
    474                 T.td[peerid_s], T.td[shnum],
    475                 T.td["[%d:+%d]" % (start, length)],
    476                 T.td[srt(sent)], T.td[srt(received)], T.td[receivedlen],
     602            if r_ev["finish_time"] is not None:
     603                rtt = r_ev["finish_time"] - r_ev["start_time"]
     604            serverid_s = idlib.shortnodeid_b2a(r_ev["serverid"])
     605            t[T.tr(style="background: %s" % self.color(r_ev["serverid"]))[
     606                T.td[serverid_s], T.td[r_ev["shnum"]],
     607                T.td["[%d:+%d]" % (r_ev["start"], r_ev["length"])],
     608                T.td[srt(r_ev["start_time"])], T.td[srt(r_ev["finish_time"])],
     609                T.td[r_ev["response_length"] or ""],
    477610                T.td[self.render_time(None, rtt)],
    478611                ]]
    479612        l["Requests:", t]
    class DownloadStatusPage(DownloadResultsRendererMixin, rend.Page): 
    524657    def render_status(self, ctx, data):
    525658        return data.get_status()
    526659
     660class DownloadStatusTimelinePage(rend.Page):
     661    docFactory = getxmlfile("download-status-timeline.xhtml")
     662
     663    def render_started(self, ctx, data):
     664        TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
     665        started_s = time.strftime(TIME_FORMAT,
     666                                  time.localtime(data.get_started()))
     667        return started_s + " (%s)" % data.get_started()
     668
     669    def render_si(self, ctx, data):
     670        si_s = base32.b2a_or_none(data.get_storage_index())
     671        if si_s is None:
     672            si_s = "(None)"
     673        return si_s
     674
     675    def render_helper(self, ctx, data):
     676        return {True: "Yes",
     677                False: "No"}[data.using_helper()]
     678
     679    def render_total_size(self, ctx, data):
     680        size = data.get_size()
     681        if size is None:
     682            return "(unknown)"
     683        return size
     684
     685    def render_progress(self, ctx, data):
     686        progress = data.get_progress()
     687        # TODO: make an ascii-art bar
     688        return "%.1f%%" % (100.0 * progress)
     689
     690    def render_status(self, ctx, data):
     691        return data.get_status()
     692
    527693class RetrieveStatusPage(rend.Page, RateAndTimeMixin):
    528694    docFactory = getxmlfile("retrieve-status.xhtml")
    529695
  • new file viz-notes.org

    diff --git a/viz-notes.org b/viz-notes.org
    new file mode 100644
    index 0000000..1ae033d
    - +  
     1
     2* layout
     3** read() requests
     4   - possibly overlapping: code should find minimum number of rows
     5** segment events (request/delivery/error)
     6   - also possibly overlapping, use minimum rows
     7   - might be nice to show which is active at any given time
     8** DYHB queries+responses
     9   - completely overlapping, use exactly one row per server
     10** server block-read requests (send/receive/error)
     11   - use one cluster per server
     12   - lots of overlapping reads
     13   - within a cluster, use lowest available row
     14   - 3-tuple Y axis: (serverid, shnum, overlaps)
     15** ideally, tahoe should serve raw data and let JS do the sorting
     16   - but my JS is not that good
     17   - maybe just provide a hint: include a row number in each event, which
     18     tells you how to overlap them
     19   - multiple parts, joined with "-", use as JS dict keys
     20
     21* a graph that shows Y=segment-offset, X=start/finish time
     22
     23* a scatter plot that shows segnum-tree-height versus finish-start
     24  - a local download showed segments that needed a lot of hash nodes taking
     25    way more time than others