Ticket #1200: viz-2.diff

File viz-2.diff, 815.8 KB (added by warner, at 2010-12-01T16:45:27Z)

dated patch to latest trunk, includes detailed "misc events". Not tested. For experimentation only.

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

    diff --git a/src/allmydata/immutable/downloader/finder.py b/src/allmydata/immutable/downloader/finder.py
    index 4816ccd..3309286 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 04482e6..f0a73eb 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."""
     129        uses an independent offset= value.
     130        """
    130131        # for concurrent operations: each gets its own Segmentation manager
    131132        if size is None:
    132133            size = self._verifycap.size
    class DownloadNode: 
    148149            read_ev.finished(now())
    149150            # no data, so no producer, so no register/unregisterProducer
    150151            return defer.succeed(consumer)
     152
     153        # for concurrent operations, each read() gets its own Segmentation
     154        # manager
    151155        s = Segmentation(self, offset, size, consumer, read_ev, lp)
     156
    152157        # this raises an interesting question: what segments to fetch? if
    153158        # offset=0, always fetch the first segment, and then allow
    154159        # Segmentation to be responsible for pulling the subsequent ones if
    class DownloadNode: 
    186191                     si=base32.b2a(self._verifycap.storage_index)[:8],
    187192                     segnum=segnum,
    188193                     level=log.OPERATIONAL, parent=logparent, umid="UKFjDQ")
    189         self._download_status.add_segment_request(segnum, now())
     194        seg_ev = self._download_status.add_segment_request(segnum, now())
    190195        d = defer.Deferred()
    191196        c = Cancel(self._cancel_request)
    192         self._segment_requests.append( (segnum, d, c, lp) )
     197        self._segment_requests.append( (segnum, d, c, seg_ev, lp) )
    193198        self._start_new_segment()
    194199        return (d, c)
    195200
    class DownloadNode: 
    213218
    214219    def _start_new_segment(self):
    215220        if self._active_segment is None and self._segment_requests:
    216             segnum = self._segment_requests[0][0]
     221            (segnum, d, c, seg_ev, lp) = self._segment_requests[0]
    217222            k = self._verifycap.needed_shares
    218             lp = self._segment_requests[0][3]
    219223            log.msg(format="%(node)s._start_new_segment: segnum=%(segnum)d",
    220224                    node=repr(self), segnum=segnum,
    221225                    level=log.NOISY, parent=lp, umid="wAlnHQ")
    222226            self._active_segment = fetcher = SegmentFetcher(self, segnum, k, lp)
     227            seg_ev.activate(now())
    223228            active_shares = [s for s in self._shares if s.is_alive()]
    224229            fetcher.add_shares(active_shares) # this triggers the loop
    225230
    class DownloadNode: 
    383388    def fetch_failed(self, sf, f):
    384389        assert sf is self._active_segment
    385390        # deliver error upwards
    386         for (d,c) in self._extract_requests(sf.segnum):
     391        for (d,c,seg_ev) in self._extract_requests(sf.segnum):
     392            seg_ev.error(now())
    387393            eventually(self._deliver, d, c, f)
    388394        self._active_segment = None
    389395        self._start_new_segment()
    390396
    391397    def process_blocks(self, segnum, blocks):
     398        start = now()
    392399        d = defer.maybeDeferred(self._decode_blocks, segnum, blocks)
    393400        d.addCallback(self._check_ciphertext_hash, segnum)
    394401        def _deliver(result):
    395             ds = self._download_status
    396             if isinstance(result, Failure):
    397                 ds.add_segment_error(segnum, now())
    398             else:
    399                 (offset, segment, decodetime) = result
    400                 ds.add_segment_delivery(segnum, now(),
    401                                         offset, len(segment), decodetime)
    402402            log.msg(format="delivering segment(%(segnum)d)",
    403403                    segnum=segnum,
    404404                    level=log.OPERATIONAL, parent=self._lp,
    405405                    umid="j60Ojg")
    406             for (d,c) in self._extract_requests(segnum):
    407                 eventually(self._deliver, d, c, result)
     406            when = now()
     407            if isinstance(result, Failure):
     408                # this catches failures in decode or ciphertext hash
     409                for (d,c,seg_ev) in self._extract_requests(segnum):
     410                    seg_ev.error(when)
     411                    eventually(self._deliver, d, c, result)
     412            else:
     413                (offset, segment, decodetime) = result
     414                for (d,c,seg_ev) in self._extract_requests(segnum):
     415                    # when we have two requests for the same segment, the
     416                    # second one will not be "activated" before the data is
     417                    # delivered, so to allow the status-reporting code to see
     418                    # consistent behavior, we activate them all now. The
     419                    # SegmentEvent will ignore duplicate activate() calls.
     420                    # Note that this will result in an infinite "receive
     421                    # speed" for the second request.
     422                    seg_ev.activate(when)
     423                    seg_ev.deliver(when, offset, len(segment), decodetime)
     424                    eventually(self._deliver, d, c, result)
     425            self._download_status.add_misc_event("process_block", start, now())
    408426            self._active_segment = None
    409427            self._start_new_segment()
    410428        d.addBoth(_deliver)
    411         d.addErrback(lambda f:
    412                      log.err("unhandled error during process_blocks",
    413                              failure=f, level=log.WEIRD,
    414                              parent=self._lp, umid="MkEsCg"))
     429        d.addErrback(log.err, "unhandled error during process_blocks",
     430                     level=log.WEIRD, parent=self._lp, umid="MkEsCg")
    415431
    416432    def _decode_blocks(self, segnum, blocks):
     433        start = now()
    417434        tail = (segnum == self.num_segments-1)
    418435        codec = self._codec
    419436        block_size = self.block_size
    class DownloadNode: 
    434451            shares.append(share)
    435452        del blocks
    436453
    437         start = now()
    438454        d = codec.decode(shares, shareids)   # segment
    439455        del shares
    440456        def _process(buffers):
    class DownloadNode: 
    444460            del buffers
    445461            if tail:
    446462                segment = segment[:self.tail_segment_size]
     463            self._download_status.add_misc_event("decode", start, now())
    447464            return (segment, decodetime)
    448465        d.addCallback(_process)
    449466        return d
    450467
    451468    def _check_ciphertext_hash(self, (segment, decodetime), segnum):
     469        start = now()
    452470        assert self._active_segment.segnum == segnum
    453471        assert self.segment_size is not None
    454472        offset = segnum * self.segment_size
    class DownloadNode: 
    456474        h = hashutil.crypttext_segment_hash(segment)
    457475        try:
    458476            self.ciphertext_hash_tree.set_hashes(leaves={segnum: h})
     477            self._download_status.add_misc_event("CThash", start, now())
    459478            return (offset, segment, decodetime)
    460479        except (BadHashError, NotEnoughHashesError):
    461480            format = ("hash failure in ciphertext_hash_tree:"
    class DownloadNode: 
    479498    def _extract_requests(self, segnum):
    480499        """Remove matching requests and return their (d,c) tuples so that the
    481500        caller can retire them."""
    482         retire = [(d,c) for (segnum0, d, c, lp) in self._segment_requests
     501        retire = [(d,c,seg_ev)
     502                  for (segnum0,d,c,seg_ev,lp) in self._segment_requests
    483503                  if segnum0 == segnum]
    484504        self._segment_requests = [t for t in self._segment_requests
    485505                                  if t[0] != segnum]
    class DownloadNode: 
    488508    def _cancel_request(self, c):
    489509        self._segment_requests = [t for t in self._segment_requests
    490510                                  if t[2] != c]
    491         segnums = [segnum for (segnum,d,c,lp) in self._segment_requests]
     511        segnums = [segnum for (segnum,d,c,seg_ev,lp) in self._segment_requests]
    492512        # self._active_segment might be None in rare circumstances, so make
    493513        # sure we tolerate it
    494514        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..c4e46c3 100644
    a b class Share: 
    244244
    245245        # First, consume all of the information that we currently have, for
    246246        # all the segments people currently want.
     247        start = now()
    247248        while self._get_satisfaction():
    248249            pass
     250        self._download_status.add_misc_event("satisfy", start, now())
    249251
    250252        # When we get no satisfaction (from the data we've received so far),
    251253        # we determine what data we desire (to satisfy more requests). The
    252254        # number of segments is finite, so I can't get no satisfaction
    253255        # forever.
     256        start = now()
    254257        wanted, needed = self._desire()
     258        self._download_status.add_misc_event("desire", start, now())
    255259
    256260        # Finally, send out requests for whatever we need (desire minus
    257261        # have). You can't always get what you want, but if you try
    class Share: 
    259263        self._send_requests(wanted + needed)
    260264
    261265        # and sometimes you can't even get what you need
     266        start = now()
    262267        disappointment = needed & self._unavailable
    263268        if disappointment.len():
    264269            self.had_corruption = True
    265270            raise DataUnavailable("need %s but will never get it" %
    266271                                  disappointment.dump())
     272        self._download_status.add_misc_event("checkdis", start, now())
    267273
    268274    def _get_satisfaction(self):
    269275        # return True if we retired a data block, and should therefore be
    class Share: 
    727733                         share=repr(self),
    728734                         start=start, length=length,
    729735                         level=log.NOISY, parent=self._lp, umid="sgVAyA")
    730             req_ev = ds.add_request_sent(self._peerid, self._shnum,
    731                                          start, length, now())
     736            block_ev = ds.add_block_request(self._peerid, self._shnum,
     737                                            start, length, now())
    732738            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)
     739            d.addCallback(self._got_data, start, length, block_ev, lp)
     740            d.addErrback(self._got_error, start, length, block_ev, lp)
    735741            d.addCallback(self._trigger_loop)
    736742            d.addErrback(lambda f:
    737743                         log.err(format="unhandled error during send_request",
    class Share: 
    741747    def _send_request(self, start, length):
    742748        return self._rref.callRemote("read", start, length)
    743749
    744     def _got_data(self, data, start, length, req_ev, lp):
    745         req_ev.finished(len(data), now())
     750    def _got_data(self, data, start, length, block_ev, lp):
     751        block_ev.finished(len(data), now())
    746752        if not self._alive:
    747753            return
    748754        log.msg(format="%(share)s._got_data [%(start)d:+%(length)d] -> %(datalen)d",
    class Share: 
    784790        # the wanted/needed span is only "wanted" for the first pass. Once
    785791        # the offset table arrives, it's all "needed".
    786792
    787     def _got_error(self, f, start, length, req_ev, lp):
    788         req_ev.finished("error", now())
     793    def _got_error(self, f, start, length, block_ev, lp):
     794        block_ev.error(now())
    789795        log.msg(format="error requesting %(start)d+%(length)d"
    790796                " from %(server)s for si %(si)s",
    791797                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 4576d92..ed1fdbc 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
     131        self.misc_events = []
     132
     133    def add_misc_event(self, what, start, finish=None):
     134        self.misc_events.append( {"what": what,
     135                                  "start_time": start,
     136                                  "finish_time": finish,
     137                                  } )
    80138
    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
     139    def add_read_event(self, start, length, when):
     140        if self.first_timestamp is None:
     141            self.first_timestamp = when
     142        r = { "start": start,
     143              "length": length,
     144              "start_time": when,
     145              "finish_time": None,
     146              "bytes_returned": 0,
     147              "decrypt_time": 0,
     148              "paused_time": 0,
     149              }
     150        self.read_events.append(r)
     151        return ReadEvent(r, self)
    112152
    113153    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)
     154        if self.first_timestamp is None:
     155            self.first_timestamp = when
     156        r = { "segment_number": segnum,
     157              "start_time": when,
     158              "active_time": None,
     159              "finish_time": None,
     160              "success": None,
     161              "decode_time": None,
     162              "segment_start": None,
     163              "segment_length": None,
     164              }
    123165        self.segment_events.append(r)
     166        return SegmentEvent(r, self)
    124167
    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
     168    def add_dyhb_request(self, serverid, when):
     169        r = { "serverid": serverid,
     170              "start_time": when,
     171              "success": None,
     172              "response_shnums": None,
     173              "finish_time": None,
     174              }
     175        self.dyhb_requests.append(r)
     176        return DYHBEvent(r, self)
     177
     178    def add_block_request(self, serverid, shnum, start, length, when):
     179        r = { "serverid": serverid,
     180              "shnum": shnum,
     181              "start": start,
     182              "length": length,
     183              "start_time": when,
     184              "finish_time": None,
     185              "success": None,
     186              "response_length": None,
     187              }
     188        self.block_requests.append(r)
     189        return BlockRequestEvent(r, self)
     190
     191    def update_last_timestamp(self, when):
     192        if self.last_timestamp is None or when > self.last_timestamp:
     193            self.last_timestamp = when
    145194
    146195    def add_known_share(self, serverid, shnum):
    147196        self.known_shares.append( (serverid, shnum) )
    class DownloadStatus: 
    160209        # mention all outstanding segment requests
    161210        outstanding = set()
    162211        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)
     212        outstanding = set([s_ev["segment_number"]
     213                           for s_ev in self.segment_events
     214                           if s_ev["finish_time"] is None])
     215        errorful = set([s_ev["segment_number"]
     216                        for s_ev in self.segment_events
     217                        if s_ev["success"] is False])
    172218        def join(segnums):
    173219            if len(segnums) == 1:
    174220                return "segment %s" % list(segnums)[0]
    class DownloadStatus: 
    191237            return 0.0
    192238        total_outstanding, total_received = 0, 0
    193239        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
     240            if r_ev["finish_time"] is None:
     241                total_outstanding += r_ev["length"]
     242                total_received += r_ev["bytes_returned"]
    198243            # else ignore completed requests
    199244        if not total_outstanding:
    200245            return 1.0
    class DownloadStatus: 
    207252        # a download is considered active if it has at least one outstanding
    208253        # read() call
    209254        for r_ev in self.read_events:
    210             (ign1, ign2, ign3, finishtime, ign4, ign5, ign6) = r_ev
    211             if finishtime is None:
     255            if r_ev["finish_time"] is None:
    212256                return True
    213257        return False
    214258
    215259    def get_started(self):
    216         return self.started
     260        return self.first_timestamp
    217261    def get_results(self):
    218262        return None # TODO
  • src/allmydata/immutable/filenode.py

    diff --git a/src/allmydata/immutable/filenode.py b/src/allmydata/immutable/filenode.py
    index ed3785b..2adc69b 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)
     65            consumer.set_download_status(self._download_status)
    6566        return self._node.read(consumer, offset, size, read_ev)
    6667
    6768    def get_segment(self, segnum):
    class DecryptingConsumer: 
    177178
    178179    def __init__(self, consumer, readkey, offset):
    179180        self._consumer = consumer
    180         self._read_event = None
     181        self._read_ev = None
     182        self._download_status = None
    181183        # TODO: pycryptopp CTR-mode needs random-access operations: I want
    182184        # either a=AES(readkey, offset) or better yet both of:
    183185        #  a=AES(readkey, offset=0)
    class DecryptingConsumer: 
    190192        self._decryptor.process("\x00"*offset_small)
    191193
    192194    def set_download_status_read_event(self, read_ev):
    193         self._read_event = read_ev
     195        self._read_ev = read_ev
     196    def set_download_status(self, ds):
     197        self._download_status = ds
    194198
    195199    def registerProducer(self, producer, streaming):
    196200        # this passes through, so the real consumer can flow-control the real
    class DecryptingConsumer: 
    203207    def write(self, ciphertext):
    204208        started = now()
    205209        plaintext = self._decryptor.process(ciphertext)
    206         if self._read_event:
     210        if self._read_ev:
    207211            elapsed = now() - started
    208             self._read_event.update(0, elapsed, 0)
     212            self._read_ev.update(0, elapsed, 0)
     213        if self._download_status:
     214            self._download_status.add_misc_event("AES", started, now())
    209215        self._consumer.write(plaintext)
    210216
    211217class ImmutableFileNode:
  • src/allmydata/test/test_download.py

    diff --git a/src/allmydata/test/test_download.py b/src/allmydata/test/test_download.py
    index 3a69220..4d0e364 100644
    a b class Status(unittest.TestCase): 
    12141214        now = 12345.1
    12151215        ds = DownloadStatus("si-1", 123)
    12161216        self.failUnlessEqual(ds.get_status(), "idle")
    1217         ds.add_segment_request(0, now)
     1217        ev0 = ds.add_segment_request(0, now)
    12181218        self.failUnlessEqual(ds.get_status(), "fetching segment 0")
    1219         ds.add_segment_delivery(0, now+1, 0, 1000, 2.0)
     1219        ev0.activate(now+0.5)
     1220        ev0.deliver(now+1, 0, 1000, 2.0)
    12201221        self.failUnlessEqual(ds.get_status(), "idle")
    1221         ds.add_segment_request(2, now+2)
    1222         ds.add_segment_request(1, now+2)
     1222        ev2 = ds.add_segment_request(2, now+2)
     1223        del ev2 # hush pyflakes
     1224        ev1 = ds.add_segment_request(1, now+2)
    12231225        self.failUnlessEqual(ds.get_status(), "fetching segments 1,2")
    1224         ds.add_segment_error(1, now+3)
     1226        ev1.error(now+3)
    12251227        self.failUnlessEqual(ds.get_status(),
    12261228                             "fetching segment 2; errors on segment 1")
    12271229
  • src/allmydata/test/test_web.py

    diff --git a/src/allmydata/test/test_web.py b/src/allmydata/test/test_web.py
    index f68e98d..b4b8bdc 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)
    8992
    9093    # 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    e = ds.add_segment_request(4, now)
     95    e.activate(now)
     96    e.deliver(now, 0, 140, 0.5)
    9397
    94     e = ds.add_dyhb_sent("serverid_a", now)
     98    e = ds.add_dyhb_request(serverid_a, now)
    9599    e.finished([1,2], now+1)
    96     e = ds.add_dyhb_sent("serverid_b", now+2) # left unfinished
     100    e = ds.add_dyhb_request(serverid_b, now+2) # left unfinished
    97101
    98102    e = ds.add_read_event(0, 120, now)
    99103    e.update(60, 0.5, 0.1) # bytes, decrypttime, pausetime
    100104    e.finished(now+1)
    101105    e = ds.add_read_event(120, 30, now+2) # left unfinished
    102106
    103     e = ds.add_request_sent("serverid_a", 1, 100, 20, now)
     107    e = ds.add_block_request(serverid_a, 1, 100, 20, now)
    104108    e.finished(20, now+1)
    105     e = ds.add_request_sent("serverid_a", 1, 120, 30, now+1) # left unfinished
     109    e = ds.add_block_request(serverid_a, 1, 120, 30, now+1) # left unfinished
    106110
    107111    # make sure that add_read_event() can come first too
    108     ds1 = DownloadStatus("storage_index", 1234)
     112    ds1 = DownloadStatus(storage_index, 1234)
    109113    e = ds1.add_read_event(0, 120, now)
    110114    e.update(60, 0.5, 0.1) # bytes, decrypttime, pausetime
    111115    e.finished(now+1)
    class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi 
    554558        def _check_dl(res):
    555559            self.failUnless("File Download Status" in res, res)
    556560        d.addCallback(_check_dl)
    557         d.addCallback(lambda res: self.GET("/status/down-%d?t=json" % dl_num))
     561        d.addCallback(lambda res: self.GET("/status/down-%d/event_json" % dl_num))
    558562        def _check_dl_json(res):
    559563            data = simplejson.loads(res)
    560564            self.failUnless(isinstance(data, dict))
     565            self.failUnless("read" in data)
     566            self.failUnlessEqual(data["read"][0]["length"], 120)
     567            self.failUnlessEqual(data["segment"][0]["segment_length"], 100)
     568            self.failUnlessEqual(data["segment"][2]["segment_number"], 2)
     569            self.failUnlessEqual(data["segment"][2]["finish_time"], None)
     570            phwr_id = base32.b2a(hashutil.tagged_hash("foo", "serverid_a")[:20])
     571            cmpu_id = base32.b2a(hashutil.tagged_hash("foo", "serverid_b")[:20])
     572            # serverids[] keys are strings, since that's what JSON does, but
     573            # we'd really like them to be ints
     574            self.failUnlessEqual(data["serverids"]["0"], "phwr")
     575            self.failUnlessEqual(data["serverids"]["1"], "cmpu")
     576            self.failUnlessEqual(data["server_info"][phwr_id]["short"], "phwr")
     577            self.failUnlessEqual(data["server_info"][cmpu_id]["short"], "cmpu")
     578            self.failUnless("dyhb" in data)
     579            self.failUnless("misc" in data)
    561580        d.addCallback(_check_dl_json)
    562581        d.addCallback(lambda res: self.GET("/status/up-%d" % ul_num))
    563582        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..ff893db
    - +  
     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-d3.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  <a id="zoomin" href="#">ZOOMIN</a> <a id="zoomout" href="#">ZOOMOUT</a>
     27  <div id="overview" style="float:right;width:166px;height:100px; border: 1px solid #ddd">overview</div>
     28  <div id="timeline" style="width:600px;border: 1px solid #aaa">Timeline</div>
     29</div>
     30
     31<div>Return to the <a href="/">Welcome Page</a></div>
     32
     33  </body>
     34</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..e5150e9
    - +  
     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.length+60;
     15          var read_top = dyhb_top + 30*pv.max(data.dyhb,
     16                                              function(d) {return d.row;})+60;
     17          var segment_top = read_top + 30*data.read[data.read.length-1].row+60;
     18          var misc_top = segment_top + 30*data.segment[data.segment.length-1].row+60;
     19          var block_top = misc_top + 50;
     20          var block_row_to_y = {};
     21          var row_y=0;
     22          for (var group=0; group < data.block_rownums.length; group++) {
     23              for (var row=0; row < data.block_rownums[group]; row++) {
     24                  block_row_to_y[group+"-"+row] = row_y;
     25                  row_y += 10;
     26              }
     27              row_y += 5;
     28          }
     29          var height = block_top + row_y;
     30
     31          var kx = bounds.min;
     32          var ky = 1;
     33          var x = pv.Scale.linear(bounds.min, bounds.max).range(0, WIDTH-40);
     34          var relx = pv.Scale.linear(0, duration).range(0, WIDTH-40);
     35          //var y = pv.Scale.linear(-ky,ky).range(0, height);
     36          //x.nice(); relx.nice();
     37
     38          /* add the invisible panel now, at the bottom of the stack, so that
     39          it won't steal mouseover events and prevent tooltips from
     40          working. */
     41          var zoomer = vis.add(pv.Panel)
     42              .events("all")
     43              .event("mousedown", pv.Behavior.pan())
     44              .event("mousewheel", pv.Behavior.zoom())
     45              .event("pan", transform)
     46              .event("zoom", transform)
     47          ;
     48
     49          vis.anchor("top").top(-20).add(pv.Label).text("DYHB Requests");
     50
     51          vis.add(pv.Bar)
     52              .data(data.dyhb)
     53              .height(20)
     54              .top(function (d) {return 30*d.row;})
     55              .left(function(d){return x(d.start_time);})
     56              .width(function(d){return x(d.finish_time)-x(d.start_time);})
     57              .title(function(d){return "shnums: "+d.response_shnums;})
     58              .fillStyle(function(d){return data.server_info[d.serverid].color;})
     59              .strokeStyle("black").lineWidth(1)
     60          .anchor("left").add(pv.Label).text(function(d){return d.serverid.slice(0,4);})
     61          ;
     62
     63          vis.add(pv.Rule)
     64              .data(data.dyhb)
     65              .top(function(d){return 30*d.row + 20/2;})
     66              .left(0).width(0)
     67              .strokeStyle("#888")
     68              .anchor("left").add(pv.Label)
     69              .text(function(d){return d.serverid.slice(0,4);})
     70          ;
     71
     72          /* we use a function for data=relx.ticks() here instead of
     73           simply .data(relx.ticks()) so that it will be recalculated when
     74           the scales change (by pan/zoom) */
     75          var xaxis = vis.add(pv.Rule)
     76              .data(function() {return relx.ticks();})
     77              .strokeStyle("#ccc")
     78              .left(relx)
     79              .anchor("bottom").add(pv.Label)
     80              .text(function(d){return relx.tickFormat(d)+"s";});
     81
     82          var read = vis.add(pv.Panel).top(read_top);
     83          read.anchor("top").top(-20).add(pv.Label).text("read() requests");
     84
     85          read.add(pv.Bar)
     86              .data(data.read)
     87              .height(20)
     88              .top(function (d) {return 30*d.row;})
     89              .left(function(d){return x(d.start_time);})
     90              .width(function(d){return x(d.finish_time)-x(d.start_time);})
     91              .title(function(d){return "read(start="+d.start+", len="+d.length+") -> "+d.bytes_returned+" bytes";})
     92              .fillStyle("red")
     93              .strokeStyle("black").lineWidth(1);
     94
     95          var segment = vis.add(pv.Panel).top(segment_top);
     96          segment.anchor("top").top(-20).add(pv.Label).text("segment() requests");
     97
     98          segment.add(pv.Bar)
     99              .data(data.segment)
     100              .height(20)
     101              .top(function (d) {return 30*d.row;})
     102              .left(function(d){return x(d.start_time);})
     103              .width(function(d){return x(d.finish_time)-x(d.start_time);})
     104              .title(function(d){return "seg"+d.segment_number+" ["+d.segment_start+":+"+d.segment_length+"] (took "+(d.finish_time-d.start_time)+")";})
     105              .fillStyle(function(d){if (d.success) return "#c0ffc0";
     106                                    else return "#ffc0c0";})
     107              .strokeStyle("black").lineWidth(1)
     108              .anchor("center").add(pv.Label).text(function(d){return d.segment_number;});
     109
     110          var block = vis.add(pv.Panel).top(block_top);
     111          block.anchor("top").top(-20).add(pv.Label).text("block requests");
     112
     113          var shnum_colors = pv.Colors.category10();
     114          block.add(pv.Bar)
     115              .data(data.block)
     116              .height(10)
     117              .top(function (d) {return block_row_to_y[d.row[0]+"-"+d.row[1]];})
     118              .left(function(d){return x(d.start_time);})
     119              .width(function(d){return x(d.finish_time)-x(d.start_time);})
     120              .title(function(d){return "sh"+d.shnum+"-on-"+d.serverid.slice(0,4)+" ["+d.start+":+"+d.length+"] -> "+d.response_length;})
     121              .fillStyle(function(d){return data.server_info[d.serverid].color;})
     122              .strokeStyle(function(d){return shnum_colors(d.shnum).color;})
     123              .lineWidth(function(d)
     124                         {if (d.response_length > 100) return 3;
     125                         else return 1;
     126                          })
     127          ;
     128
     129          var misc = vis.add(pv.Panel).top(misc_top);
     130          misc.anchor("top").top(-20).add(pv.Label).text("misc");
     131          misc.add(pv.Bar)
     132              .data(data.misc)
     133              .height(20)
     134              .top(function(d){return 25*d.row;})
     135              .left(function(d){return x(d.start_time);})
     136              .width(function(d){return x(d.finish_time)-x(d.start_time);})
     137              .title(function(d)
     138                     {return "start="+d.start_time+"  finish="+d.finish_time+"  duration="+(d.finish_time-d.start_time);})
     139              .fillStyle("#eee")
     140              .strokeStyle("#444")
     141          .anchor("top").add(pv.Label).text(function(d){return d.what;})
     142          ;
     143
     144
     145          vis.height(height);
     146
     147          function zoomin() {
     148              var t = zoomer.transform().invert();
     149              t.k = t.k/1.5;
     150              zoomer.transform(t.invert());
     151              zoompan(t);
     152          }
     153
     154          function zoomout() {
     155              var t = zoomer.transform().invert();
     156              t.k = t.k*1.5;
     157              zoomer.transform(t.invert());
     158              zoompan(t);
     159          }
     160
     161          function transform() {
     162              var t = this.transform().invert();
     163              zoompan(t);
     164          }
     165          function zoompan(t) {
     166              // when t.x=0 and t.k=1.0, left should be bounds.min
     167              x.domain(bounds.min + (t.x/WIDTH)*duration,
     168                       bounds.min + t.k*duration + (t.x/WIDTH)*duration);
     169              relx.domain(0 + t.x/WIDTH*duration,
     170                          t.k*duration + (t.x/WIDTH)*duration);
     171              vis.render();
     172          }
     173
     174          vis.render();
     175          $("#zoomin").click(zoomin);
     176          $("#zoomout").click(zoomout);
     177      }
     178
     179      $.ajax({url: "event_json",
     180              method: 'GET',
     181              dataType: 'json',
     182              success: onDataReceived });
     183});
     184
  • 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-d3.2.js

    diff --git a/src/allmydata/web/protovis-d3.2.js b/src/allmydata/web/protovis-d3.2.js
    new file mode 100644
    index 0000000..bfe5a02
    - +  
     1// fba9dc272a443cf9fdb984676a7732a6a082f4c0
     2/**
     3 * @class The built-in Array class.
     4 * @name Array
     5 */
     6
     7/**
     8 * Creates a new array with the results of calling a provided function on every
     9 * element in this array. Implemented in Javascript 1.6.
     10 *
     11 * @function
     12 * @name Array.prototype.map
     13 * @see <a
     14 * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/Map">map</a>
     15 * documentation.
     16 * @param {function} f function that produces an element of the new Array from
     17 * an element of the current one.
     18 * @param [o] object to use as <tt>this</tt> when executing <tt>f</tt>.
     19 */
     20if (!Array.prototype.map) Array.prototype.map = function(f, o) {
     21  var n = this.length;
     22  var result = new Array(n);
     23  for (var i = 0; i < n; i++) {
     24    if (i in this) {
     25      result[i] = f.call(o, this[i], i, this);
     26    }
     27  }
     28  return result;
     29};
     30
     31/**
     32 * Creates a new array with all elements that pass the test implemented by the
     33 * provided function. Implemented in Javascript 1.6.
     34 *
     35 * @function
     36 * @name Array.prototype.filter
     37 * @see <a
     38 * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/filter">filter</a>
     39 * documentation.
     40 * @param {function} f function to test each element of the array.
     41 * @param [o] object to use as <tt>this</tt> when executing <tt>f</tt>.
     42 */
     43if (!Array.prototype.filter) Array.prototype.filter = function(f, o) {
     44  var n = this.length;
     45  var result = new Array();
     46  for (var i = 0; i < n; i++) {
     47    if (i in this) {
     48      var v = this[i];
     49      if (f.call(o, v, i, this)) result.push(v);
     50    }
     51  }
     52  return result;
     53};
     54
     55/**
     56 * Executes a provided function once per array element. Implemented in
     57 * Javascript 1.6.
     58 *
     59 * @function
     60 * @name Array.prototype.forEach
     61 * @see <a
     62 * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/ForEach">forEach</a>
     63 * documentation.
     64 * @param {function} f function to execute for each element.
     65 * @param [o] object to use as <tt>this</tt> when executing <tt>f</tt>.
     66 */
     67if (!Array.prototype.forEach) Array.prototype.forEach = function(f, o) {
     68  var n = this.length >>> 0;
     69  for (var i = 0; i < n; i++) {
     70    if (i in this) f.call(o, this[i], i, this);
     71  }
     72};
     73
     74/**
     75 * Apply a function against an accumulator and each value of the array (from
     76 * left-to-right) as to reduce it to a single value. Implemented in Javascript
     77 * 1.8.
     78 *
     79 * @function
     80 * @name Array.prototype.reduce
     81 * @see <a
     82 * href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/Reduce">reduce</a>
     83 * documentation.
     84 * @param {function} f function to execute on each value in the array.
     85 * @param [v] object to use as the first argument to the first call of
     86 * <tt>t</tt>.
     87 */
     88if (!Array.prototype.reduce) Array.prototype.reduce = function(f, v) {
     89  var len = this.length;
     90  if (!len && (arguments.length == 1)) {
     91    throw new Error("reduce: empty array, no initial value");
     92  }
     93
     94  var i = 0;
     95  if (arguments.length < 2) {
     96    while (true) {
     97      if (i in this) {
     98        v = this[i++];
     99        break;
     100      }
     101      if (++i >= len) {
     102        throw new Error("reduce: no values, no initial value");
     103      }
     104    }
     105  }
     106
     107  for (; i < len; i++) {
     108    if (i in this) {
     109      v = f(v, this[i], i, this);
     110    }
     111  }
     112  return v;
     113};
     114/**
     115 * The top-level Protovis namespace. All public methods and fields should be
     116 * registered on this object. Note that core Protovis source is surrounded by an
     117 * anonymous function, so any other declared globals will not be visible outside
     118 * of core methods. This also allows multiple versions of Protovis to coexist,
     119 * since each version will see their own <tt>pv</tt> namespace.
     120 *
     121 * @namespace The top-level Protovis namespace, <tt>pv</tt>.
     122 */
     123var pv = {};
     124
     125/**
     126 * Protovis major and minor version numbers.
     127 *
     128 * @namespace Protovis major and minor version numbers.
     129 */
     130pv.version = {
     131  /**
     132   * The major version number.
     133   *
     134   * @type number
     135   * @constant
     136   */
     137  major: 3,
     138
     139  /**
     140   * The minor version number.
     141   *
     142   * @type number
     143   * @constant
     144   */
     145  minor: 2
     146};
     147
     148/**
     149 * Returns the passed-in argument, <tt>x</tt>; the identity function. This method
     150 * is provided for convenience since it is used as the default behavior for a
     151 * number of property functions.
     152 *
     153 * @param x a value.
     154 * @returns the value <tt>x</tt>.
     155 */
     156pv.identity = function(x) { return x; };
     157
     158/**
     159 * Returns <tt>this.index</tt>. This method is provided for convenience for use
     160 * with scales. For example, to color bars by their index, say:
     161 *
     162 * <pre>.fillStyle(pv.Colors.category10().by(pv.index))</pre>
     163 *
     164 * This method is equivalent to <tt>function() this.index</tt>, but more
     165 * succinct. Note that the <tt>index</tt> property is also supported for
     166 * accessor functions with {@link pv.max}, {@link pv.min} and other array
     167 * utility methods.
     168 *
     169 * @see pv.Scale
     170 * @see pv.Mark#index
     171 */
     172pv.index = function() { return this.index; };
     173
     174/**
     175 * Returns <tt>this.childIndex</tt>. This method is provided for convenience for
     176 * use with scales. For example, to color bars by their child index, say:
     177 *
     178 * <pre>.fillStyle(pv.Colors.category10().by(pv.child))</pre>
     179 *
     180 * This method is equivalent to <tt>function() this.childIndex</tt>, but more
     181 * succinct.
     182 *
     183 * @see pv.Scale
     184 * @see pv.Mark#childIndex
     185 */
     186pv.child = function() { return this.childIndex; };
     187
     188/**
     189 * Returns <tt>this.parent.index</tt>. This method is provided for convenience
     190 * for use with scales. This method is provided for convenience for use with
     191 * scales. For example, to color bars by their parent index, say:
     192 *
     193 * <pre>.fillStyle(pv.Colors.category10().by(pv.parent))</pre>
     194 *
     195 * Tthis method is equivalent to <tt>function() this.parent.index</tt>, but more
     196 * succinct.
     197 *
     198 * @see pv.Scale
     199 * @see pv.Mark#index
     200 */
     201pv.parent = function() { return this.parent.index; };
     202
     203/**
     204 * Stores the current event. This field is only set within event handlers.
     205 *
     206 * @type Event
     207 * @name pv.event
     208 */
     209/**
     210 * @private Returns a prototype object suitable for extending the given class
     211 * <tt>f</tt>. Rather than constructing a new instance of <tt>f</tt> to serve as
     212 * the prototype (which unnecessarily runs the constructor on the created
     213 * prototype object, potentially polluting it), an anonymous function is
     214 * generated internally that shares the same prototype:
     215 *
     216 * <pre>function g() {}
     217 * g.prototype = f.prototype;
     218 * return new g();</pre>
     219 *
     220 * For more details, see Douglas Crockford's essay on prototypal inheritance.
     221 *
     222 * @param {function} f a constructor.
     223 * @returns a suitable prototype object.
     224 * @see Douglas Crockford's essay on <a
     225 * href="http://javascript.crockford.com/prototypal.html">prototypal
     226 * inheritance</a>.
     227 */
     228pv.extend = function(f) {
     229  function g() {}
     230  g.prototype = f.prototype || f;
     231  return new g();
     232};
     233
     234try {
     235  eval("pv.parse = function(x) x;"); // native support
     236} catch (e) {
     237
     238/**
     239 * @private Parses a Protovis specification, which may use JavaScript 1.8
     240 * function expresses, replacing those function expressions with proper
     241 * functions such that the code can be run by a JavaScript 1.6 interpreter. This
     242 * hack only supports function expressions (using clumsy regular expressions, no
     243 * less), and not other JavaScript 1.8 features such as let expressions.
     244 *
     245 * @param {string} s a Protovis specification (i.e., a string of JavaScript 1.8
     246 * source code).
     247 * @returns {string} a conformant JavaScript 1.6 source code.
     248 */
     249  pv.parse = function(js) { // hacky regex support
     250    var re = new RegExp("function\\s*(\\b\\w+)?\\s*\\([^)]*\\)\\s*", "mg"), m, d, i = 0, s = "";
     251    while (m = re.exec(js)) {
     252      var j = m.index + m[0].length;
     253      if (js.charAt(j) != '{') {
     254        s += js.substring(i, j) + "{return ";
     255        i = j;
     256        for (var p = 0; p >= 0 && j < js.length; j++) {
     257          var c = js.charAt(j);
     258          switch (c) {
     259            case '"': case '\'': {
     260              while (++j < js.length && (d = js.charAt(j)) != c) {
     261                if (d == '\\') j++;
     262              }
     263              break;
     264            }
     265            case '[': case '(': p++; break;
     266            case ']': case ')': p--; break;
     267            case ';':
     268            case ',': if (p == 0) p--; break;
     269          }
     270        }
     271        s += pv.parse(js.substring(i, --j)) + ";}";
     272        i = j;
     273      }
     274      re.lastIndex = j;
     275    }
     276    s += js.substring(i);
     277    return s;
     278  };
     279}
     280
     281/**
     282 * @private Computes the value of the specified CSS property <tt>p</tt> on the
     283 * specified element <tt>e</tt>.
     284 *
     285 * @param {string} p the name of the CSS property.
     286 * @param e the element on which to compute the CSS property.
     287 */
     288pv.css = function(e, p) {
     289  return window.getComputedStyle
     290      ? window.getComputedStyle(e, null).getPropertyValue(p)
     291      : e.currentStyle[p];
     292};
     293
     294/**
     295 * @private Reports the specified error to the JavaScript console. Mozilla only
     296 * allows logging to the console for privileged code; if the console is
     297 * unavailable, the alert dialog box is used instead.
     298 *
     299 * @param e the exception that triggered the error.
     300 */
     301pv.error = function(e) {
     302  (typeof console == "undefined") ? alert(e) : console.error(e);
     303};
     304
     305/**
     306 * @private Registers the specified listener for events of the specified type on
     307 * the specified target. For standards-compliant browsers, this method uses
     308 * <tt>addEventListener</tt>; for Internet Explorer, <tt>attachEvent</tt>.
     309 *
     310 * @param target a DOM element.
     311 * @param {string} type the type of event, such as "click".
     312 * @param {function} the event handler callback.
     313 */
     314pv.listen = function(target, type, listener) {
     315  listener = pv.listener(listener);
     316  return target.addEventListener
     317      ? target.addEventListener(type, listener, false)
     318      : target.attachEvent("on" + type, listener);
     319};
     320
     321/**
     322 * @private Returns a wrapper for the specified listener function such that the
     323 * {@link pv.event} is set for the duration of the listener's invocation. The
     324 * wrapper is cached on the returned function, such that duplicate registrations
     325 * of the wrapped event handler are ignored.
     326 *
     327 * @param {function} f an event handler.
     328 * @returns {function} the wrapped event handler.
     329 */
     330pv.listener = function(f) {
     331  return f.$listener || (f.$listener = function(e) {
     332      try {
     333        pv.event = e;
     334        return f.call(this, e);
     335      } finally {
     336        delete pv.event;
     337      }
     338    });
     339};
     340
     341/**
     342 * @private Returns true iff <i>a</i> is an ancestor of <i>e</i>. This is useful
     343 * for ignoring mouseout and mouseover events that are contained within the
     344 * target element.
     345 */
     346pv.ancestor = function(a, e) {
     347  while (e) {
     348    if (e == a) return true;
     349    e = e.parentNode;
     350  }
     351  return false;
     352};
     353
     354/** @private Returns a locally-unique positive id. */
     355pv.id = function() {
     356  var id = 1; return function() { return id++; };
     357}();
     358
     359/** @private Returns a function wrapping the specified constant. */
     360pv.functor = function(v) {
     361  return typeof v == "function" ? v : function() { return v; };
     362};
     363/*
     364 * Parses the Protovis specifications on load, allowing the use of JavaScript
     365 * 1.8 function expressions on browsers that only support JavaScript 1.6.
     366 *
     367 * @see pv.parse
     368 */
     369pv.listen(window, "load", function() {
     370   /*
     371    * Note: in Firefox any variables declared here are visible to the eval'd
     372    * script below. Even worse, any global variables declared by the script
     373    * could overwrite local variables here (such as the index, `i`)!  To protect
     374    * against this, all variables are explicitly scoped on a pv.$ object.
     375    */
     376    pv.$ = {i:0, x:document.getElementsByTagName("script")};
     377    for (; pv.$.i < pv.$.x.length; pv.$.i++) {
     378      pv.$.s = pv.$.x[pv.$.i];
     379      if (pv.$.s.type == "text/javascript+protovis") {
     380        try {
     381          window.eval(pv.parse(pv.$.s.text));
     382        } catch (e) {
     383          pv.error(e);
     384        }
     385      }
     386    }
     387    delete pv.$;
     388  });
     389/**
     390 * Abstract; see an implementing class.
     391 *
     392 * @class Represents an abstract text formatter and parser. A <i>format</i> is a
     393 * function that converts an object of a given type, such as a <tt>Date</tt>, to
     394 * a human-readable string representation. The format may also have a
     395 * {@link #parse} method for converting a string representation back to the
     396 * given object type.
     397 *
     398 * <p>Because formats are themselves functions, they can be used directly as
     399 * mark properties. For example, if the data associated with a label are dates,
     400 * a date format can be used as label text:
     401 *
     402 * <pre>    .text(pv.Format.date("%m/%d/%y"))</pre>
     403 *
     404 * And as with scales, if the format is used in multiple places, it can be
     405 * convenient to declare it as a global variable and then reference it from the
     406 * appropriate property functions. For example, if the data has a <tt>date</tt>
     407 * attribute, and <tt>format</tt> references a given date format:
     408 *
     409 * <pre>    .text(function(d) format(d.date))</pre>
     410 *
     411 * Similarly, to parse a string into a date:
     412 *
     413 * <pre>var date = format.parse("4/30/2010");</pre>
     414 *
     415 * Not all format implementations support parsing. See the implementing class
     416 * for details.
     417 *
     418 * @see pv.Format.date
     419 * @see pv.Format.number
     420 * @see pv.Format.time
     421 */
     422pv.Format = {};
     423
     424/**
     425 * Formats the specified object, returning the string representation.
     426 *
     427 * @function
     428 * @name pv.Format.prototype.format
     429 * @param {object} x the object to format.
     430 * @returns {string} the formatted string.
     431 */
     432
     433/**
     434 * Parses the specified string, returning the object representation.
     435 *
     436 * @function
     437 * @name pv.Format.prototype.parse
     438 * @param {string} x the string to parse.
     439 * @returns {object} the parsed object.
     440 */
     441
     442/**
     443 * @private Given a string that may be used as part of a regular expression,
     444 * this methods returns an appropriately quoted version of the specified string,
     445 * with any special characters escaped.
     446 *
     447 * @param {string} s a string to quote.
     448 * @returns {string} the quoted string.
     449 */
     450pv.Format.re = function(s) {
     451  return s.replace(/[\\\^\$\*\+\?\[\]\(\)\.\{\}]/g, "\\$&");
     452};
     453
     454/**
     455 * @private Optionally pads the specified string <i>s</i> so that it is at least
     456 * <i>n</i> characters long, using the padding character <i>c</i>.
     457 *
     458 * @param {string} c the padding character.
     459 * @param {number} n the minimum string length.
     460 * @param {string} s the string to pad.
     461 * @returns {string} the padded string.
     462 */
     463pv.Format.pad = function(c, n, s) {
     464  var m = n - String(s).length;
     465  return (m < 1) ? s : new Array(m + 1).join(c) + s;
     466};
     467/**
     468 * Constructs a new date format with the specified string pattern.
     469 *
     470 * @class The format string is in the same format expected by the
     471 * <tt>strftime</tt> function in C. The following conversion specifications are
     472 * supported:<ul>
     473 *
     474 * <li>%a - abbreviated weekday name.</li>
     475 * <li>%A - full weekday name.</li>
     476 * <li>%b - abbreviated month names.</li>
     477 * <li>%B - full month names.</li>
     478 * <li>%c - locale's appropriate date and time.</li>
     479 * <li>%C - century number.</li>
     480 * <li>%d - day of month [01,31] (zero padded).</li>
     481 * <li>%D - same as %m/%d/%y.</li>
     482 * <li>%e - day of month [ 1,31] (space padded).</li>
     483 * <li>%h - same as %b.</li>
     484 * <li>%H - hour (24-hour clock) [00,23] (zero padded).</li>
     485 * <li>%I - hour (12-hour clock) [01,12] (zero padded).</li>
     486 * <li>%m - month number [01,12] (zero padded).</li>
     487 * <li>%M - minute [0,59] (zero padded).</li>
     488 * <li>%n - newline character.</li>
     489 * <li>%p - locale's equivalent of a.m. or p.m.</li>
     490 * <li>%r - same as %I:%M:%S %p.</li>
     491 * <li>%R - same as %H:%M.</li>
     492 * <li>%S - second [00,61] (zero padded).</li>
     493 * <li>%t - tab character.</li>
     494 * <li>%T - same as %H:%M:%S.</li>
     495 * <li>%x - same as %m/%d/%y.</li>
     496 * <li>%X - same as %I:%M:%S %p.</li>
     497 * <li>%y - year with century [00,99] (zero padded).</li>
     498 * <li>%Y - year including century.</li>
     499 * <li>%% - %.</li>
     500 *
     501 * </ul>The following conversion specifications are currently <i>unsupported</i>
     502 * for formatting:<ul>
     503 *
     504 * <li>%j - day number [1,366].</li>
     505 * <li>%u - weekday number [1,7].</li>
     506 * <li>%U - week number [00,53].</li>
     507 * <li>%V - week number [01,53].</li>
     508 * <li>%w - weekday number [0,6].</li>
     509 * <li>%W - week number [00,53].</li>
     510 * <li>%Z - timezone name or abbreviation.</li>
     511 *
     512 * </ul>In addition, the following conversion specifications are currently
     513 * <i>unsupported</i> for parsing:<ul>
     514 *
     515 * <li>%a - day of week, either abbreviated or full name.</li>
     516 * <li>%A - same as %a.</li>
     517 * <li>%c - locale's appropriate date and time.</li>
     518 * <li>%C - century number.</li>
     519 * <li>%D - same as %m/%d/%y.</li>
     520 * <li>%I - hour (12-hour clock) [1,12].</li>
     521 * <li>%n - any white space.</li>
     522 * <li>%p - locale's equivalent of a.m. or p.m.</li>
     523 * <li>%r - same as %I:%M:%S %p.</li>
     524 * <li>%R - same as %H:%M.</li>
     525 * <li>%t - same as %n.</li>
     526 * <li>%T - same as %H:%M:%S.</li>
     527 * <li>%x - locale's equivalent to %m/%d/%y.</li>
     528 * <li>%X - locale's equivalent to %I:%M:%S %p.</li>
     529 *
     530 * </ul>
     531 *
     532 * @see <a
     533 * href="http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html">strftime</a>
     534 * documentation.
     535 * @see <a
     536 * href="http://www.opengroup.org/onlinepubs/007908799/xsh/strptime.html">strptime</a>
     537 * documentation.
     538 * @extends pv.Format
     539 * @param {string} pattern the format pattern.
     540 */
     541pv.Format.date = function(pattern) {
     542  var pad = pv.Format.pad;
     543
     544  /** @private */
     545  function format(d) {
     546    return pattern.replace(/%[a-zA-Z0-9]/g, function(s) {
     547        switch (s) {
     548          case '%a': return [
     549              "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
     550            ][d.getDay()];
     551          case '%A': return [
     552              "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
     553              "Saturday"
     554            ][d.getDay()];
     555          case '%h':
     556          case '%b': return [
     557              "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
     558              "Oct", "Nov", "Dec"
     559            ][d.getMonth()];
     560          case '%B': return [
     561              "January", "February", "March", "April", "May", "June", "July",
     562              "August", "September", "October", "November", "December"
     563            ][d.getMonth()];
     564          case '%c': return d.toLocaleString();
     565          case '%C': return pad("0", 2, Math.floor(d.getFullYear() / 100) % 100);
     566          case '%d': return pad("0", 2, d.getDate());
     567          case '%x':
     568          case '%D': return pad("0", 2, d.getMonth() + 1)
     569                    + "/" + pad("0", 2, d.getDate())
     570                    + "/" + pad("0", 2, d.getFullYear() % 100);
     571          case '%e': return pad(" ", 2, d.getDate());
     572          case '%H': return pad("0", 2, d.getHours());
     573          case '%I': {
     574            var h = d.getHours() % 12;
     575            return h ? pad("0", 2, h) : 12;
     576          }
     577          // TODO %j: day of year as a decimal number [001,366]
     578          case '%m': return pad("0", 2, d.getMonth() + 1);
     579          case '%M': return pad("0", 2, d.getMinutes());
     580          case '%n': return "\n";
     581          case '%p': return d.getHours() < 12 ? "AM" : "PM";
     582          case '%T':
     583          case '%X':
     584          case '%r': {
     585            var h = d.getHours() % 12;
     586            return (h ? pad("0", 2, h) : 12)
     587                    + ":" + pad("0", 2, d.getMinutes())
     588                    + ":" + pad("0", 2, d.getSeconds())
     589                    + " " + (d.getHours() < 12 ? "AM" : "PM");
     590          }
     591          case '%R': return pad("0", 2, d.getHours()) + ":" + pad("0", 2, d.getMinutes());
     592          case '%S': return pad("0", 2, d.getSeconds());
     593          case '%Q': return pad("0", 3, d.getMilliseconds());
     594          case '%t': return "\t";
     595          case '%u': {
     596            var w = d.getDay();
     597            return w ? w : 1;
     598          }
     599          // TODO %U: week number (sunday first day) [00,53]
     600          // TODO %V: week number (monday first day) [01,53] ... with weirdness
     601          case '%w': return d.getDay();
     602          // TODO %W: week number (monday first day) [00,53] ... with weirdness
     603          case '%y': return pad("0", 2, d.getFullYear() % 100);
     604          case '%Y': return d.getFullYear();
     605          // TODO %Z: timezone name or abbreviation
     606          case '%%': return "%";
     607        }
     608        return s;
     609      });
     610  }
     611
     612  /**
     613   * Converts a date to a string using the associated formatting pattern.
     614   *
     615   * @function
     616   * @name pv.Format.date.prototype.format
     617   * @param {Date} date a date to format.
     618   * @returns {string} the formatted date as a string.
     619   */
     620  format.format = format;
     621
     622  /**
     623   * Parses a date from a string using the associated formatting pattern.
     624   *
     625   * @function
     626   * @name pv.Format.date.prototype.parse
     627   * @param {string} s the string to parse as a date.
     628   * @returns {Date} the parsed date.
     629   */
     630  format.parse = function(s) {
     631    var year = 1970, month = 0, date = 1, hour = 0, minute = 0, second = 0;
     632    var fields = [function() {}];
     633
     634    /* Register callbacks for each field in the format pattern. */
     635    var re = pv.Format.re(pattern).replace(/%[a-zA-Z0-9]/g, function(s) {
     636        switch (s) {
     637          // TODO %a: day of week, either abbreviated or full name
     638          // TODO %A: same as %a
     639          case '%b': {
     640            fields.push(function(x) { month = {
     641                  Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5, Jul: 6, Aug: 7,
     642                  Sep: 8, Oct: 9, Nov: 10, Dec: 11
     643                }[x]; });
     644            return "([A-Za-z]+)";
     645          }
     646          case '%h':
     647          case '%B': {
     648            fields.push(function(x) { month = {
     649                  January: 0, February: 1, March: 2, April: 3, May: 4, June: 5,
     650                  July: 6, August: 7, September: 8, October: 9, November: 10,
     651                  December: 11
     652                }[x]; });
     653            return "([A-Za-z]+)";
     654          }
     655          // TODO %c: locale's appropriate date and time
     656          // TODO %C: century number[0,99]
     657          case '%e':
     658          case '%d': {
     659            fields.push(function(x) { date = x; });
     660            return "([0-9]+)";
     661          }
     662          // TODO %D: same as %m/%d/%y
     663          case '%I':
     664          case '%H': {
     665            fields.push(function(x) { hour = x; });
     666            return "([0-9]+)";
     667          }
     668          // TODO %j: day number [1,366]
     669          case '%m': {
     670            fields.push(function(x) { month = x - 1; });
     671            return "([0-9]+)";
     672          }
     673          case '%M': {
     674            fields.push(function(x) { minute = x; });
     675            return "([0-9]+)";
     676          }
     677          // TODO %n: any white space
     678          // TODO %p: locale's equivalent of a.m. or p.m.
     679          case '%p': { // TODO this is a hack
     680            fields.push(function(x) {
     681              if (hour == 12) {
     682                if (x == "am") hour = 0;
     683              } else if (x == "pm") {
     684                hour = Number(hour) + 12;
     685              }
     686            });
     687            return "(am|pm)";
     688          }
     689          // TODO %r: %I:%M:%S %p
     690          // TODO %R: %H:%M
     691          case '%S': {
     692            fields.push(function(x) { second = x; });
     693            return "([0-9]+)";
     694          }
     695          // TODO %t: any white space
     696          // TODO %T: %H:%M:%S
     697          // TODO %U: week number [00,53]
     698          // TODO %w: weekday [0,6]
     699          // TODO %W: week number [00, 53]
     700          // TODO %x: locale date (%m/%d/%y)
     701          // TODO %X: locale time (%I:%M:%S %p)
     702          case '%y': {
     703            fields.push(function(x) {
     704                x = Number(x);
     705                year = x + (((0 <= x) && (x < 69)) ? 2000
     706                    : (((x >= 69) && (x < 100) ? 1900 : 0)));
     707              });
     708            return "([0-9]+)";
     709          }
     710          case '%Y': {
     711            fields.push(function(x) { year = x; });
     712            return "([0-9]+)";
     713          }
     714          case '%%': {
     715            fields.push(function() {});
     716            return "%";
     717          }
     718        }
     719        return s;
     720      });
     721
     722    var match = s.match(re);
     723    if (match) match.forEach(function(m, i) { fields[i](m); });
     724    return new Date(year, month, date, hour, minute, second);
     725  };
     726
     727  return format;
     728};
     729/**
     730 * Returns a time format of the given type, either "short" or "long".
     731 *
     732 * @class Represents a time format, converting between a <tt>number</tt>
     733 * representing a duration in milliseconds, and a <tt>string</tt>. Two types of
     734 * time formats are supported: "short" and "long". The <i>short</i> format type
     735 * returns a string such as "3.3 days" or "12.1 minutes", while the <i>long</i>
     736 * format returns "13:04:12" or similar.
     737 *
     738 * @extends pv.Format
     739 * @param {string} type the type; "short" or "long".
     740 */
     741pv.Format.time = function(type) {
     742  var pad = pv.Format.pad;
     743
     744  /*
     745   * MILLISECONDS = 1
     746   * SECONDS = 1e3
     747   * MINUTES = 6e4
     748   * HOURS = 36e5
     749   * DAYS = 864e5
     750   * WEEKS = 6048e5
     751   * MONTHS = 2592e6
     752   * YEARS = 31536e6
     753   */
     754
     755  /** @private */
     756  function format(t) {
     757    t = Number(t); // force conversion from Date
     758    switch (type) {
     759      case "short": {
     760        if (t >= 31536e6) {
     761          return (t / 31536e6).toFixed(1) + " years";
     762        } else if (t >= 6048e5) {
     763          return (t / 6048e5).toFixed(1) + " weeks";
     764        } else if (t >= 864e5) {
     765          return (t / 864e5).toFixed(1) + " days";
     766        } else if (t >= 36e5) {
     767          return (t / 36e5).toFixed(1) + " hours";
     768        } else if (t >= 6e4) {
     769          return (t / 6e4).toFixed(1) + " minutes";
     770        }
     771        return (t / 1e3).toFixed(1) + " seconds";
     772      }
     773      case "long": {
     774        var a = [],
     775            s = ((t % 6e4) / 1e3) >> 0,
     776            m = ((t % 36e5) / 6e4) >> 0;
     777        a.push(pad("0", 2, s));
     778        if (t >= 36e5) {
     779          var h = ((t % 864e5) / 36e5) >> 0;
     780          a.push(pad("0", 2, m));
     781          if (t >= 864e5) {
     782            a.push(pad("0", 2, h));
     783            a.push(Math.floor(t / 864e5).toFixed());
     784          } else {
     785            a.push(h.toFixed());
     786          }
     787        } else {
     788          a.push(m.toFixed());
     789        }
     790        return a.reverse().join(":");
     791      }
     792    }
     793  }
     794
     795  /**
     796   * Formats the specified time, returning the string representation.
     797   *
     798   * @function
     799   * @name pv.Format.time.prototype.format
     800   * @param {number} t the duration in milliseconds. May also be a <tt>Date</tt>.
     801   * @returns {string} the formatted string.
     802   */
     803  format.format = format;
     804
     805  /**
     806   * Parses the specified string, returning the time in milliseconds.
     807   *
     808   * @function
     809   * @name pv.Format.time.prototype.parse
     810   * @param {string} s a formatted string.
     811   * @returns {number} the parsed duration in milliseconds.
     812   */
     813  format.parse = function(s) {
     814    switch (type) {
     815      case "short": {
     816        var re = /([0-9,.]+)\s*([a-z]+)/g, a, t = 0;
     817        while (a = re.exec(s)) {
     818          var f = parseFloat(a[0].replace(",", "")), u = 0;
     819          switch (a[2].toLowerCase()) {
     820            case "year": case "years": u = 31536e6; break;
     821            case "week": case "weeks": u = 6048e5; break;
     822            case "day": case "days": u = 864e5; break;
     823            case "hour": case "hours": u = 36e5; break;
     824            case "minute": case "minutes": u = 6e4; break;
     825            case "second": case "seconds": u = 1e3; break;
     826          }
     827          t += f * u;
     828        }
     829        return t;
     830      }
     831      case "long": {
     832        var a = s.replace(",", "").split(":").reverse(), t = 0;
     833        if (a.length) t += parseFloat(a[0]) * 1e3;
     834        if (a.length > 1) t += parseFloat(a[1]) * 6e4;
     835        if (a.length > 2) t += parseFloat(a[2]) * 36e5;
     836        if (a.length > 3) t += parseFloat(a[3]) * 864e5;
     837        return t;
     838      }
     839    }
     840  }
     841
     842  return format;
     843};
     844/**
     845 * Returns a default number format.
     846 *
     847 * @class Represents a number format, converting between a <tt>number</tt> and a
     848 * <tt>string</tt>. This class allows numbers to be formatted with variable
     849 * precision (both for the integral and fractional part of the number), optional
     850 * thousands grouping, and optional padding. The thousands (",") and decimal
     851 * (".") separator can be customized.
     852 *
     853 * @returns {pv.Format.number} a number format.
     854 */
     855pv.Format.number = function() {
     856  var mini = 0, // default minimum integer digits
     857      maxi = Infinity, // default maximum integer digits
     858      mins = 0, // mini, including group separators
     859      minf = 0, // default minimum fraction digits
     860      maxf = 0, // default maximum fraction digits
     861      maxk = 1, // 10^maxf
     862      padi = "0", // default integer pad
     863      padf = "0", // default fraction pad
     864      padg = true, // whether group separator affects integer padding
     865      decimal = ".", // default decimal separator
     866      group = ","; // default group separator
     867
     868  /** @private */
     869  function format(x) {
     870    /* Round the fractional part, and split on decimal separator. */
     871    if (Infinity > maxf) x = Math.round(x * maxk) / maxk;
     872    var s = String(Math.abs(x)).split(".");
     873
     874    /* Pad, truncate and group the integral part. */
     875    var i = s[0], n = (x < 0) ? "-" : "";
     876    if (i.length > maxi) i = i.substring(i.length - maxi);
     877    if (padg && (i.length < mini)) i = n + new Array(mini - i.length + 1).join(padi) + i;
     878    if (i.length > 3) i = i.replace(/\B(?=(?:\d{3})+(?!\d))/g, group);
     879    if (!padg && (i.length < mins)) i = new Array(mins - i.length + 1).join(padi) + n + i;
     880    s[0] = i;
     881
     882    /* Pad the fractional part. */
     883    var f = s[1] || "";
     884    if (f.length < minf) s[1] = f + new Array(minf - f.length + 1).join(padf);
     885
     886    return s.join(decimal);
     887  }
     888
     889  /**
     890   * @function
     891   * @name pv.Format.number.prototype.format
     892   * @param {number} x
     893   * @returns {string}
     894   */
     895  format.format = format;
     896
     897  /**
     898   * Parses the specified string as a number. Before parsing, leading and
     899   * trailing padding is removed. Group separators are also removed, and the
     900   * decimal separator is replaced with the standard point ("."). The integer
     901   * part is truncated per the maximum integer digits, and the fraction part is
     902   * rounded per the maximum fraction digits.
     903   *
     904   * @function
     905   * @name pv.Format.number.prototype.parse
     906   * @param {string} x the string to parse.
     907   * @returns {number} the parsed number.
     908   */
     909  format.parse = function(x) {
     910    var re = pv.Format.re;
     911
     912    /* Remove leading and trailing padding. Split on the decimal separator. */
     913    var s = String(x)
     914        .replace(new RegExp("^(" + re(padi) + ")*"), "")
     915        .replace(new RegExp("(" + re(padf) + ")*$"), "")
     916        .split(decimal);
     917
     918    /* Remove grouping and truncate the integral part. */
     919    var i = s[0].replace(new RegExp(re(group), "g"), "");
     920    if (i.length > maxi) i = i.substring(i.length - maxi);
     921
     922    /* Round the fractional part. */
     923    var f = s[1] ? Number("0." + s[1]) : 0;
     924    if (Infinity > maxf) f = Math.round(f * maxk) / maxk;
     925
     926    return Math.round(i) + f;
     927  };
     928
     929  /**
     930   * Sets or gets the minimum and maximum number of integer digits. This
     931   * controls the number of decimal digits to display before the decimal
     932   * separator for the integral part of the number. If the number of digits is
     933   * smaller than the minimum, the digits are padded; if the number of digits is
     934   * larger, the digits are truncated, showing only the lower-order digits. The
     935   * default range is [0, Infinity].
     936   *
     937   * <p>If only one argument is specified to this method, this value is used as
     938   * both the minimum and maximum number. If no arguments are specified, a
     939   * two-element array is returned containing the minimum and the maximum.
     940   *
     941   * @function
     942   * @name pv.Format.number.prototype.integerDigits
     943   * @param {number} [min] the minimum integer digits.
     944   * @param {number} [max] the maximum integer digits.
     945   * @returns {pv.Format.number} <tt>this</tt>, or the current integer digits.
     946   */
     947  format.integerDigits = function(min, max) {
     948    if (arguments.length) {
     949      mini = Number(min);
     950      maxi = (arguments.length > 1) ? Number(max) : mini;
     951      mins = mini + Math.floor(mini / 3) * group.length;
     952      return this;
     953    }
     954    return [mini, maxi];
     955  };
     956
     957  /**
     958   * Sets or gets the minimum and maximum number of fraction digits. The
     959   * controls the number of decimal digits to display after the decimal
     960   * separator for the fractional part of the number. If the number of digits is
     961   * smaller than the minimum, the digits are padded; if the number of digits is
     962   * larger, the fractional part is rounded, showing only the higher-order
     963   * digits. The default range is [0, 0].
     964   *
     965   * <p>If only one argument is specified to this method, this value is used as
     966   * both the minimum and maximum number. If no arguments are specified, a
     967   * two-element array is returned containing the minimum and the maximum.
     968   *
     969   * @function
     970   * @name pv.Format.number.prototype.fractionDigits
     971   * @param {number} [min] the minimum fraction digits.
     972   * @param {number} [max] the maximum fraction digits.
     973   * @returns {pv.Format.number} <tt>this</tt>, or the current fraction digits.
     974   */
     975  format.fractionDigits = function(min, max) {
     976    if (arguments.length) {
     977      minf = Number(min);
     978      maxf = (arguments.length > 1) ? Number(max) : minf;
     979      maxk = Math.pow(10, maxf);
     980      return this;
     981    }
     982    return [minf, maxf];
     983  };
     984
     985  /**
     986   * Sets or gets the character used to pad the integer part. The integer pad is
     987   * used when the number of integer digits is smaller than the minimum. The
     988   * default pad character is "0" (zero).
     989   *
     990   * @param {string} [x] the new pad character.
     991   * @returns {pv.Format.number} <tt>this</tt> or the current pad character.
     992   */
     993  format.integerPad = function(x) {
     994    if (arguments.length) {
     995      padi = String(x);
     996      padg = /\d/.test(padi);
     997      return this;
     998    }
     999    return padi;
     1000  };
     1001
     1002  /**
     1003   * Sets or gets the character used to pad the fration part. The fraction pad
     1004   * is used when the number of fraction digits is smaller than the minimum. The
     1005   * default pad character is "0" (zero).
     1006   *
     1007   * @param {string} [x] the new pad character.
     1008   * @returns {pv.Format.number} <tt>this</tt> or the current pad character.
     1009   */
     1010  format.fractionPad = function(x) {
     1011    if (arguments.length) {
     1012      padf = String(x);
     1013      return this;
     1014    }
     1015    return padf;
     1016  };
     1017
     1018  /**
     1019   * Sets or gets the character used as the decimal point, separating the
     1020   * integer and fraction parts of the number. The default decimal point is ".".
     1021   *
     1022   * @param {string} [x] the new decimal separator.
     1023   * @returns {pv.Format.number} <tt>this</tt> or the current decimal separator.
     1024   */
     1025  format.decimal = function(x) {
     1026    if (arguments.length) {
     1027      decimal = String(x);
     1028      return this;
     1029    }
     1030    return decimal;
     1031  };
     1032
     1033  /**
     1034   * Sets or gets the character used as the group separator, grouping integer
     1035   * digits by thousands. The default decimal point is ",". Grouping can be
     1036   * disabled by using "" for the separator.
     1037   *
     1038   * @param {string} [x] the new group separator.
     1039   * @returns {pv.Format.number} <tt>this</tt> or the current group separator.
     1040   */
     1041  format.group = function(x) {
     1042    if (arguments.length) {
     1043      group = x ? String(x) : "";
     1044      mins = mini + Math.floor(mini / 3) * group.length;
     1045      return this;
     1046    }
     1047    return group;
     1048  };
     1049
     1050  return format;
     1051};
     1052/**
     1053 * @private A private variant of Array.prototype.map that supports the index
     1054 * property.
     1055 */
     1056pv.map = function(array, f) {
     1057  var o = {};
     1058  return f
     1059      ? array.map(function(d, i) { o.index = i; return f.call(o, d); })
     1060      : array.slice();
     1061};
     1062
     1063/**
     1064 * Concatenates the specified array with itself <i>n</i> times. For example,
     1065 * <tt>pv.repeat([1, 2])</tt> returns [1, 2, 1, 2].
     1066 *
     1067 * @param {array} a an array.
     1068 * @param {number} [n] the number of times to repeat; defaults to two.
     1069 * @returns {array} an array that repeats the specified array.
     1070 */
     1071pv.repeat = function(array, n) {
     1072  if (arguments.length == 1) n = 2;
     1073  return pv.blend(pv.range(n).map(function() { return array; }));
     1074};
     1075
     1076/**
     1077 * Given two arrays <tt>a</tt> and <tt>b</tt>, <style
     1078 * type="text/css">sub{line-height:0}</style> returns an array of all possible
     1079 * pairs of elements [a<sub>i</sub>, b<sub>j</sub>]. The outer loop is on array
     1080 * <i>a</i>, while the inner loop is on <i>b</i>, such that the order of
     1081 * returned elements is [a<sub>0</sub>, b<sub>0</sub>], [a<sub>0</sub>,
     1082 * b<sub>1</sub>], ... [a<sub>0</sub>, b<sub>m</sub>], [a<sub>1</sub>,
     1083 * b<sub>0</sub>], [a<sub>1</sub>, b<sub>1</sub>], ... [a<sub>1</sub>,
     1084 * b<sub>m</sub>], ... [a<sub>n</sub>, b<sub>m</sub>]. If either array is empty,
     1085 * an empty array is returned.
     1086 *
     1087 * @param {array} a an array.
     1088 * @param {array} b an array.
     1089 * @returns {array} an array of pairs of elements in <tt>a</tt> and <tt>b</tt>.
     1090 */
     1091pv.cross = function(a, b) {
     1092  var array = [];
     1093  for (var i = 0, n = a.length, m = b.length; i < n; i++) {
     1094    for (var j = 0, x = a[i]; j < m; j++) {
     1095      array.push([x, b[j]]);
     1096    }
     1097  }
     1098  return array;
     1099};
     1100
     1101/**
     1102 * Given the specified array of arrays, concatenates the arrays into a single
     1103 * array. If the individual arrays are explicitly known, an alternative to blend
     1104 * is to use JavaScript's <tt>concat</tt> method directly. These two equivalent
     1105 * expressions:<ul>
     1106 *
     1107 * <li><tt>pv.blend([[1, 2, 3], ["a", "b", "c"]])</tt>
     1108 * <li><tt>[1, 2, 3].concat(["a", "b", "c"])</tt>
     1109 *
     1110 * </ul>return [1, 2, 3, "a", "b", "c"].
     1111 *
     1112 * @param {array[]} arrays an array of arrays.
     1113 * @returns {array} an array containing all the elements of each array in
     1114 * <tt>arrays</tt>.
     1115 */
     1116pv.blend = function(arrays) {
     1117  return Array.prototype.concat.apply([], arrays);
     1118};
     1119
     1120/**
     1121 * Given the specified array of arrays, <style
     1122 * type="text/css">sub{line-height:0}</style> transposes each element
     1123 * array<sub>ij</sub> with array<sub>ji</sub>. If the array has dimensions
     1124 * <i>n</i>&times;<i>m</i>, it will have dimensions <i>m</i>&times;<i>n</i>
     1125 * after this method returns. This method transposes the elements of the array
     1126 * in place, mutating the array, and returning a reference to the array.
     1127 *
     1128 * @param {array[]} arrays an array of arrays.
     1129 * @returns {array[]} the passed-in array, after transposing the elements.
     1130 */
     1131pv.transpose = function(arrays) {
     1132  var n = arrays.length, m = pv.max(arrays, function(d) { return d.length; });
     1133
     1134  if (m > n) {
     1135    arrays.length = m;
     1136    for (var i = n; i < m; i++) {
     1137      arrays[i] = new Array(n);
     1138    }
     1139    for (var i = 0; i < n; i++) {
     1140      for (var j = i + 1; j < m; j++) {
     1141        var t = arrays[i][j];
     1142        arrays[i][j] = arrays[j][i];
     1143        arrays[j][i] = t;
     1144      }
     1145    }
     1146  } else {
     1147    for (var i = 0; i < m; i++) {
     1148      arrays[i].length = n;
     1149    }
     1150    for (var i = 0; i < n; i++) {
     1151      for (var j = 0; j < i; j++) {
     1152        var t = arrays[i][j];
     1153        arrays[i][j] = arrays[j][i];
     1154        arrays[j][i] = t;
     1155      }
     1156    }
     1157  }
     1158
     1159  arrays.length = m;
     1160  for (var i = 0; i < m; i++) {
     1161    arrays[i].length = n;
     1162  }
     1163
     1164  return arrays;
     1165};
     1166
     1167/**
     1168 * Returns a normalized copy of the specified array, such that the sum of the
     1169 * returned elements sum to one. If the specified array is not an array of
     1170 * numbers, an optional accessor function <tt>f</tt> can be specified to map the
     1171 * elements to numbers. For example, if <tt>array</tt> is an array of objects,
     1172 * and each object has a numeric property "foo", the expression
     1173 *
     1174 * <pre>pv.normalize(array, function(d) d.foo)</pre>
     1175 *
     1176 * returns a normalized array on the "foo" property. If an accessor function is
     1177 * not specified, the identity function is used. Accessor functions can refer to
     1178 * <tt>this.index</tt>.
     1179 *
     1180 * @param {array} array an array of objects, or numbers.
     1181 * @param {function} [f] an optional accessor function.
     1182 * @returns {number[]} an array of numbers that sums to one.
     1183 */
     1184pv.normalize = function(array, f) {
     1185  var norm = pv.map(array, f), sum = pv.sum(norm);
     1186  for (var i = 0; i < norm.length; i++) norm[i] /= sum;
     1187  return norm;
     1188};
     1189
     1190/**
     1191 * Returns a permutation of the specified array, using the specified array of
     1192 * indexes. The returned array contains the corresponding element in
     1193 * <tt>array</tt> for each index in <tt>indexes</tt>, in order. For example,
     1194 *
     1195 * <pre>pv.permute(["a", "b", "c"], [1, 2, 0])</pre>
     1196 *
     1197 * returns <tt>["b", "c", "a"]</tt>. It is acceptable for the array of indexes
     1198 * to be a different length from the array of elements, and for indexes to be
     1199 * duplicated or omitted. The optional accessor function <tt>f</tt> can be used
     1200 * to perform a simultaneous mapping of the array elements. Accessor functions
     1201 * can refer to <tt>this.index</tt>.
     1202 *
     1203 * @param {array} array an array.
     1204 * @param {number[]} indexes an array of indexes into <tt>array</tt>.
     1205 * @param {function} [f] an optional accessor function.
     1206 * @returns {array} an array of elements from <tt>array</tt>; a permutation.
     1207 */
     1208pv.permute = function(array, indexes, f) {
     1209  if (!f) f = pv.identity;
     1210  var p = new Array(indexes.length), o = {};
     1211  indexes.forEach(function(j, i) { o.index = j; p[i] = f.call(o, array[j]); });
     1212  return p;
     1213};
     1214
     1215/**
     1216 * Returns a map from key to index for the specified <tt>keys</tt> array. For
     1217 * example,
     1218 *
     1219 * <pre>pv.numerate(["a", "b", "c"])</pre>
     1220 *
     1221 * returns <tt>{a: 0, b: 1, c: 2}</tt>. Note that since JavaScript maps only
     1222 * support string keys, <tt>keys</tt> must contain strings, or other values that
     1223 * naturally map to distinct string values. Alternatively, an optional accessor
     1224 * function <tt>f</tt> can be specified to compute the string key for the given
     1225 * element. Accessor functions can refer to <tt>this.index</tt>.
     1226 *
     1227 * @param {array} keys an array, usually of string keys.
     1228 * @param {function} [f] an optional key function.
     1229 * @returns a map from key to index.
     1230 */
     1231pv.numerate = function(keys, f) {
     1232  if (!f) f = pv.identity;
     1233  var map = {}, o = {};
     1234  keys.forEach(function(x, i) { o.index = i; map[f.call(o, x)] = i; });
     1235  return map;
     1236};
     1237
     1238/**
     1239 * Returns the unique elements in the specified array, in the order they appear.
     1240 * Note that since JavaScript maps only support string keys, <tt>array</tt> must
     1241 * contain strings, or other values that naturally map to distinct string
     1242 * values. Alternatively, an optional accessor function <tt>f</tt> can be
     1243 * specified to compute the string key for the given element. Accessor functions
     1244 * can refer to <tt>this.index</tt>.
     1245 *
     1246 * @param {array} array an array, usually of string keys.
     1247 * @param {function} [f] an optional key function.
     1248 * @returns {array} the unique values.
     1249 */
     1250pv.uniq = function(array, f) {
     1251  if (!f) f = pv.identity;
     1252  var map = {}, keys = [], o = {}, y;
     1253  array.forEach(function(x, i) {
     1254    o.index = i;
     1255    y = f.call(o, x);
     1256    if (!(y in map)) map[y] = keys.push(y);
     1257  });
     1258  return keys;
     1259};
     1260
     1261/**
     1262 * The comparator function for natural order. This can be used in conjunction with
     1263 * the built-in array <tt>sort</tt> method to sort elements by their natural
     1264 * order, ascending. Note that if no comparator function is specified to the
     1265 * built-in <tt>sort</tt> method, the default order is lexicographic, <i>not</i>
     1266 * natural!
     1267 *
     1268 * @see <a
     1269 * href="http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/sort">Array.sort</a>.
     1270 * @param a an element to compare.
     1271 * @param b an element to compare.
     1272 * @returns {number} negative if a &lt; b; positive if a &gt; b; otherwise 0.
     1273 */
     1274pv.naturalOrder = function(a, b) {
     1275  return (a < b) ? -1 : ((a > b) ? 1 : 0);
     1276};
     1277
     1278/**
     1279 * The comparator function for reverse natural order. This can be used in
     1280 * conjunction with the built-in array <tt>sort</tt> method to sort elements by
     1281 * their natural order, descending. Note that if no comparator function is
     1282 * specified to the built-in <tt>sort</tt> method, the default order is
     1283 * lexicographic, <i>not</i> natural!
     1284 *
     1285 * @see #naturalOrder
     1286 * @param a an element to compare.
     1287 * @param b an element to compare.
     1288 * @returns {number} negative if a &lt; b; positive if a &gt; b; otherwise 0.
     1289 */
     1290pv.reverseOrder = function(b, a) {
     1291  return (a < b) ? -1 : ((a > b) ? 1 : 0);
     1292};
     1293
     1294/**
     1295 * Searches the specified array of numbers for the specified value using the
     1296 * binary search algorithm. The array must be sorted (as by the <tt>sort</tt>
     1297 * method) prior to making this call. If it is not sorted, the results are
     1298 * undefined. If the array contains multiple elements with the specified value,
     1299 * there is no guarantee which one will be found.
     1300 *
     1301 * <p>The <i>insertion point</i> is defined as the point at which the value
     1302 * would be inserted into the array: the index of the first element greater than
     1303 * the value, or <tt>array.length</tt>, if all elements in the array are less
     1304 * than the specified value. Note that this guarantees that the return value
     1305 * will be nonnegative if and only if the value is found.
     1306 *
     1307 * @param {number[]} array the array to be searched.
     1308 * @param {number} value the value to be searched for.
     1309 * @returns the index of the search value, if it is contained in the array;
     1310 * otherwise, (-(<i>insertion point</i>) - 1).
     1311 * @param {function} [f] an optional key function.
     1312 */
     1313pv.search = function(array, value, f) {
     1314  if (!f) f = pv.identity;
     1315  var low = 0, high = array.length - 1;
     1316  while (low <= high) {
     1317    var mid = (low + high) >> 1, midValue = f(array[mid]);
     1318    if (midValue < value) low = mid + 1;
     1319    else if (midValue > value) high = mid - 1;
     1320    else return mid;
     1321  }
     1322  return -low - 1;
     1323};
     1324
     1325pv.search.index = function(array, value, f) {
     1326  var i = pv.search(array, value, f);
     1327  return (i < 0) ? (-i - 1) : i;
     1328};
     1329/**
     1330 * Returns an array of numbers, starting at <tt>start</tt>, incrementing by
     1331 * <tt>step</tt>, until <tt>stop</tt> is reached. The stop value is
     1332 * exclusive. If only a single argument is specified, this value is interpeted
     1333 * as the <i>stop</i> value, with the <i>start</i> value as zero. If only two
     1334 * arguments are specified, the step value is implied to be one.
     1335 *
     1336 * <p>The method is modeled after the built-in <tt>range</tt> method from
     1337 * Python. See the Python documentation for more details.
     1338 *
     1339 * @see <a href="http://docs.python.org/library/functions.html#range">Python range</a>
     1340 * @param {number} [start] the start value.
     1341 * @param {number} stop the stop value.
     1342 * @param {number} [step] the step value.
     1343 * @returns {number[]} an array of numbers.
     1344 */
     1345pv.range = function(start, stop, step) {
     1346  if (arguments.length == 1) {
     1347    stop = start;
     1348    start = 0;
     1349  }
     1350  if (step == undefined) step = 1;
     1351  if ((stop - start) / step == Infinity) throw new Error("range must be finite");
     1352  var array = [], i = 0, j;
     1353  if (step < 0) {
     1354    while ((j = start + step * i++) > stop) {
     1355      array.push(j);
     1356    }
     1357  } else {
     1358    while ((j = start + step * i++) < stop) {
     1359      array.push(j);
     1360    }
     1361  }
     1362  return array;
     1363};
     1364
     1365/**
     1366 * Returns a random number in the range [<tt>start</tt>, <tt>stop</tt>) that is
     1367 * a multiple of <tt>step</tt>. More specifically, the returned number is of the
     1368 * form <tt>start</tt> + <i>n</i> * <tt>step</tt>, where <i>n</i> is a
     1369 * nonnegative integer. If <tt>step</tt> is not specified, it defaults to 1,
     1370 * returning a random integer if <tt>start</tt> is also an integer.
     1371 *
     1372 * @param {number} [start] the start value value.
     1373 * @param {number} stop the stop value.
     1374 * @param {number} [step] the step value.
     1375 * @returns {number} a random number between <i>start</i> and <i>stop</i>.
     1376 */
     1377pv.random = function(start, stop, step) {
     1378  if (arguments.length == 1) {
     1379    stop = start;
     1380    start = 0;
     1381  }
     1382  if (step == undefined) step = 1;
     1383  return step
     1384      ? (Math.floor(Math.random() * (stop - start) / step) * step + start)
     1385      : (Math.random() * (stop - start) + start);
     1386};
     1387
     1388/**
     1389 * Returns the sum of the specified array. If the specified array is not an
     1390 * array of numbers, an optional accessor function <tt>f</tt> can be specified
     1391 * to map the elements to numbers. See {@link #normalize} for an example.
     1392 * Accessor functions can refer to <tt>this.index</tt>.
     1393 *
     1394 * @param {array} array an array of objects, or numbers.
     1395 * @param {function} [f] an optional accessor function.
     1396 * @returns {number} the sum of the specified array.
     1397 */
     1398pv.sum = function(array, f) {
     1399  var o = {};
     1400  return array.reduce(f
     1401      ? function(p, d, i) { o.index = i; return p + f.call(o, d); }
     1402      : function(p, d) { return p + d; }, 0);
     1403};
     1404
     1405/**
     1406 * Returns the maximum value of the specified array. If the specified array is
     1407 * not an array of numbers, an optional accessor function <tt>f</tt> can be
     1408 * specified to map the elements to numbers. See {@link #normalize} for an
     1409 * example. Accessor functions can refer to <tt>this.index</tt>.
     1410 *
     1411 * @param {array} array an array of objects, or numbers.
     1412 * @param {function} [f] an optional accessor function.
     1413 * @returns {number} the maximum value of the specified array.
     1414 */
     1415pv.max = function(array, f) {
     1416  if (f == pv.index) return array.length - 1;
     1417  return Math.max.apply(null, f ? pv.map(array, f) : array);
     1418};
     1419
     1420/**
     1421 * Returns the index of the maximum value of the specified array. If the
     1422 * specified array is not an array of numbers, an optional accessor function
     1423 * <tt>f</tt> can be specified to map the elements to numbers. See
     1424 * {@link #normalize} for an example. Accessor functions can refer to
     1425 * <tt>this.index</tt>.
     1426 *
     1427 * @param {array} array an array of objects, or numbers.
     1428 * @param {function} [f] an optional accessor function.
     1429 * @returns {number} the index of the maximum value of the specified array.
     1430 */
     1431pv.max.index = function(array, f) {
     1432  if (!array.length) return -1;
     1433  if (f == pv.index) return array.length - 1;
     1434  if (!f) f = pv.identity;
     1435  var maxi = 0, maxx = -Infinity, o = {};
     1436  for (var i = 0; i < array.length; i++) {
     1437    o.index = i;
     1438    var x = f.call(o, array[i]);
     1439    if (x > maxx) {
     1440      maxx = x;
     1441      maxi = i;
     1442    }
     1443  }
     1444  return maxi;
     1445}
     1446
     1447/**
     1448 * Returns the minimum value of the specified array of numbers. If the specified
     1449 * array is not an array of numbers, an optional accessor function <tt>f</tt>
     1450 * can be specified to map the elements to numbers. See {@link #normalize} for
     1451 * an example. Accessor functions can refer to <tt>this.index</tt>.
     1452 *
     1453 * @param {array} array an array of objects, or numbers.
     1454 * @param {function} [f] an optional accessor function.
     1455 * @returns {number} the minimum value of the specified array.
     1456 */
     1457pv.min = function(array, f) {
     1458  if (f == pv.index) return 0;
     1459  return Math.min.apply(null, f ? pv.map(array, f) : array);
     1460};
     1461
     1462/**
     1463 * Returns the index of the minimum value of the specified array. If the
     1464 * specified array is not an array of numbers, an optional accessor function
     1465 * <tt>f</tt> can be specified to map the elements to numbers. See
     1466 * {@link #normalize} for an example. Accessor functions can refer to
     1467 * <tt>this.index</tt>.
     1468 *
     1469 * @param {array} array an array of objects, or numbers.
     1470 * @param {function} [f] an optional accessor function.
     1471 * @returns {number} the index of the minimum value of the specified array.
     1472 */
     1473pv.min.index = function(array, f) {
     1474  if (!array.length) return -1;
     1475  if (f == pv.index) return 0;
     1476  if (!f) f = pv.identity;
     1477  var mini = 0, minx = Infinity, o = {};
     1478  for (var i = 0; i < array.length; i++) {
     1479    o.index = i;
     1480    var x = f.call(o, array[i]);
     1481    if (x < minx) {
     1482      minx = x;
     1483      mini = i;
     1484    }
     1485  }
     1486  return mini;
     1487}
     1488
     1489/**
     1490 * Returns the arithmetic mean, or average, of the specified array. If the
     1491 * specified array is not an array of numbers, an optional accessor function
     1492 * <tt>f</tt> can be specified to map the elements to numbers. See
     1493 * {@link #normalize} for an example. Accessor functions can refer to
     1494 * <tt>this.index</tt>.
     1495 *
     1496 * @param {array} array an array of objects, or numbers.
     1497 * @param {function} [f] an optional accessor function.
     1498 * @returns {number} the mean of the specified array.
     1499 */
     1500pv.mean = function(array, f) {
     1501  return pv.sum(array, f) / array.length;
     1502};
     1503
     1504/**
     1505 * Returns the median of the specified array. If the specified array is not an
     1506 * array of numbers, an optional accessor function <tt>f</tt> can be specified
     1507 * to map the elements to numbers. See {@link #normalize} for an example.
     1508 * Accessor functions can refer to <tt>this.index</tt>.
     1509 *
     1510 * @param {array} array an array of objects, or numbers.
     1511 * @param {function} [f] an optional accessor function.
     1512 * @returns {number} the median of the specified array.
     1513 */
     1514pv.median = function(array, f) {
     1515  if (f == pv.index) return (array.length - 1) / 2;
     1516  array = pv.map(array, f).sort(pv.naturalOrder);
     1517  if (array.length % 2) return array[Math.floor(array.length / 2)];
     1518  var i = array.length / 2;
     1519  return (array[i - 1] + array[i]) / 2;
     1520};
     1521
     1522/**
     1523 * Returns the unweighted variance of the specified array. If the specified
     1524 * array is not an array of numbers, an optional accessor function <tt>f</tt>
     1525 * can be specified to map the elements to numbers. See {@link #normalize} for
     1526 * an example. Accessor functions can refer to <tt>this.index</tt>.
     1527 *
     1528 * @param {array} array an array of objects, or numbers.
     1529 * @param {function} [f] an optional accessor function.
     1530 * @returns {number} the variance of the specified array.
     1531 */
     1532pv.variance = function(array, f) {
     1533  if (array.length < 1) return NaN;
     1534  if (array.length == 1) return 0;
     1535  var mean = pv.mean(array, f), sum = 0, o = {};
     1536  if (!f) f = pv.identity;
     1537  for (var i = 0; i < array.length; i++) {
     1538    o.index = i;
     1539    var d = f.call(o, array[i]) - mean;
     1540    sum += d * d;
     1541  }
     1542  return sum;
     1543};
     1544
     1545/**
     1546 * Returns an unbiased estimation of the standard deviation of a population,
     1547 * given the specified random sample. If the specified array is not an array of
     1548 * numbers, an optional accessor function <tt>f</tt> can be specified to map the
     1549 * elements to numbers. See {@link #normalize} for an example. Accessor
     1550 * functions can refer to <tt>this.index</tt>.
     1551 *
     1552 * @param {array} array an array of objects, or numbers.
     1553 * @param {function} [f] an optional accessor function.
     1554 * @returns {number} the standard deviation of the specified array.
     1555 */
     1556pv.deviation = function(array, f) {
     1557  return Math.sqrt(pv.variance(array, f) / (array.length - 1));
     1558};
     1559
     1560/**
     1561 * Returns the logarithm with a given base value.
     1562 *
     1563 * @param {number} x the number for which to compute the logarithm.
     1564 * @param {number} b the base of the logarithm.
     1565 * @returns {number} the logarithm value.
     1566 */
     1567pv.log = function(x, b) {
     1568  return Math.log(x) / Math.log(b);
     1569};
     1570
     1571/**
     1572 * Computes a zero-symmetric logarithm. Computes the logarithm of the absolute
     1573 * value of the input, and determines the sign of the output according to the
     1574 * sign of the input value.
     1575 *
     1576 * @param {number} x the number for which to compute the logarithm.
     1577 * @param {number} b the base of the logarithm.
     1578 * @returns {number} the symmetric log value.
     1579 */
     1580pv.logSymmetric = function(x, b) {
     1581  return (x == 0) ? 0 : ((x < 0) ? -pv.log(-x, b) : pv.log(x, b));
     1582};
     1583
     1584/**
     1585 * Computes a zero-symmetric logarithm, with adjustment to values between zero
     1586 * and the logarithm base. This adjustment introduces distortion for values less
     1587 * than the base number, but enables simultaneous plotting of log-transformed
     1588 * data involving both positive and negative numbers.
     1589 *
     1590 * @param {number} x the number for which to compute the logarithm.
     1591 * @param {number} b the base of the logarithm.
     1592 * @returns {number} the adjusted, symmetric log value.
     1593 */
     1594pv.logAdjusted = function(x, b) {
     1595  if (!isFinite(x)) return x;
     1596  var negative = x < 0;
     1597  if (x < b) x += (b - x) / b;
     1598  return negative ? -pv.log(x, b) : pv.log(x, b);
     1599};
     1600
     1601/**
     1602 * Rounds an input value down according to its logarithm. The method takes the
     1603 * floor of the logarithm of the value and then uses the resulting value as an
     1604 * exponent for the base value.
     1605 *
     1606 * @param {number} x the number for which to compute the logarithm floor.
     1607 * @param {number} b the base of the logarithm.
     1608 * @returns {number} the rounded-by-logarithm value.
     1609 */
     1610pv.logFloor = function(x, b) {
     1611  return (x > 0)
     1612      ? Math.pow(b, Math.floor(pv.log(x, b)))
     1613      : -Math.pow(b, -Math.floor(-pv.log(-x, b)));
     1614};
     1615
     1616/**
     1617 * Rounds an input value up according to its logarithm. The method takes the
     1618 * ceiling of the logarithm of the value and then uses the resulting value as an
     1619 * exponent for the base value.
     1620 *
     1621 * @param {number} x the number for which to compute the logarithm ceiling.
     1622 * @param {number} b the base of the logarithm.
     1623 * @returns {number} the rounded-by-logarithm value.
     1624 */
     1625pv.logCeil = function(x, b) {
     1626  return (x > 0)
     1627      ? Math.pow(b, Math.ceil(pv.log(x, b)))
     1628      : -Math.pow(b, -Math.ceil(-pv.log(-x, b)));
     1629};
     1630
     1631(function() {
     1632  var radians = Math.PI / 180,
     1633      degrees = 180 / Math.PI;
     1634
     1635  /** Returns the number of radians corresponding to the specified degrees. */
     1636  pv.radians = function(degrees) { return radians * degrees; };
     1637
     1638  /** Returns the number of degrees corresponding to the specified radians. */
     1639  pv.degrees = function(radians) { return degrees * radians; };
     1640})();
     1641/**
     1642 * Returns all of the property names (keys) of the specified object (a map). The
     1643 * order of the returned array is not defined.
     1644 *
     1645 * @param map an object.
     1646 * @returns {string[]} an array of strings corresponding to the keys.
     1647 * @see #entries
     1648 */
     1649pv.keys = function(map) {
     1650  var array = [];
     1651  for (var key in map) {
     1652    array.push(key);
     1653  }
     1654  return array;
     1655};
     1656
     1657/**
     1658 * Returns all of the entries (key-value pairs) of the specified object (a
     1659 * map). The order of the returned array is not defined. Each key-value pair is
     1660 * represented as an object with <tt>key</tt> and <tt>value</tt> attributes,
     1661 * e.g., <tt>{key: "foo", value: 42}</tt>.
     1662 *
     1663 * @param map an object.
     1664 * @returns {array} an array of key-value pairs corresponding to the keys.
     1665 */
     1666pv.entries = function(map) {
     1667  var array = [];
     1668  for (var key in map) {
     1669    array.push({ key: key, value: map[key] });
     1670  }
     1671  return array;
     1672};
     1673
     1674/**
     1675 * Returns all of the values (attribute values) of the specified object (a
     1676 * map). The order of the returned array is not defined.
     1677 *
     1678 * @param map an object.
     1679 * @returns {array} an array of objects corresponding to the values.
     1680 * @see #entries
     1681 */
     1682pv.values = function(map) {
     1683  var array = [];
     1684  for (var key in map) {
     1685    array.push(map[key]);
     1686  }
     1687  return array;
     1688};
     1689
     1690/**
     1691 * Returns a map constructed from the specified <tt>keys</tt>, using the
     1692 * function <tt>f</tt> to compute the value for each key. The single argument to
     1693 * the value function is the key. The callback is invoked only for indexes of
     1694 * the array which have assigned values; it is not invoked for indexes which
     1695 * have been deleted or which have never been assigned values.
     1696 *
     1697 * <p>For example, this expression creates a map from strings to string length:
     1698 *
     1699 * <pre>pv.dict(["one", "three", "seventeen"], function(s) s.length)</pre>
     1700 *
     1701 * The returned value is <tt>{one: 3, three: 5, seventeen: 9}</tt>. Accessor
     1702 * functions can refer to <tt>this.index</tt>.
     1703 *
     1704 * @param {array} keys an array.
     1705 * @param {function} f a value function.
     1706 * @returns a map from keys to values.
     1707 */
     1708pv.dict = function(keys, f) {
     1709  var m = {}, o = {};
     1710  for (var i = 0; i < keys.length; i++) {
     1711    if (i in keys) {
     1712      var k = keys[i];
     1713      o.index = i;
     1714      m[k] = f.call(o, k);
     1715    }
     1716  }
     1717  return m;
     1718};
     1719/**
     1720 * Returns a {@link pv.Dom} operator for the given map. This is a convenience
     1721 * factory method, equivalent to <tt>new pv.Dom(map)</tt>. To apply the operator
     1722 * and retrieve the root node, call {@link pv.Dom#root}; to retrieve all nodes
     1723 * flattened, use {@link pv.Dom#nodes}.
     1724 *
     1725 * @see pv.Dom
     1726 * @param map a map from which to construct a DOM.
     1727 * @returns {pv.Dom} a DOM operator for the specified map.
     1728 */
     1729pv.dom = function(map) {
     1730  return new pv.Dom(map);
     1731};
     1732
     1733/**
     1734 * Constructs a DOM operator for the specified map. This constructor should not
     1735 * be invoked directly; use {@link pv.dom} instead.
     1736 *
     1737 * @class Represets a DOM operator for the specified map. This allows easy
     1738 * transformation of a hierarchical JavaScript object (such as a JSON map) to a
     1739 * W3C Document Object Model hierarchy. For more information on which attributes
     1740 * and methods from the specification are supported, see {@link pv.Dom.Node}.
     1741 *
     1742 * <p>Leaves in the map are determined using an associated <i>leaf</i> function;
     1743 * see {@link #leaf}. By default, leaves are any value whose type is not
     1744 * "object", such as numbers or strings.
     1745 *
     1746 * @param map a map from which to construct a DOM.
     1747 */
     1748pv.Dom = function(map) {
     1749  this.$map = map;
     1750};
     1751
     1752/** @private The default leaf function. */
     1753pv.Dom.prototype.$leaf = function(n) {
     1754  return typeof n != "object";
     1755};
     1756
     1757/**
     1758 * Sets or gets the leaf function for this DOM operator. The leaf function
     1759 * identifies which values in the map are leaves, and which are internal nodes.
     1760 * By default, objects are considered internal nodes, and primitives (such as
     1761 * numbers and strings) are considered leaves.
     1762 *
     1763 * @param {function} f the new leaf function.
     1764 * @returns the current leaf function, or <tt>this</tt>.
     1765 */
     1766pv.Dom.prototype.leaf = function(f) {
     1767  if (arguments.length) {
     1768    this.$leaf = f;
     1769    return this;
     1770  }
     1771  return this.$leaf;
     1772};
     1773
     1774/**
     1775 * Applies the DOM operator, returning the root node.
     1776 *
     1777 * @returns {pv.Dom.Node} the root node.
     1778 * @param {string} [nodeName] optional node name for the root.
     1779 */
     1780pv.Dom.prototype.root = function(nodeName) {
     1781  var leaf = this.$leaf, root = recurse(this.$map);
     1782
     1783  /** @private */
     1784  function recurse(map) {
     1785    var n = new pv.Dom.Node();
     1786    for (var k in map) {
     1787      var v = map[k];
     1788      n.appendChild(leaf(v) ? new pv.Dom.Node(v) : recurse(v)).nodeName = k;
     1789    }
     1790    return n;
     1791  }
     1792
     1793  root.nodeName = nodeName;
     1794  return root;
     1795};
     1796
     1797/**
     1798 * Applies the DOM operator, returning the array of all nodes in preorder
     1799 * traversal.
     1800 *
     1801 * @returns {array} the array of nodes in preorder traversal.
     1802 */
     1803pv.Dom.prototype.nodes = function() {
     1804  return this.root().nodes();
     1805};
     1806
     1807/**
     1808 * Constructs a DOM node for the specified value. Instances of this class are
     1809 * not typically created directly; instead they are generated from a JavaScript
     1810 * map using the {@link pv.Dom} operator.
     1811 *
     1812 * @class Represents a <tt>Node</tt> in the W3C Document Object Model.
     1813 */
     1814pv.Dom.Node = function(value) {
     1815  this.nodeValue = value;
     1816  this.childNodes = [];
     1817};
     1818
     1819/**
     1820 * The node name. When generated from a map, the node name corresponds to the
     1821 * key at the given level in the map. Note that the root node has no associated
     1822 * key, and thus has an undefined node name (and no <tt>parentNode</tt>).
     1823 *
     1824 * @type string
     1825 * @field pv.Dom.Node.prototype.nodeName
     1826 */
     1827
     1828/**
     1829 * The node value. When generated from a map, node value corresponds to the leaf
     1830 * value for leaf nodes, and is undefined for internal nodes.
     1831 *
     1832 * @field pv.Dom.Node.prototype.nodeValue
     1833 */
     1834
     1835/**
     1836 * The array of child nodes. This array is empty for leaf nodes. An easy way to
     1837 * check if child nodes exist is to query <tt>firstChild</tt>.
     1838 *
     1839 * @type array
     1840 * @field pv.Dom.Node.prototype.childNodes
     1841 */
     1842
     1843/**
     1844 * The parent node, which is null for root nodes.
     1845 *
     1846 * @type pv.Dom.Node
     1847 */
     1848pv.Dom.Node.prototype.parentNode = null;
     1849
     1850/**
     1851 * The first child, which is null for leaf nodes.
     1852 *
     1853 * @type pv.Dom.Node
     1854 */
     1855pv.Dom.Node.prototype.firstChild = null;
     1856
     1857/**
     1858 * The last child, which is null for leaf nodes.
     1859 *
     1860 * @type pv.Dom.Node
     1861 */
     1862pv.Dom.Node.prototype.lastChild = null;
     1863
     1864/**
     1865 * The previous sibling node, which is null for the first child.
     1866 *
     1867 * @type pv.Dom.Node
     1868 */
     1869pv.Dom.Node.prototype.previousSibling = null;
     1870
     1871/**
     1872 * The next sibling node, which is null for the last child.
     1873 *
     1874 * @type pv.Dom.Node
     1875 */
     1876pv.Dom.Node.prototype.nextSibling = null;
     1877
     1878/**
     1879 * Removes the specified child node from this node.
     1880 *
     1881 * @throws Error if the specified child is not a child of this node.
     1882 * @returns {pv.Dom.Node} the removed child.
     1883 */
     1884pv.Dom.Node.prototype.removeChild = function(n) {
     1885  var i = this.childNodes.indexOf(n);
     1886  if (i == -1) throw new Error("child not found");
     1887  this.childNodes.splice(i, 1);
     1888  if (n.previousSibling) n.previousSibling.nextSibling = n.nextSibling;
     1889  else this.firstChild = n.nextSibling;
     1890  if (n.nextSibling) n.nextSibling.previousSibling = n.previousSibling;
     1891  else this.lastChild = n.previousSibling;
     1892  delete n.nextSibling;
     1893  delete n.previousSibling;
     1894  delete n.parentNode;
     1895  return n;
     1896};
     1897
     1898/**
     1899 * Appends the specified child node to this node. If the specified child is
     1900 * already part of the DOM, the child is first removed before being added to
     1901 * this node.
     1902 *
     1903 * @returns {pv.Dom.Node} the appended child.
     1904 */
     1905pv.Dom.Node.prototype.appendChild = function(n) {
     1906  if (n.parentNode) n.parentNode.removeChild(n);
     1907  n.parentNode = this;
     1908  n.previousSibling = this.lastChild;
     1909  if (this.lastChild) this.lastChild.nextSibling = n;
     1910  else this.firstChild = n;
     1911  this.lastChild = n;
     1912  this.childNodes.push(n);
     1913  return n;
     1914};
     1915
     1916/**
     1917 * Inserts the specified child <i>n</i> before the given reference child
     1918 * <i>r</i> of this node. If <i>r</i> is null, this method is equivalent to
     1919 * {@link #appendChild}. If <i>n</i> is already part of the DOM, it is first
     1920 * removed before being inserted.
     1921 *
     1922 * @throws Error if <i>r</i> is non-null and not a child of this node.
     1923 * @returns {pv.Dom.Node} the inserted child.
     1924 */
     1925pv.Dom.Node.prototype.insertBefore = function(n, r) {
     1926  if (!r) return this.appendChild(n);
     1927  var i = this.childNodes.indexOf(r);
     1928  if (i == -1) throw new Error("child not found");
     1929  if (n.parentNode) n.parentNode.removeChild(n);
     1930  n.parentNode = this;
     1931  n.nextSibling = r;
     1932  n.previousSibling = r.previousSibling;
     1933  if (r.previousSibling) {
     1934    r.previousSibling.nextSibling = n;
     1935  } else {
     1936    if (r == this.lastChild) this.lastChild = n;
     1937    this.firstChild = n;
     1938  }
     1939  this.childNodes.splice(i, 0, n);
     1940  return n;
     1941};
     1942
     1943/**
     1944 * Replaces the specified child <i>r</i> of this node with the node <i>n</i>. If
     1945 * <i>n</i> is already part of the DOM, it is first removed before being added.
     1946 *
     1947 * @throws Error if <i>r</i> is not a child of this node.
     1948 */
     1949pv.Dom.Node.prototype.replaceChild = function(n, r) {
     1950  var i = this.childNodes.indexOf(r);
     1951  if (i == -1) throw new Error("child not found");
     1952  if (n.parentNode) n.parentNode.removeChild(n);
     1953  n.parentNode = this;
     1954  n.nextSibling = r.nextSibling;
     1955  n.previousSibling = r.previousSibling;
     1956  if (r.previousSibling) r.previousSibling.nextSibling = n;
     1957  else this.firstChild = n;
     1958  if (r.nextSibling) r.nextSibling.previousSibling = n;
     1959  else this.lastChild = n;
     1960  this.childNodes[i] = n;
     1961  return r;
     1962};
     1963
     1964/**
     1965 * Visits each node in the tree in preorder traversal, applying the specified
     1966 * function <i>f</i>. The arguments to the function are:<ol>
     1967 *
     1968 * <li>The current node.
     1969 * <li>The current depth, starting at 0 for the root node.</ol>
     1970 *
     1971 * @param {function} f a function to apply to each node.
     1972 */
     1973pv.Dom.Node.prototype.visitBefore = function(f) {
     1974  function visit(n, i) {
     1975    f(n, i);
     1976    for (var c = n.firstChild; c; c = c.nextSibling) {
     1977      visit(c, i + 1);
     1978    }
     1979  }
     1980  visit(this, 0);
     1981};
     1982
     1983/**
     1984 * Visits each node in the tree in postorder traversal, applying the specified
     1985 * function <i>f</i>. The arguments to the function are:<ol>
     1986 *
     1987 * <li>The current node.
     1988 * <li>The current depth, starting at 0 for the root node.</ol>
     1989 *
     1990 * @param {function} f a function to apply to each node.
     1991 */
     1992pv.Dom.Node.prototype.visitAfter = function(f) {
     1993  function visit(n, i) {
     1994    for (var c = n.firstChild; c; c = c.nextSibling) {
     1995      visit(c, i + 1);
     1996    }
     1997    f(n, i);
     1998  }
     1999  visit(this, 0);
     2000};
     2001
     2002/**
     2003 * Sorts child nodes of this node, and all descendent nodes recursively, using
     2004 * the specified comparator function <tt>f</tt>. The comparator function is
     2005 * passed two nodes to compare.
     2006 *
     2007 * <p>Note: during the sort operation, the comparator function should not rely
     2008 * on the tree being well-formed; the values of <tt>previousSibling</tt> and
     2009 * <tt>nextSibling</tt> for the nodes being compared are not defined during the
     2010 * sort operation.
     2011 *
     2012 * @param {function} f a comparator function.
     2013 * @returns this.
     2014 */
     2015pv.Dom.Node.prototype.sort = function(f) {
     2016  if (this.firstChild) {
     2017    this.childNodes.sort(f);
     2018    var p = this.firstChild = this.childNodes[0], c;
     2019    delete p.previousSibling;
     2020    for (var i = 1; i < this.childNodes.length; i++) {
     2021      p.sort(f);
     2022      c = this.childNodes[i];
     2023      c.previousSibling = p;
     2024      p = p.nextSibling = c;
     2025    }
     2026    this.lastChild = p;
     2027    delete p.nextSibling;
     2028    p.sort(f);
     2029  }
     2030  return this;
     2031};
     2032
     2033/**
     2034 * Reverses all sibling nodes.
     2035 *
     2036 * @returns this.
     2037 */
     2038pv.Dom.Node.prototype.reverse = function() {
     2039  var childNodes = [];
     2040  this.visitAfter(function(n) {
     2041      while (n.lastChild) childNodes.push(n.removeChild(n.lastChild));
     2042      for (var c; c = childNodes.pop();) n.insertBefore(c, n.firstChild);
     2043    });
     2044  return this;
     2045};
     2046
     2047/** Returns all descendants of this node in preorder traversal. */
     2048pv.Dom.Node.prototype.nodes = function() {
     2049  var array = [];
     2050
     2051  /** @private */
     2052  function flatten(node) {
     2053    array.push(node);
     2054    node.childNodes.forEach(flatten);
     2055  }
     2056
     2057  flatten(this, array);
     2058  return array;
     2059};
     2060
     2061/**
     2062 * Toggles the child nodes of this node. If this node is not yet toggled, this
     2063 * method removes all child nodes and appends them to a new <tt>toggled</tt>
     2064 * array attribute on this node. Otherwise, if this node is toggled, this method
     2065 * re-adds all toggled child nodes and deletes the <tt>toggled</tt> attribute.
     2066 *
     2067 * <p>This method has no effect if the node has no child nodes.
     2068 *
     2069 * @param {boolean} [recursive] whether the toggle should apply to descendants.
     2070 */
     2071pv.Dom.Node.prototype.toggle = function(recursive) {
     2072  if (recursive) return this.toggled
     2073      ? this.visitBefore(function(n) { if (n.toggled) n.toggle(); })
     2074      : this.visitAfter(function(n) { if (!n.toggled) n.toggle(); });
     2075  var n = this;
     2076  if (n.toggled) {
     2077    for (var c; c = n.toggled.pop();) n.appendChild(c);
     2078    delete n.toggled;
     2079  } else if (n.lastChild) {
     2080    n.toggled = [];
     2081    while (n.lastChild) n.toggled.push(n.removeChild(n.lastChild));
     2082  }
     2083};
     2084
     2085/**
     2086 * Given a flat array of values, returns a simple DOM with each value wrapped by
     2087 * a node that is a child of the root node.
     2088 *
     2089 * @param {array} values.
     2090 * @returns {array} nodes.
     2091 */
     2092pv.nodes = function(values) {
     2093  var root = new pv.Dom.Node();
     2094  for (var i = 0; i < values.length; i++) {
     2095    root.appendChild(new pv.Dom.Node(values[i]));
     2096  }
     2097  return root.nodes();
     2098};
     2099/**
     2100 * Returns a {@link pv.Tree} operator for the specified array. This is a
     2101 * convenience factory method, equivalent to <tt>new pv.Tree(array)</tt>.
     2102 *
     2103 * @see pv.Tree
     2104 * @param {array} array an array from which to construct a tree.
     2105 * @returns {pv.Tree} a tree operator for the specified array.
     2106 */
     2107pv.tree = function(array) {
     2108  return new pv.Tree(array);
     2109};
     2110
     2111/**
     2112 * Constructs a tree operator for the specified array. This constructor should
     2113 * not be invoked directly; use {@link pv.tree} instead.
     2114 *
     2115 * @class Represents a tree operator for the specified array. The tree operator
     2116 * allows a hierarchical map to be constructed from an array; it is similar to
     2117 * the {@link pv.Nest} operator, except the hierarchy is derived dynamically
     2118 * from the array elements.
     2119 *
     2120 * <p>For example, given an array of size information for ActionScript classes:
     2121 *
     2122 * <pre>{ name: "flare.flex.FlareVis", size: 4116 },
     2123 * { name: "flare.physics.DragForce", size: 1082 },
     2124 * { name: "flare.physics.GravityForce", size: 1336 }, ...</pre>
     2125 *
     2126 * To facilitate visualization, it may be useful to nest the elements by their
     2127 * package hierarchy:
     2128 *
     2129 * <pre>var tree = pv.tree(classes)
     2130 *     .keys(function(d) d.name.split("."))
     2131 *     .map();</pre>
     2132 *
     2133 * The resulting tree is:
     2134 *
     2135 * <pre>{ flare: {
     2136 *     flex: {
     2137 *       FlareVis: {
     2138 *         name: "flare.flex.FlareVis",
     2139 *         size: 4116 } },
     2140 *     physics: {
     2141 *       DragForce: {
     2142 *         name: "flare.physics.DragForce",
     2143 *         size: 1082 },
     2144 *       GravityForce: {
     2145 *         name: "flare.physics.GravityForce",
     2146 *         size: 1336 } },
     2147 *     ... } }</pre>
     2148 *
     2149 * By specifying a value function,
     2150 *
     2151 * <pre>var tree = pv.tree(classes)
     2152 *     .keys(function(d) d.name.split("."))
     2153 *     .value(function(d) d.size)
     2154 *     .map();</pre>
     2155 *
     2156 * we can further eliminate redundant data:
     2157 *
     2158 * <pre>{ flare: {
     2159 *     flex: {
     2160 *       FlareVis: 4116 },
     2161 *     physics: {
     2162 *       DragForce: 1082,
     2163 *       GravityForce: 1336 },
     2164 *   ... } }</pre>
     2165 *
     2166 * For visualizations with large data sets, performance improvements may be seen
     2167 * by storing the data in a tree format, and then flattening it into an array at
     2168 * runtime with {@link pv.Flatten}.
     2169 *
     2170 * @param {array} array an array from which to construct a tree.
     2171 */
     2172pv.Tree = function(array) {
     2173  this.array = array;
     2174};
     2175
     2176/**
     2177 * Assigns a <i>keys</i> function to this operator; required. The keys function
     2178 * returns an array of <tt>string</tt>s for each element in the associated
     2179 * array; these keys determine how the elements are nested in the tree. The
     2180 * returned keys should be unique for each element in the array; otherwise, the
     2181 * behavior of this operator is undefined.
     2182 *
     2183 * @param {function} k the keys function.
     2184 * @returns {pv.Tree} this.
     2185 */
     2186pv.Tree.prototype.keys = function(k) {
     2187  this.k = k;
     2188  return this;
     2189};
     2190
     2191/**
     2192 * Assigns a <i>value</i> function to this operator; optional. The value
     2193 * function specifies an optional transformation of the element in the array
     2194 * before it is inserted into the map. If no value function is specified, it is
     2195 * equivalent to using the identity function.
     2196 *
     2197 * @param {function} k the value function.
     2198 * @returns {pv.Tree} this.
     2199 */
     2200pv.Tree.prototype.value = function(v) {
     2201  this.v = v;
     2202  return this;
     2203};
     2204
     2205/**
     2206 * Returns a hierarchical map of values. The hierarchy is determined by the keys
     2207 * function; the values in the map are determined by the value function.
     2208 *
     2209 * @returns a hierarchical map of values.
     2210 */
     2211pv.Tree.prototype.map = function() {
     2212  var map = {}, o = {};
     2213  for (var i = 0; i < this.array.length; i++) {
     2214    o.index = i;
     2215    var value = this.array[i], keys = this.k.call(o, value), node = map;
     2216    for (var j = 0; j < keys.length - 1; j++) {
     2217      node = node[keys[j]] || (node[keys[j]] = {});
     2218    }
     2219    node[keys[j]] = this.v ? this.v.call(o, value) : value;
     2220  }
     2221  return map;
     2222};
     2223/**
     2224 * Returns a {@link pv.Nest} operator for the specified array. This is a
     2225 * convenience factory method, equivalent to <tt>new pv.Nest(array)</tt>.
     2226 *
     2227 * @see pv.Nest
     2228 * @param {array} array an array of elements to nest.
     2229 * @returns {pv.Nest} a nest operator for the specified array.
     2230 */
     2231pv.nest = function(array) {
     2232  return new pv.Nest(array);
     2233};
     2234
     2235/**
     2236 * Constructs a nest operator for the specified array. This constructor should
     2237 * not be invoked directly; use {@link pv.nest} instead.
     2238 *
     2239 * @class Represents a {@link Nest} operator for the specified array. Nesting
     2240 * allows elements in an array to be grouped into a hierarchical tree
     2241 * structure. The levels in the tree are specified by <i>key</i> functions. The
     2242 * leaf nodes of the tree can be sorted by value, while the internal nodes can
     2243 * be sorted by key. Finally, the tree can be returned either has a
     2244 * multidimensional array via {@link #entries}, or as a hierarchical map via
     2245 * {@link #map}. The {@link #rollup} routine similarly returns a map, collapsing
     2246 * the elements in each leaf node using a summary function.
     2247 *
     2248 * <p>For example, consider the following tabular data structure of Barley
     2249 * yields, from various sites in Minnesota during 1931-2:
     2250 *
     2251 * <pre>{ yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm" },
     2252 * { yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca" },
     2253 * { yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris" }, ...</pre>
     2254 *
     2255 * To facilitate visualization, it may be useful to nest the elements first by
     2256 * year, and then by variety, as follows:
     2257 *
     2258 * <pre>var nest = pv.nest(yields)
     2259 *     .key(function(d) d.year)
     2260 *     .key(function(d) d.variety)
     2261 *     .entries();</pre>
     2262 *
     2263 * This returns a nested array. Each element of the outer array is a key-values
     2264 * pair, listing the values for each distinct key:
     2265 *
     2266 * <pre>{ key: 1931, values: [
     2267 *   { key: "Manchuria", values: [
     2268 *       { yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm" },
     2269 *       { yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca" },
     2270 *       { yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris" },
     2271 *       ...
     2272 *     ] },
     2273 *   { key: "Glabron", values: [
     2274 *       { yield: 43.07, variety: "Glabron", year: 1931, site: "University Farm" },
     2275 *       { yield: 55.20, variety: "Glabron", year: 1931, site: "Waseca" },
     2276 *       ...
     2277 *     ] },
     2278 *   ] },
     2279 * { key: 1932, values: ... }</pre>
     2280 *
     2281 * Further details, including sorting and rollup, is provided below on the
     2282 * corresponding methods.
     2283 *
     2284 * @param {array} array an array of elements to nest.
     2285 */
     2286pv.Nest = function(array) {
     2287  this.array = array;
     2288  this.keys = [];
     2289};
     2290
     2291/**
     2292 * Nests using the specified key function. Multiple keys may be added to the
     2293 * nest; the array elements will be nested in the order keys are specified.
     2294 *
     2295 * @param {function} key a key function; must return a string or suitable map
     2296 * key.
     2297 * @returns {pv.Nest} this.
     2298 */
     2299pv.Nest.prototype.key = function(key) {
     2300  this.keys.push(key);
     2301  return this;
     2302};
     2303
     2304/**
     2305 * Sorts the previously-added keys. The natural sort order is used by default
     2306 * (see {@link pv.naturalOrder}); if an alternative order is desired,
     2307 * <tt>order</tt> should be a comparator function. If this method is not called
     2308 * (i.e., keys are <i>unsorted</i>), keys will appear in the order they appear
     2309 * in the underlying elements array. For example,
     2310 *
     2311 * <pre>pv.nest(yields)
     2312 *     .key(function(d) d.year)
     2313 *     .key(function(d) d.variety)
     2314 *     .sortKeys()
     2315 *     .entries()</pre>
     2316 *
     2317 * groups yield data by year, then variety, and sorts the variety groups
     2318 * lexicographically (since the variety attribute is a string).
     2319 *
     2320 * <p>Key sort order is only used in conjunction with {@link #entries}, which
     2321 * returns an array of key-values pairs. If the nest is used to construct a
     2322 * {@link #map} instead, keys are unsorted.
     2323 *
     2324 * @param {function} [order] an optional comparator function.
     2325 * @returns {pv.Nest} this.
     2326 */
     2327pv.Nest.prototype.sortKeys = function(order) {
     2328  this.keys[this.keys.length - 1].order = order || pv.naturalOrder;
     2329  return this;
     2330};
     2331
     2332/**
     2333 * Sorts the leaf values. The natural sort order is used by default (see
     2334 * {@link pv.naturalOrder}); if an alternative order is desired, <tt>order</tt>
     2335 * should be a comparator function. If this method is not called (i.e., values
     2336 * are <i>unsorted</i>), values will appear in the order they appear in the
     2337 * underlying elements array. For example,
     2338 *
     2339 * <pre>pv.nest(yields)
     2340 *     .key(function(d) d.year)
     2341 *     .key(function(d) d.variety)
     2342 *     .sortValues(function(a, b) a.yield - b.yield)
     2343 *     .entries()</pre>
     2344 *
     2345 * groups yield data by year, then variety, and sorts the values for each
     2346 * variety group by yield.
     2347 *
     2348 * <p>Value sort order, unlike keys, applies to both {@link #entries} and
     2349 * {@link #map}. It has no effect on {@link #rollup}.
     2350 *
     2351 * @param {function} [order] an optional comparator function.
     2352 * @returns {pv.Nest} this.
     2353 */
     2354pv.Nest.prototype.sortValues = function(order) {
     2355  this.order = order || pv.naturalOrder;
     2356  return this;
     2357};
     2358
     2359/**
     2360 * Returns a hierarchical map of values. Each key adds one level to the
     2361 * hierarchy. With only a single key, the returned map will have a key for each
     2362 * distinct value of the key function; the correspond value with be an array of
     2363 * elements with that key value. If a second key is added, this will be a nested
     2364 * map. For example:
     2365 *
     2366 * <pre>pv.nest(yields)
     2367 *     .key(function(d) d.variety)
     2368 *     .key(function(d) d.site)
     2369 *     .map()</pre>
     2370 *
     2371 * returns a map <tt>m</tt> such that <tt>m[variety][site]</tt> is an array, a subset of
     2372 * <tt>yields</tt>, with each element having the given variety and site.
     2373 *
     2374 * @returns a hierarchical map of values.
     2375 */
     2376pv.Nest.prototype.map = function() {
     2377  var map = {}, values = [];
     2378
     2379  /* Build the map. */
     2380  for (var i, j = 0; j < this.array.length; j++) {
     2381    var x = this.array[j];
     2382    var m = map;
     2383    for (i = 0; i < this.keys.length - 1; i++) {
     2384      var k = this.keys[i](x);
     2385      if (!m[k]) m[k] = {};
     2386      m = m[k];
     2387    }
     2388    k = this.keys[i](x);
     2389    if (!m[k]) {
     2390      var a = [];
     2391      values.push(a);
     2392      m[k] = a;
     2393    }
     2394    m[k].push(x);
     2395  }
     2396
     2397  /* Sort each leaf array. */
     2398  if (this.order) {
     2399    for (var i = 0; i < values.length; i++) {
     2400      values[i].sort(this.order);
     2401    }
     2402  }
     2403
     2404  return map;
     2405};
     2406
     2407/**
     2408 * Returns a hierarchical nested array. This method is similar to
     2409 * {@link pv.entries}, but works recursively on the entire hierarchy. Rather
     2410 * than returning a map like {@link #map}, this method returns a nested
     2411 * array. Each element of the array has a <tt>key</tt> and <tt>values</tt>
     2412 * field. For leaf nodes, the <tt>values</tt> array will be a subset of the
     2413 * underlying elements array; for non-leaf nodes, the <tt>values</tt> array will
     2414 * contain more key-values pairs.
     2415 *
     2416 * <p>For an example usage, see the {@link Nest} constructor.
     2417 *
     2418 * @returns a hierarchical nested array.
     2419 */
     2420pv.Nest.prototype.entries = function() {
     2421
     2422  /** Recursively extracts the entries for the given map. */
     2423  function entries(map) {
     2424    var array = [];
     2425    for (var k in map) {
     2426      var v = map[k];
     2427      array.push({ key: k, values: (v instanceof Array) ? v : entries(v) });
     2428    };
     2429    return array;
     2430  }
     2431
     2432  /** Recursively sorts the values for the given key-values array. */
     2433  function sort(array, i) {
     2434    var o = this.keys[i].order;
     2435    if (o) array.sort(function(a, b) { return o(a.key, b.key); });
     2436    if (++i < this.keys.length) {
     2437      for (var j = 0; j < array.length; j++) {
     2438        sort.call(this, array[j].values, i);
     2439      }
     2440    }
     2441    return array;
     2442  }
     2443
     2444  return sort.call(this, entries(this.map()), 0);
     2445};
     2446
     2447/**
     2448 * Returns a rollup map. The behavior of this method is the same as
     2449 * {@link #map}, except that the leaf values are replaced with the return value
     2450 * of the specified rollup function <tt>f</tt>. For example,
     2451 *
     2452 * <pre>pv.nest(yields)
     2453 *      .key(function(d) d.site)
     2454 *      .rollup(function(v) pv.median(v, function(d) d.yield))</pre>
     2455 *
     2456 * first groups yield data by site, and then returns a map from site to median
     2457 * yield for the given site.
     2458 *
     2459 * @see #map
     2460 * @param {function} f a rollup function.
     2461 * @returns a hierarchical map, with the leaf values computed by <tt>f</tt>.
     2462 */
     2463pv.Nest.prototype.rollup = function(f) {
     2464
     2465  /** Recursively descends to the leaf nodes (arrays) and does rollup. */
     2466  function rollup(map) {
     2467    for (var key in map) {
     2468      var value = map[key];
     2469      if (value instanceof Array) {
     2470        map[key] = f(value);
     2471      } else {
     2472        rollup(value);
     2473      }
     2474    }
     2475    return map;
     2476  }
     2477
     2478  return rollup(this.map());
     2479};
     2480/**
     2481 * Returns a {@link pv.Flatten} operator for the specified map. This is a
     2482 * convenience factory method, equivalent to <tt>new pv.Flatten(map)</tt>.
     2483 *
     2484 * @see pv.Flatten
     2485 * @param map a map to flatten.
     2486 * @returns {pv.Flatten} a flatten operator for the specified map.
     2487 */
     2488pv.flatten = function(map) {
     2489  return new pv.Flatten(map);
     2490};
     2491
     2492/**
     2493 * Constructs a flatten operator for the specified map. This constructor should
     2494 * not be invoked directly; use {@link pv.flatten} instead.
     2495 *
     2496 * @class Represents a flatten operator for the specified array. Flattening
     2497 * allows hierarchical maps to be flattened into an array. The levels in the
     2498 * input tree are specified by <i>key</i> functions.
     2499 *
     2500 * <p>For example, consider the following hierarchical data structure of Barley
     2501 * yields, from various sites in Minnesota during 1931-2:
     2502 *
     2503 * <pre>{ 1931: {
     2504 *     Manchuria: {
     2505 *       "University Farm": 27.00,
     2506 *       "Waseca": 48.87,
     2507 *       "Morris": 27.43,
     2508 *       ... },
     2509 *     Glabron: {
     2510 *       "University Farm": 43.07,
     2511 *       "Waseca": 55.20,
     2512 *       ... } },
     2513 *   1932: {
     2514 *     ... } }</pre>
     2515 *
     2516 * To facilitate visualization, it may be useful to flatten the tree into a
     2517 * tabular array:
     2518 *
     2519 * <pre>var array = pv.flatten(yields)
     2520 *     .key("year")
     2521 *     .key("variety")
     2522 *     .key("site")
     2523 *     .key("yield")
     2524 *     .array();</pre>
     2525 *
     2526 * This returns an array of object elements. Each element in the array has
     2527 * attributes corresponding to this flatten operator's keys:
     2528 *
     2529 * <pre>{ site: "University Farm", variety: "Manchuria", year: 1931, yield: 27 },
     2530 * { site: "Waseca", variety: "Manchuria", year: 1931, yield: 48.87 },
     2531 * { site: "Morris", variety: "Manchuria", year: 1931, yield: 27.43 },
     2532 * { site: "University Farm", variety: "Glabron", year: 1931, yield: 43.07 },
     2533 * { site: "Waseca", variety: "Glabron", year: 1931, yield: 55.2 }, ...</pre>
     2534 *
     2535 * <p>The flatten operator is roughly the inverse of the {@link pv.Nest} and
     2536 * {@link pv.Tree} operators.
     2537 *
     2538 * @param map a map to flatten.
     2539 */
     2540pv.Flatten = function(map) {
     2541  this.map = map;
     2542  this.keys = [];
     2543};
     2544
     2545/**
     2546 * Flattens using the specified key function. Multiple keys may be added to the
     2547 * flatten; the tiers of the underlying tree must correspond to the specified
     2548 * keys, in order. The order of the returned array is undefined; however, you
     2549 * can easily sort it.
     2550 *
     2551 * @param {string} key the key name.
     2552 * @param {function} [f] an optional value map function.
     2553 * @returns {pv.Nest} this.
     2554 */
     2555pv.Flatten.prototype.key = function(key, f) {
     2556  this.keys.push({name: key, value: f});
     2557  delete this.$leaf;
     2558  return this;
     2559};
     2560
     2561/**
     2562 * Flattens using the specified leaf function. This is an alternative to
     2563 * specifying an explicit set of keys; the tiers of the underlying tree will be
     2564 * determined dynamically by recursing on the values, and the resulting keys
     2565 * will be stored in the entries <tt>keys</tt> attribute. The leaf function must
     2566 * return true for leaves, and false for internal nodes.
     2567 *
     2568 * @param {function} f a leaf function.
     2569 * @returns {pv.Nest} this.
     2570 */
     2571pv.Flatten.prototype.leaf = function(f) {
     2572  this.keys.length = 0;
     2573  this.$leaf = f;
     2574  return this;
     2575};
     2576
     2577/**
     2578 * Returns the flattened array. Each entry in the array is an object; each
     2579 * object has attributes corresponding to this flatten operator's keys.
     2580 *
     2581 * @returns an array of elements from the flattened map.
     2582 */
     2583pv.Flatten.prototype.array = function() {
     2584  var entries = [], stack = [], keys = this.keys, leaf = this.$leaf;
     2585
     2586  /* Recursively visit using the leaf function. */
     2587  if (leaf) {
     2588    function recurse(value, i) {
     2589      if (leaf(value)) {
     2590        entries.push({keys: stack.slice(), value: value});
     2591      } else {
     2592        for (var key in value) {
     2593          stack.push(key);
     2594          recurse(value[key], i + 1);
     2595          stack.pop();
     2596        }
     2597      }
     2598    }
     2599    recurse(this.map, 0);
     2600    return entries;
     2601  }
     2602
     2603  /* Recursively visits the specified value. */
     2604  function visit(value, i) {
     2605    if (i < keys.length - 1) {
     2606      for (var key in value) {
     2607        stack.push(key);
     2608        visit(value[key], i + 1);
     2609        stack.pop();
     2610      }
     2611    } else {
     2612      entries.push(stack.concat(value));
     2613    }
     2614  }
     2615
     2616  visit(this.map, 0);
     2617  return entries.map(function(stack) {
     2618      var m = {};
     2619      for (var i = 0; i < keys.length; i++) {
     2620        var k = keys[i], v = stack[i];
     2621        m[k.name] = k.value ? k.value.call(null, v) : v;
     2622      }
     2623      return m;
     2624    });
     2625};
     2626/**
     2627 * Returns a {@link pv.Vector} for the specified <i>x</i> and <i>y</i>
     2628 * coordinate. This is a convenience factory method, equivalent to <tt>new
     2629 * pv.Vector(x, y)</tt>.
     2630 *
     2631 * @see pv.Vector
     2632 * @param {number} x the <i>x</i> coordinate.
     2633 * @param {number} y the <i>y</i> coordinate.
     2634 * @returns {pv.Vector} a vector for the specified coordinates.
     2635 */
     2636pv.vector = function(x, y) {
     2637  return new pv.Vector(x, y);
     2638};
     2639
     2640/**
     2641 * Constructs a {@link pv.Vector} for the specified <i>x</i> and <i>y</i>
     2642 * coordinate. This constructor should not be invoked directly; use
     2643 * {@link pv.vector} instead.
     2644 *
     2645 * @class Represents a two-dimensional vector; a 2-tuple <i>&#x27e8;x,
     2646 * y&#x27e9;</i>. The intent of this class is to simplify vector math. Note that
     2647 * in performance-sensitive cases it may be more efficient to represent 2D
     2648 * vectors as simple objects with <tt>x</tt> and <tt>y</tt> attributes, rather
     2649 * than using instances of this class.
     2650 *
     2651 * @param {number} x the <i>x</i> coordinate.
     2652 * @param {number} y the <i>y</i> coordinate.
     2653 */
     2654pv.Vector = function(x, y) {
     2655  this.x = x;
     2656  this.y = y;
     2657};
     2658
     2659/**
     2660 * Returns a vector perpendicular to this vector: <i>&#x27e8;-y, x&#x27e9;</i>.
     2661 *
     2662 * @returns {pv.Vector} a perpendicular vector.
     2663 */
     2664pv.Vector.prototype.perp = function() {
     2665  return new pv.Vector(-this.y, this.x);
     2666};
     2667
     2668/**
     2669 * Returns a normalized copy of this vector: a vector with the same direction,
     2670 * but unit length. If this vector has zero length this method returns a copy of
     2671 * this vector.
     2672 *
     2673 * @returns {pv.Vector} a unit vector.
     2674 */
     2675pv.Vector.prototype.norm = function() {
     2676  var l = this.length();
     2677  return this.times(l ? (1 / l) : 1);
     2678};
     2679
     2680/**
     2681 * Returns the magnitude of this vector, defined as <i>sqrt(x * x + y * y)</i>.
     2682 *
     2683 * @returns {number} a length.
     2684 */
     2685pv.Vector.prototype.length = function() {
     2686  return Math.sqrt(this.x * this.x + this.y * this.y);
     2687};
     2688
     2689/**
     2690 * Returns a scaled copy of this vector: <i>&#x27e8;x * k, y * k&#x27e9;</i>.
     2691 * To perform the equivalent divide operation, use <i>1 / k</i>.
     2692 *
     2693 * @param {number} k the scale factor.
     2694 * @returns {pv.Vector} a scaled vector.
     2695 */
     2696pv.Vector.prototype.times = function(k) {
     2697  return new pv.Vector(this.x * k, this.y * k);
     2698};
     2699
     2700/**
     2701 * Returns this vector plus the vector <i>v</i>: <i>&#x27e8;x + v.x, y +
     2702 * v.y&#x27e9;</i>. If only one argument is specified, it is interpreted as the
     2703 * vector <i>v</i>.
     2704 *
     2705 * @param {number} x the <i>x</i> coordinate to add.
     2706 * @param {number} y the <i>y</i> coordinate to add.
     2707 * @returns {pv.Vector} a new vector.
     2708 */
     2709pv.Vector.prototype.plus = function(x, y) {
     2710  return (arguments.length == 1)
     2711      ? new pv.Vector(this.x + x.x, this.y + x.y)
     2712      : new pv.Vector(this.x + x, this.y + y);
     2713};
     2714
     2715/**
     2716 * Returns this vector minus the vector <i>v</i>: <i>&#x27e8;x - v.x, y -
     2717 * v.y&#x27e9;</i>. If only one argument is specified, it is interpreted as the
     2718 * vector <i>v</i>.
     2719 *
     2720 * @param {number} x the <i>x</i> coordinate to subtract.
     2721 * @param {number} y the <i>y</i> coordinate to subtract.
     2722 * @returns {pv.Vector} a new vector.
     2723 */
     2724pv.Vector.prototype.minus = function(x, y) {
     2725  return (arguments.length == 1)
     2726      ? new pv.Vector(this.x - x.x, this.y - x.y)
     2727      : new pv.Vector(this.x - x, this.y - y);
     2728};
     2729
     2730/**
     2731 * Returns the dot product of this vector and the vector <i>v</i>: <i>x * v.x +
     2732 * y * v.y</i>. If only one argument is specified, it is interpreted as the
     2733 * vector <i>v</i>.
     2734 *
     2735 * @param {number} x the <i>x</i> coordinate to dot.
     2736 * @param {number} y the <i>y</i> coordinate to dot.
     2737 * @returns {number} a dot product.
     2738 */
     2739pv.Vector.prototype.dot = function(x, y) {
     2740  return (arguments.length == 1)
     2741      ? this.x * x.x + this.y * x.y
     2742      : this.x * x + this.y * y;
     2743};
     2744/**
     2745 * Returns a new identity transform.
     2746 *
     2747 * @class Represents a transformation matrix. The transformation matrix is
     2748 * limited to expressing translate and uniform scale transforms only; shearing,
     2749 * rotation, general affine, and other transforms are not supported.
     2750 *
     2751 * <p>The methods on this class treat the transform as immutable, returning a
     2752 * copy of the transformation matrix with the specified transform applied. Note,
     2753 * alternatively, that the matrix fields can be get and set directly.
     2754 */
     2755pv.Transform = function() {};
     2756pv.Transform.prototype = {k: 1, x: 0, y: 0};
     2757
     2758/**
     2759 * The scale magnitude; defaults to 1.
     2760 *
     2761 * @type number
     2762 * @name pv.Transform.prototype.k
     2763 */
     2764
     2765/**
     2766 * The x-offset; defaults to 0.
     2767 *
     2768 * @type number
     2769 * @name pv.Transform.prototype.x
     2770 */
     2771
     2772/**
     2773 * The y-offset; defaults to 0.
     2774 *
     2775 * @type number
     2776 * @name pv.Transform.prototype.y
     2777 */
     2778
     2779/**
     2780 * @private The identity transform.
     2781 *
     2782 * @type pv.Transform
     2783 */
     2784pv.Transform.identity = new pv.Transform();
     2785
     2786// k 0 x   1 0 a   k 0 ka+x
     2787// 0 k y * 0 1 b = 0 k kb+y
     2788// 0 0 1   0 0 1   0 0 1
     2789
     2790/**
     2791 * Returns a translated copy of this transformation matrix.
     2792 *
     2793 * @param {number} x the x-offset.
     2794 * @param {number} y the y-offset.
     2795 * @returns {pv.Transform} the translated transformation matrix.
     2796 */
     2797pv.Transform.prototype.translate = function(x, y) {
     2798  var v = new pv.Transform();
     2799  v.k = this.k;
     2800  v.x = this.k * x + this.x;
     2801  v.y = this.k * y + this.y;
     2802  return v;
     2803};
     2804
     2805// k 0 x   d 0 0   kd  0 x
     2806// 0 k y * 0 d 0 =  0 kd y
     2807// 0 0 1   0 0 1    0  0 1
     2808
     2809/**
     2810 * Returns a scaled copy of this transformation matrix.
     2811 *
     2812 * @param {number} k
     2813 * @returns {pv.Transform} the scaled transformation matrix.
     2814 */
     2815pv.Transform.prototype.scale = function(k) {
     2816  var v = new pv.Transform();
     2817  v.k = this.k * k;
     2818  v.x = this.x;
     2819  v.y = this.y;
     2820  return v;
     2821};
     2822
     2823/**
     2824 * Returns the inverse of this transformation matrix.
     2825 *
     2826 * @returns {pv.Transform} the inverted transformation matrix.
     2827 */
     2828pv.Transform.prototype.invert = function() {
     2829  var v = new pv.Transform(), k = 1 / this.k;
     2830  v.k = k;
     2831  v.x = -this.x * k;
     2832  v.y = -this.y * k;
     2833  return v;
     2834};
     2835
     2836// k 0 x   d 0 a   kd  0 ka+x
     2837// 0 k y * 0 d b =  0 kd kb+y
     2838// 0 0 1   0 0 1    0  0    1
     2839
     2840/**
     2841 * Returns this matrix post-multiplied by the specified matrix <i>m</i>.
     2842 *
     2843 * @param {pv.Transform} m
     2844 * @returns {pv.Transform} the post-multiplied transformation matrix.
     2845 */
     2846pv.Transform.prototype.times = function(m) {
     2847  var v = new pv.Transform();
     2848  v.k = this.k * m.k;
     2849  v.x = this.k * m.x + this.x;
     2850  v.y = this.k * m.y + this.y;
     2851  return v;
     2852};
     2853/**
     2854 * Abstract; see the various scale implementations.
     2855 *
     2856 * @class Represents a scale; a function that performs a transformation from
     2857 * data domain to visual range. For quantitative and quantile scales, the domain
     2858 * is expressed as numbers; for ordinal scales, the domain is expressed as
     2859 * strings (or equivalently objects with unique string representations). The
     2860 * "visual range" may correspond to pixel space, colors, font sizes, and the
     2861 * like.
     2862 *
     2863 * <p>Note that scales are functions, and thus can be used as properties
     2864 * directly, assuming that the data associated with a mark is a number. While
     2865 * this is convenient for single-use scales, frequently it is desirable to
     2866 * define scales globally:
     2867 *
     2868 * <pre>var y = pv.Scale.linear(0, 100).range(0, 640);</pre>
     2869 *
     2870 * The <tt>y</tt> scale can now be equivalently referenced within a property:
     2871 *
     2872 * <pre>    .height(function(d) y(d))</pre>
     2873 *
     2874 * Alternatively, if the data are not simple numbers, the appropriate value can
     2875 * be passed to the <tt>y</tt> scale (e.g., <tt>d.foo</tt>). The {@link #by}
     2876 * method similarly allows the data to be mapped to a numeric value before
     2877 * performing the linear transformation.
     2878 *
     2879 * @see pv.Scale.quantitative
     2880 * @see pv.Scale.quantile
     2881 * @see pv.Scale.ordinal
     2882 * @extends function
     2883 */
     2884pv.Scale = function() {};
     2885
     2886/**
     2887 * @private Returns a function that interpolators from the start value to the
     2888 * end value, given a parameter <i>t</i> in [0, 1].
     2889 *
     2890 * @param start the start value.
     2891 * @param end the end value.
     2892 */
     2893pv.Scale.interpolator = function(start, end) {
     2894  if (typeof start == "number") {
     2895    return function(t) {
     2896      return t * (end - start) + start;
     2897    };
     2898  }
     2899
     2900  /* For now, assume color. */
     2901  start = pv.color(start).rgb();
     2902  end = pv.color(end).rgb();
     2903  return function(t) {
     2904    var a = start.a * (1 - t) + end.a * t;
     2905    if (a < 1e-5) a = 0; // avoid scientific notation
     2906    return (start.a == 0) ? pv.rgb(end.r, end.g, end.b, a)
     2907        : ((end.a == 0) ? pv.rgb(start.r, start.g, start.b, a)
     2908        : pv.rgb(
     2909            Math.round(start.r * (1 - t) + end.r * t),
     2910            Math.round(start.g * (1 - t) + end.g * t),
     2911            Math.round(start.b * (1 - t) + end.b * t), a));
     2912  };
     2913};
     2914
     2915/**
     2916 * Returns a view of this scale by the specified accessor function <tt>f</tt>.
     2917 * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
     2918 * <tt>function(d) y(d.foo)</tt>.
     2919 *
     2920 * <p>This method is provided for convenience, such that scales can be
     2921 * succinctly defined inline. For example, given an array of data elements that
     2922 * have a <tt>score</tt> attribute with the domain [0, 1], the height property
     2923 * could be specified as:
     2924 *
     2925 * <pre>    .height(pv.Scale.linear().range(0, 480).by(function(d) d.score))</pre>
     2926 *
     2927 * This is equivalent to:
     2928 *
     2929 * <pre>    .height(function(d) d.score * 480)</pre>
     2930 *
     2931 * This method should be used judiciously; it is typically more clear to invoke
     2932 * the scale directly, passing in the value to be scaled.
     2933 *
     2934 * @function
     2935 * @name pv.Scale.prototype.by
     2936 * @param {function} f an accessor function.
     2937 * @returns {pv.Scale} a view of this scale by the specified accessor function.
     2938 */
     2939/**
     2940 * Returns a default quantitative, linear, scale for the specified domain. The
     2941 * arguments to this constructor are optional, and equivalent to calling
     2942 * {@link #domain}. The default domain and range are [0,1].
     2943 *
     2944 * <p>This constructor is typically not used directly; see one of the
     2945 * quantitative scale implementations instead.
     2946 *
     2947 * @class Represents an abstract quantitative scale; a function that performs a
     2948 * numeric transformation. This class is typically not used directly; see one of
     2949 * the quantitative scale implementations (linear, log, root, etc.)
     2950 * instead. <style type="text/css">sub{line-height:0}</style> A quantitative
     2951 * scale represents a 1-dimensional transformation from a numeric domain of
     2952 * input data [<i>d<sub>0</sub></i>, <i>d<sub>1</sub></i>] to a numeric range of
     2953 * pixels [<i>r<sub>0</sub></i>, <i>r<sub>1</sub></i>]. In addition to
     2954 * readability, scales offer several useful features:
     2955 *
     2956 * <p>1. The range can be expressed in colors, rather than pixels. For example:
     2957 *
     2958 * <pre>    .fillStyle(pv.Scale.linear(0, 100).range("red", "green"))</pre>
     2959 *
     2960 * will fill the marks "red" on an input value of 0, "green" on an input value
     2961 * of 100, and some color in-between for intermediate values.
     2962 *
     2963 * <p>2. The domain and range can be subdivided for a non-uniform
     2964 * transformation. For example, you may want a diverging color scale that is
     2965 * increasingly red for negative values, and increasingly green for positive
     2966 * values:
     2967 *
     2968 * <pre>    .fillStyle(pv.Scale.linear(-1, 0, 1).range("red", "white", "green"))</pre>
     2969 *
     2970 * The domain can be specified as a series of <i>n</i> monotonically-increasing
     2971 * values; the range must also be specified as <i>n</i> values, resulting in
     2972 * <i>n - 1</i> contiguous linear scales.
     2973 *
     2974 * <p>3. Quantitative scales can be inverted for interaction. The
     2975 * {@link #invert} method takes a value in the output range, and returns the
     2976 * corresponding value in the input domain. This is frequently used to convert
     2977 * the mouse location (see {@link pv.Mark#mouse}) to a value in the input
     2978 * domain. Note that inversion is only supported for numeric ranges, and not
     2979 * colors.
     2980 *
     2981 * <p>4. A scale can be queried for reasonable "tick" values. The {@link #ticks}
     2982 * method provides a convenient way to get a series of evenly-spaced rounded
     2983 * values in the input domain. Frequently these are used in conjunction with
     2984 * {@link pv.Rule} to display tick marks or grid lines.
     2985 *
     2986 * <p>5. A scale can be "niced" to extend the domain to suitable rounded
     2987 * numbers. If the minimum and maximum of the domain are messy because they are
     2988 * derived from data, you can use {@link #nice} to round these values down and
     2989 * up to even numbers.
     2990 *
     2991 * @param {number...} domain... optional domain values.
     2992 * @see pv.Scale.linear
     2993 * @see pv.Scale.log
     2994 * @see pv.Scale.root
     2995 * @extends pv.Scale
     2996 */
     2997pv.Scale.quantitative = function() {
     2998  var d = [0, 1], // default domain
     2999      l = [0, 1], // default transformed domain
     3000      r = [0, 1], // default range
     3001      i = [pv.identity], // default interpolators
     3002      type = Number, // default type
     3003      n = false, // whether the domain is negative
     3004      f = pv.identity, // default forward transform
     3005      g = pv.identity, // default inverse transform
     3006      tickFormat = String; // default tick formatting function
     3007
     3008  /** @private */
     3009  function newDate(x) {
     3010    return new Date(x);
     3011  }
     3012
     3013  /** @private */
     3014  function scale(x) {
     3015    var j = pv.search(d, x);
     3016    if (j < 0) j = -j - 2;
     3017    j = Math.max(0, Math.min(i.length - 1, j));
     3018    return i[j]((f(x) - l[j]) / (l[j + 1] - l[j]));
     3019  }
     3020
     3021  /** @private */
     3022  scale.transform = function(forward, inverse) {
     3023    /** @ignore */ f = function(x) { return n ? -forward(-x) : forward(x); };
     3024    /** @ignore */ g = function(y) { return n ? -inverse(-y) : inverse(y); };
     3025    l = d.map(f);
     3026    return this;
     3027  };
     3028
     3029  /**
     3030   * Sets or gets the input domain. This method can be invoked several ways:
     3031   *
     3032   * <p>1. <tt>domain(min, ..., max)</tt>
     3033   *
     3034   * <p>Specifying the domain as a series of numbers is the most explicit and
     3035   * recommended approach. Most commonly, two numbers are specified: the minimum
     3036   * and maximum value. However, for a diverging scale, or other subdivided
     3037   * non-uniform scales, multiple values can be specified. Values can be derived
     3038   * from data using {@link pv.min} and {@link pv.max}. For example:
     3039   *
     3040   * <pre>    .domain(0, pv.max(array))</pre>
     3041   *
     3042   * An alternative method for deriving minimum and maximum values from data
     3043   * follows.
     3044   *
     3045   * <p>2. <tt>domain(array, minf, maxf)</tt>
     3046   *
     3047   * <p>When both the minimum and maximum value are derived from data, the
     3048   * arguments to the <tt>domain</tt> method can be specified as the array of
     3049   * data, followed by zero, one or two accessor functions. For example, if the
     3050   * array of data is just an array of numbers:
     3051   *
     3052   * <pre>    .domain(array)</pre>
     3053   *
     3054   * On the other hand, if the array elements are objects representing stock
     3055   * values per day, and the domain should consider the stock's daily low and
     3056   * daily high:
     3057   *
     3058   * <pre>    .domain(array, function(d) d.low, function(d) d.high)</pre>
     3059   *
     3060   * The first method of setting the domain is preferred because it is more
     3061   * explicit; setting the domain using this second method should be used only
     3062   * if brevity is required.
     3063   *
     3064   * <p>3. <tt>domain()</tt>
     3065   *
     3066   * <p>Invoking the <tt>domain</tt> method with no arguments returns the
     3067   * current domain as an array of numbers.
     3068   *
     3069   * @function
     3070   * @name pv.Scale.quantitative.prototype.domain
     3071   * @param {number...} domain... domain values.
     3072   * @returns {pv.Scale.quantitative} <tt>this</tt>, or the current domain.
     3073   */
     3074  scale.domain = function(array, min, max) {
     3075    if (arguments.length) {
     3076      var o; // the object we use to infer the domain type
     3077      if (array instanceof Array) {
     3078        if (arguments.length < 2) min = pv.identity;
     3079        if (arguments.length < 3) max = min;
     3080        o = array.length && min(array[0]);
     3081        d = array.length ? [pv.min(array, min), pv.max(array, max)] : [];
     3082      } else {
     3083        o = array;
     3084        d = Array.prototype.slice.call(arguments).map(Number);
     3085      }
     3086      if (!d.length) d = [-Infinity, Infinity];
     3087      else if (d.length == 1) d = [d[0], d[0]];
     3088      n = (d[0] || d[d.length - 1]) < 0;
     3089      l = d.map(f);
     3090      type = (o instanceof Date) ? newDate : Number;
     3091      return this;
     3092    }
     3093    return d.map(type);
     3094  };
     3095
     3096  /**
     3097   * Sets or gets the output range. This method can be invoked several ways:
     3098   *
     3099   * <p>1. <tt>range(min, ..., max)</tt>
     3100   *
     3101   * <p>The range may be specified as a series of numbers or colors. Most
     3102   * commonly, two numbers are specified: the minimum and maximum pixel values.
     3103   * For a color scale, values may be specified as {@link pv.Color}s or
     3104   * equivalent strings. For a diverging scale, or other subdivided non-uniform
     3105   * scales, multiple values can be specified. For example:
     3106   *
     3107   * <pre>    .range("red", "white", "green")</pre>
     3108   *
     3109   * <p>Currently, only numbers and colors are supported as range values. The
     3110   * number of range values must exactly match the number of domain values, or
     3111   * the behavior of the scale is undefined.
     3112   *
     3113   * <p>2. <tt>range()</tt>
     3114   *
     3115   * <p>Invoking the <tt>range</tt> method with no arguments returns the current
     3116   * range as an array of numbers or colors.
     3117   *
     3118   * @function
     3119   * @name pv.Scale.quantitative.prototype.range
     3120   * @param {...} range... range values.
     3121   * @returns {pv.Scale.quantitative} <tt>this</tt>, or the current range.
     3122   */
     3123  scale.range = function() {
     3124    if (arguments.length) {
     3125      r = Array.prototype.slice.call(arguments);
     3126      if (!r.length) r = [-Infinity, Infinity];
     3127      else if (r.length == 1) r = [r[0], r[0]];
     3128      i = [];
     3129      for (var j = 0; j < r.length - 1; j++) {
     3130        i.push(pv.Scale.interpolator(r[j], r[j + 1]));
     3131      }
     3132      return this;
     3133    }
     3134    return r;
     3135  };
     3136
     3137  /**
     3138   * Inverts the specified value in the output range, returning the
     3139   * corresponding value in the input domain. This is frequently used to convert
     3140   * the mouse location (see {@link pv.Mark#mouse}) to a value in the input
     3141   * domain. Inversion is only supported for numeric ranges, and not colors.
     3142   *
     3143   * <p>Note that this method does not do any rounding or bounds checking. If
     3144   * the input domain is discrete (e.g., an array index), the returned value
     3145   * should be rounded. If the specified <tt>y</tt> value is outside the range,
     3146   * the returned value may be equivalently outside the input domain.
     3147   *
     3148   * @function
     3149   * @name pv.Scale.quantitative.prototype.invert
     3150   * @param {number} y a value in the output range (a pixel location).
     3151   * @returns {number} a value in the input domain.
     3152   */
     3153  scale.invert = function(y) {
     3154    var j = pv.search(r, y);
     3155    if (j < 0) j = -j - 2;
     3156    j = Math.max(0, Math.min(i.length - 1, j));
     3157    return type(g(l[j] + (y - r[j]) / (r[j + 1] - r[j]) * (l[j + 1] - l[j])));
     3158  };
     3159
     3160  /**
     3161   * Returns an array of evenly-spaced, suitably-rounded values in the input
     3162   * domain. This method attempts to return between 5 and 10 tick values. These
     3163   * values are frequently used in conjunction with {@link pv.Rule} to display
     3164   * tick marks or grid lines.
     3165   *
     3166   * @function
     3167   * @name pv.Scale.quantitative.prototype.ticks
     3168   * @param {number} [m] optional number of desired ticks.
     3169   * @returns {number[]} an array input domain values to use as ticks.
     3170   */
     3171  scale.ticks = function(m) {
     3172    var start = d[0],
     3173        end = d[d.length - 1],
     3174        reverse = end < start,
     3175        min = reverse ? end : start,
     3176        max = reverse ? start : end,
     3177        span = max - min;
     3178
     3179    /* Special case: empty, invalid or infinite span. */
     3180    if (!span || !isFinite(span)) {
     3181      if (type == newDate) tickFormat = pv.Format.date("%x");
     3182      return [type(min)];
     3183    }
     3184
     3185    /* Special case: dates. */
     3186    if (type == newDate) {
     3187      /* Floor the date d given the precision p. */
     3188      function floor(d, p) {
     3189        switch (p) {
     3190          case 31536e6: d.setMonth(0);
     3191          case 2592e6: d.setDate(1);
     3192          case 6048e5: if (p == 6048e5) d.setDate(d.getDate() - d.getDay());
     3193          case 864e5: d.setHours(0);
     3194          case 36e5: d.setMinutes(0);
     3195          case 6e4: d.setSeconds(0);
     3196          case 1e3: d.setMilliseconds(0);
     3197        }
     3198      }
     3199
     3200      var precision, format, increment, step = 1;
     3201      if (span >= 3 * 31536e6) {
     3202        precision = 31536e6;
     3203        format = "%Y";
     3204        /** @ignore */ increment = function(d) { d.setFullYear(d.getFullYear() + step); };
     3205      } else if (span >= 3 * 2592e6) {
     3206        precision = 2592e6;
     3207        format = "%m/%Y";
     3208        /** @ignore */ increment = function(d) { d.setMonth(d.getMonth() + step); };
     3209      } else if (span >= 3 * 6048e5) {
     3210        precision = 6048e5;
     3211        format = "%m/%d";
     3212        /** @ignore */ increment = function(d) { d.setDate(d.getDate() + 7 * step); };
     3213      } else if (span >= 3 * 864e5) {
     3214        precision = 864e5;
     3215        format = "%m/%d";
     3216        /** @ignore */ increment = function(d) { d.setDate(d.getDate() + step); };
     3217      } else if (span >= 3 * 36e5) {
     3218        precision = 36e5;
     3219        format = "%I:%M %p";
     3220        /** @ignore */ increment = function(d) { d.setHours(d.getHours() + step); };
     3221      } else if (span >= 3 * 6e4) {
     3222        precision = 6e4;
     3223        format = "%I:%M %p";
     3224        /** @ignore */ increment = function(d) { d.setMinutes(d.getMinutes() + step); };
     3225      } else if (span >= 3 * 1e3) {
     3226        precision = 1e3;
     3227        format = "%I:%M:%S";
     3228        /** @ignore */ increment = function(d) { d.setSeconds(d.getSeconds() + step); };
     3229      } else {
     3230        precision = 1;
     3231        format = "%S.%Qs";
     3232        /** @ignore */ increment = function(d) { d.setTime(d.getTime() + step); };
     3233      }
     3234      tickFormat = pv.Format.date(format);
     3235
     3236      var date = new Date(min), dates = [];
     3237      floor(date, precision);
     3238
     3239      /* If we'd generate too many ticks, skip some!. */
     3240      var n = span / precision;
     3241      if (n > 10) {
     3242        switch (precision) {
     3243          case 36e5: {
     3244            step = (n > 20) ? 6 : 3;
     3245            date.setHours(Math.floor(date.getHours() / step) * step);
     3246            break;
     3247          }
     3248          case 2592e6: {
     3249            step = 3; // seasons
     3250            date.setMonth(Math.floor(date.getMonth() / step) * step);
     3251            break;
     3252          }
     3253          case 6e4: {
     3254            step = (n > 30) ? 15 : ((n > 15) ? 10 : 5);
     3255            date.setMinutes(Math.floor(date.getMinutes() / step) * step);
     3256            break;
     3257          }
     3258          case 1e3: {
     3259            step = (n > 90) ? 15 : ((n > 60) ? 10 : 5);
     3260            date.setSeconds(Math.floor(date.getSeconds() / step) * step);
     3261            break;
     3262          }
     3263          case 1: {
     3264            step = (n > 1000) ? 250 : ((n > 200) ? 100 : ((n > 100) ? 50 : ((n > 50) ? 25 : 5)));
     3265            date.setMilliseconds(Math.floor(date.getMilliseconds() / step) * step);
     3266            break;
     3267          }
     3268          default: {
     3269            step = pv.logCeil(n / 15, 10);
     3270            if (n / step < 2) step /= 5;
     3271            else if (n / step < 5) step /= 2;
     3272            date.setFullYear(Math.floor(date.getFullYear() / step) * step);
     3273            break;
     3274          }
     3275        }
     3276      }
     3277
     3278      while (true) {
     3279        increment(date);
     3280        if (date > max) break;
     3281        dates.push(new Date(date));
     3282      }
     3283      return reverse ? dates.reverse() : dates;
     3284    }
     3285
     3286    /* Normal case: numbers. */
     3287    if (!arguments.length) m = 10;
     3288    var step = pv.logFloor(span / m, 10),
     3289        err = m / (span / step);
     3290    if (err <= .15) step *= 10;
     3291    else if (err <= .35) step *= 5;
     3292    else if (err <= .75) step *= 2;
     3293    var start = Math.ceil(min / step) * step,
     3294        end = Math.floor(max / step) * step;
     3295    tickFormat = pv.Format.number()
     3296        .fractionDigits(Math.max(0, -Math.floor(pv.log(step, 10) + .01)));
     3297    var ticks = pv.range(start, end + step, step);
     3298    return reverse ? ticks.reverse() : ticks;
     3299  };
     3300
     3301  /**
     3302   * Formats the specified tick value using the appropriate precision, based on
     3303   * the step interval between tick marks. If {@link #ticks} has not been called,
     3304   * the argument is converted to a string, but no formatting is applied.
     3305   *
     3306   * @function
     3307   * @name pv.Scale.quantitative.prototype.tickFormat
     3308   * @param {number} t a tick value.
     3309   * @returns {string} a formatted tick value.
     3310   */
     3311  scale.tickFormat = function (t) { return tickFormat(t); };
     3312
     3313  /**
     3314   * "Nices" this scale, extending the bounds of the input domain to
     3315   * evenly-rounded values. Nicing is useful if the domain is computed
     3316   * dynamically from data, and may be irregular. For example, given a domain of
     3317   * [0.20147987687960267, 0.996679553296417], a call to <tt>nice()</tt> might
     3318   * extend the domain to [0.2, 1].
     3319   *
     3320   * <p>This method must be invoked each time after setting the domain.
     3321   *
     3322   * @function
     3323   * @name pv.Scale.quantitative.prototype.nice
     3324   * @returns {pv.Scale.quantitative} <tt>this</tt>.
     3325   */
     3326  scale.nice = function() {
     3327    if (d.length != 2) return this; // TODO support non-uniform domains
     3328    var start = d[0],
     3329        end = d[d.length - 1],
     3330        reverse = end < start,
     3331        min = reverse ? end : start,
     3332        max = reverse ? start : end,
     3333        span = max - min;
     3334
     3335    /* Special case: empty, invalid or infinite span. */
     3336    if (!span || !isFinite(span)) return this;
     3337
     3338    var step = Math.pow(10, Math.round(Math.log(span) / Math.log(10)) - 1);
     3339    d = [Math.floor(min / step) * step, Math.ceil(max / step) * step];
     3340    if (reverse) d.reverse();
     3341    l = d.map(f);
     3342    return this;
     3343  };
     3344
     3345  /**
     3346   * Returns a view of this scale by the specified accessor function <tt>f</tt>.
     3347   * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
     3348   * <tt>function(d) y(d.foo)</tt>.
     3349   *
     3350   * <p>This method is provided for convenience, such that scales can be
     3351   * succinctly defined inline. For example, given an array of data elements
     3352   * that have a <tt>score</tt> attribute with the domain [0, 1], the height
     3353   * property could be specified as:
     3354   *
     3355   * <pre>    .height(pv.Scale.linear().range(0, 480).by(function(d) d.score))</pre>
     3356   *
     3357   * This is equivalent to:
     3358   *
     3359   * <pre>    .height(function(d) d.score * 480)</pre>
     3360   *
     3361   * This method should be used judiciously; it is typically more clear to
     3362   * invoke the scale directly, passing in the value to be scaled.
     3363   *
     3364   * @function
     3365   * @name pv.Scale.quantitative.prototype.by
     3366   * @param {function} f an accessor function.
     3367   * @returns {pv.Scale.quantitative} a view of this scale by the specified
     3368   * accessor function.
     3369   */
     3370  scale.by = function(f) {
     3371    function by() { return scale(f.apply(this, arguments)); }
     3372    for (var method in scale) by[method] = scale[method];
     3373    return by;
     3374  };
     3375
     3376  scale.domain.apply(scale, arguments);
     3377  return scale;
     3378};
     3379/**
     3380 * Returns a linear scale for the specified domain. The arguments to this
     3381 * constructor are optional, and equivalent to calling {@link #domain}.
     3382 * The default domain and range are [0,1].
     3383 *
     3384 * @class Represents a linear scale; a function that performs a linear
     3385 * transformation. <style type="text/css">sub{line-height:0}</style> Most
     3386 * commonly, a linear scale represents a 1-dimensional linear transformation
     3387 * from a numeric domain of input data [<i>d<sub>0</sub></i>,
     3388 * <i>d<sub>1</sub></i>] to a numeric range of pixels [<i>r<sub>0</sub></i>,
     3389 * <i>r<sub>1</sub></i>]. The equation for such a scale is:
     3390 *
     3391 * <blockquote><i>f(x) = (x - d<sub>0</sub>) / (d<sub>1</sub> - d<sub>0</sub>) *
     3392 * (r<sub>1</sub> - r<sub>0</sub>) + r<sub>0</sub></i></blockquote>
     3393 *
     3394 * For example, a linear scale from the domain [0, 100] to range [0, 640]:
     3395 *
     3396 * <blockquote><i>f(x) = (x - 0) / (100 - 0) * (640 - 0) + 0</i><br>
     3397 * <i>f(x) = x / 100 * 640</i><br>
     3398 * <i>f(x) = x * 6.4</i><br>
     3399 * </blockquote>
     3400 *
     3401 * Thus, saying
     3402 *
     3403 * <pre>    .height(function(d) d * 6.4)</pre>
     3404 *
     3405 * is identical to
     3406 *
     3407 * <pre>    .height(pv.Scale.linear(0, 100).range(0, 640))</pre>
     3408 *
     3409 * Note that the scale is itself a function, and thus can be used as a property
     3410 * directly, assuming that the data associated with a mark is a number. While
     3411 * this is convenient for single-use scales, frequently it is desirable to
     3412 * define scales globally:
     3413 *
     3414 * <pre>var y = pv.Scale.linear(0, 100).range(0, 640);</pre>
     3415 *
     3416 * The <tt>y</tt> scale can now be equivalently referenced within a property:
     3417 *
     3418 * <pre>    .height(function(d) y(d))</pre>
     3419 *
     3420 * Alternatively, if the data are not simple numbers, the appropriate value can
     3421 * be passed to the <tt>y</tt> scale (e.g., <tt>d.foo</tt>). The {@link #by}
     3422 * method similarly allows the data to be mapped to a numeric value before
     3423 * performing the linear transformation.
     3424 *
     3425 * @param {number...} domain... optional domain values.
     3426 * @extends pv.Scale.quantitative
     3427 */
     3428pv.Scale.linear = function() {
     3429  var scale = pv.Scale.quantitative();
     3430  scale.domain.apply(scale, arguments);
     3431  return scale;
     3432};
     3433/**
     3434 * Returns a log scale for the specified domain. The arguments to this
     3435 * constructor are optional, and equivalent to calling {@link #domain}.
     3436 * The default domain is [1,10] and the default range is [0,1].
     3437 *
     3438 * @class Represents a log scale. <style
     3439 * type="text/css">sub{line-height:0}</style> Most commonly, a log scale
     3440 * represents a 1-dimensional log transformation from a numeric domain of input
     3441 * data [<i>d<sub>0</sub></i>, <i>d<sub>1</sub></i>] to a numeric range of
     3442 * pixels [<i>r<sub>0</sub></i>, <i>r<sub>1</sub></i>]. The equation for such a
     3443 * scale is:
     3444 *
     3445 * <blockquote><i>f(x) = (log(x) - log(d<sub>0</sub>)) / (log(d<sub>1</sub>) -
     3446 * log(d<sub>0</sub>)) * (r<sub>1</sub> - r<sub>0</sub>) +
     3447 * r<sub>0</sub></i></blockquote>
     3448 *
     3449 * where <i>log(x)</i> represents the zero-symmetric logarthim of <i>x</i> using
     3450 * the scale's associated base (default: 10, see {@link pv.logSymmetric}). For
     3451 * example, a log scale from the domain [1, 100] to range [0, 640]:
     3452 *
     3453 * <blockquote><i>f(x) = (log(x) - log(1)) / (log(100) - log(1)) * (640 - 0) + 0</i><br>
     3454 * <i>f(x) = log(x) / 2 * 640</i><br>
     3455 * <i>f(x) = log(x) * 320</i><br>
     3456 * </blockquote>
     3457 *
     3458 * Thus, saying
     3459 *
     3460 * <pre>    .height(function(d) Math.log(d) * 138.974)</pre>
     3461 *
     3462 * is equivalent to
     3463 *
     3464 * <pre>    .height(pv.Scale.log(1, 100).range(0, 640))</pre>
     3465 *
     3466 * Note that the scale is itself a function, and thus can be used as a property
     3467 * directly, assuming that the data associated with a mark is a number. While
     3468 * this is convenient for single-use scales, frequently it is desirable to
     3469 * define scales globally:
     3470 *
     3471 * <pre>var y = pv.Scale.log(1, 100).range(0, 640);</pre>
     3472 *
     3473 * The <tt>y</tt> scale can now be equivalently referenced within a property:
     3474 *
     3475 * <pre>    .height(function(d) y(d))</pre>
     3476 *
     3477 * Alternatively, if the data are not simple numbers, the appropriate value can
     3478 * be passed to the <tt>y</tt> scale (e.g., <tt>d.foo</tt>). The {@link #by}
     3479 * method similarly allows the data to be mapped to a numeric value before
     3480 * performing the log transformation.
     3481 *
     3482 * @param {number...} domain... optional domain values.
     3483 * @extends pv.Scale.quantitative
     3484 */
     3485pv.Scale.log = function() {
     3486  var scale = pv.Scale.quantitative(1, 10),
     3487      b, // logarithm base
     3488      p, // cached Math.log(b)
     3489      /** @ignore */ log = function(x) { return Math.log(x) / p; },
     3490      /** @ignore */ pow = function(y) { return Math.pow(b, y); };
     3491
     3492  /**
     3493   * Returns an array of evenly-spaced, suitably-rounded values in the input
     3494   * domain. These values are frequently used in conjunction with
     3495   * {@link pv.Rule} to display tick marks or grid lines.
     3496   *
     3497   * @function
     3498   * @name pv.Scale.log.prototype.ticks
     3499   * @returns {number[]} an array input domain values to use as ticks.
     3500   */
     3501  scale.ticks = function() {
     3502    // TODO support non-uniform domains
     3503    var d = scale.domain(),
     3504        n = d[0] < 0,
     3505        i = Math.floor(n ? -log(-d[0]) : log(d[0])),
     3506        j = Math.ceil(n ? -log(-d[1]) : log(d[1])),
     3507        ticks = [];
     3508    if (n) {
     3509      ticks.push(-pow(-i));
     3510      for (; i++ < j;) for (var k = b - 1; k > 0; k--) ticks.push(-pow(-i) * k);
     3511    } else {
     3512      for (; i < j; i++) for (var k = 1; k < b; k++) ticks.push(pow(i) * k);
     3513      ticks.push(pow(i));
     3514    }
     3515    for (i = 0; ticks[i] < d[0]; i++); // strip small values
     3516    for (j = ticks.length; ticks[j - 1] > d[1]; j--); // strip big values
     3517    return ticks.slice(i, j);
     3518  };
     3519
     3520  /**
     3521   * Formats the specified tick value using the appropriate precision, assuming
     3522   * base 10.
     3523   *
     3524   * @function
     3525   * @name pv.Scale.log.prototype.tickFormat
     3526   * @param {number} t a tick value.
     3527   * @returns {string} a formatted tick value.
     3528   */
     3529  scale.tickFormat = function(t) {
     3530    return t.toPrecision(1);
     3531  };
     3532
     3533  /**
     3534   * "Nices" this scale, extending the bounds of the input domain to
     3535   * evenly-rounded values. This method uses {@link pv.logFloor} and
     3536   * {@link pv.logCeil}. Nicing is useful if the domain is computed dynamically
     3537   * from data, and may be irregular. For example, given a domain of
     3538   * [0.20147987687960267, 0.996679553296417], a call to <tt>nice()</tt> might
     3539   * extend the domain to [0.1, 1].
     3540   *
     3541   * <p>This method must be invoked each time after setting the domain (and
     3542   * base).
     3543   *
     3544   * @function
     3545   * @name pv.Scale.log.prototype.nice
     3546   * @returns {pv.Scale.log} <tt>this</tt>.
     3547   */
     3548  scale.nice = function() {
     3549    // TODO support non-uniform domains
     3550    var d = scale.domain();
     3551    return scale.domain(pv.logFloor(d[0], b), pv.logCeil(d[1], b));
     3552  };
     3553
     3554  /**
     3555   * Sets or gets the logarithm base. Defaults to 10.
     3556   *
     3557   * @function
     3558   * @name pv.Scale.log.prototype.base
     3559   * @param {number} [v] the new base.
     3560   * @returns {pv.Scale.log} <tt>this</tt>, or the current base.
     3561   */
     3562  scale.base = function(v) {
     3563    if (arguments.length) {
     3564      b = Number(v);
     3565      p = Math.log(b);
     3566      scale.transform(log, pow); // update transformed domain
     3567      return this;
     3568    }
     3569    return b;
     3570  };
     3571
     3572  scale.domain.apply(scale, arguments);
     3573  return scale.base(10);
     3574};
     3575/**
     3576 * Returns a root scale for the specified domain. The arguments to this
     3577 * constructor are optional, and equivalent to calling {@link #domain}.
     3578 * The default domain and range are [0,1].
     3579 *
     3580 * @class Represents a root scale; a function that performs a power
     3581 * transformation. <style type="text/css">sub{line-height:0}</style> Most
     3582 * commonly, a root scale represents a 1-dimensional root transformation from a
     3583 * numeric domain of input data [<i>d<sub>0</sub></i>, <i>d<sub>1</sub></i>] to
     3584 * a numeric range of pixels [<i>r<sub>0</sub></i>, <i>r<sub>1</sub></i>].
     3585 *
     3586 * <p>Note that the scale is itself a function, and thus can be used as a
     3587 * property directly, assuming that the data associated with a mark is a
     3588 * number. While this is convenient for single-use scales, frequently it is
     3589 * desirable to define scales globally:
     3590 *
     3591 * <pre>var y = pv.Scale.root(0, 100).range(0, 640);</pre>
     3592 *
     3593 * The <tt>y</tt> scale can now be equivalently referenced within a property:
     3594 *
     3595 * <pre>    .height(function(d) y(d))</pre>
     3596 *
     3597 * Alternatively, if the data are not simple numbers, the appropriate value can
     3598 * be passed to the <tt>y</tt> scale (e.g., <tt>d.foo</tt>). The {@link #by}
     3599 * method similarly allows the data to be mapped to a numeric value before
     3600 * performing the root transformation.
     3601 *
     3602 * @param {number...} domain... optional domain values.
     3603 * @extends pv.Scale.quantitative
     3604 */
     3605pv.Scale.root = function() {
     3606  var scale = pv.Scale.quantitative();
     3607
     3608  /**
     3609   * Sets or gets the exponent; defaults to 2.
     3610   *
     3611   * @function
     3612   * @name pv.Scale.root.prototype.power
     3613   * @param {number} [v] the new exponent.
     3614   * @returns {pv.Scale.root} <tt>this</tt>, or the current base.
     3615   */
     3616  scale.power = function(v) {
     3617    if (arguments.length) {
     3618      var b = Number(v), p = 1 / b;
     3619      scale.transform(
     3620        function(x) { return Math.pow(x, p); },
     3621        function(y) { return Math.pow(y, b); });
     3622      return this;
     3623    }
     3624    return b;
     3625  };
     3626
     3627  scale.domain.apply(scale, arguments);
     3628  return scale.power(2);
     3629};
     3630/**
     3631 * Returns an ordinal scale for the specified domain. The arguments to this
     3632 * constructor are optional, and equivalent to calling {@link #domain}.
     3633 *
     3634 * @class Represents an ordinal scale. <style
     3635 * type="text/css">sub{line-height:0}</style> An ordinal scale represents a
     3636 * pairwise mapping from <i>n</i> discrete values in the input domain to
     3637 * <i>n</i> discrete values in the output range. For example, an ordinal scale
     3638 * might map a domain of species ["setosa", "versicolor", "virginica"] to colors
     3639 * ["red", "green", "blue"]. Thus, saying
     3640 *
     3641 * <pre>    .fillStyle(function(d) {
     3642 *         switch (d.species) {
     3643 *           case "setosa": return "red";
     3644 *           case "versicolor": return "green";
     3645 *           case "virginica": return "blue";
     3646 *         }
     3647 *       })</pre>
     3648 *
     3649 * is equivalent to
     3650 *
     3651 * <pre>    .fillStyle(pv.Scale.ordinal("setosa", "versicolor", "virginica")
     3652 *         .range("red", "green", "blue")
     3653 *         .by(function(d) d.species))</pre>
     3654 *
     3655 * If the mapping from species to color does not need to be specified
     3656 * explicitly, the domain can be omitted. In this case it will be inferred
     3657 * lazily from the data:
     3658 *
     3659 * <pre>    .fillStyle(pv.colors("red", "green", "blue")
     3660 *         .by(function(d) d.species))</pre>
     3661 *
     3662 * When the domain is inferred, the first time the scale is invoked, the first
     3663 * element from the range will be returned. Subsequent calls with unique values
     3664 * will return subsequent elements from the range. If the inferred domain grows
     3665 * larger than the range, range values will be reused. However, it is strongly
     3666 * recommended that the domain and the range contain the same number of
     3667 * elements.
     3668 *
     3669 * <p>A range can be discretized from a continuous interval (e.g., for pixel
     3670 * positioning) by using {@link #split}, {@link #splitFlush} or
     3671 * {@link #splitBanded} after the domain has been set. For example, if
     3672 * <tt>states</tt> is an array of the fifty U.S. state names, the state name can
     3673 * be encoded in the left position:
     3674 *
     3675 * <pre>    .left(pv.Scale.ordinal(states)
     3676 *         .split(0, 640)
     3677 *         .by(function(d) d.state))</pre>
     3678 *
     3679 * <p>N.B.: ordinal scales are not invertible (at least not yet), since the
     3680 * domain and range and discontinuous. A workaround is to use a linear scale.
     3681 *
     3682 * @param {...} domain... optional domain values.
     3683 * @extends pv.Scale
     3684 * @see pv.colors
     3685 */
     3686pv.Scale.ordinal = function() {
     3687  var d = [], i = {}, r = [], band = 0;
     3688
     3689  /** @private */
     3690  function scale(x) {
     3691    if (!(x in i)) i[x] = d.push(x) - 1;
     3692    return r[i[x] % r.length];
     3693  }
     3694
     3695  /**
     3696   * Sets or gets the input domain. This method can be invoked several ways:
     3697   *
     3698   * <p>1. <tt>domain(values...)</tt>
     3699   *
     3700   * <p>Specifying the domain as a series of values is the most explicit and
     3701   * recommended approach. However, if the domain values are derived from data,
     3702   * you may find the second method more appropriate.
     3703   *
     3704   * <p>2. <tt>domain(array, f)</tt>
     3705   *
     3706   * <p>Rather than enumerating the domain values as explicit arguments to this
     3707   * method, you can specify a single argument of an array. In addition, you can
     3708   * specify an optional accessor function to extract the domain values from the
     3709   * array.
     3710   *
     3711   * <p>3. <tt>domain()</tt>
     3712   *
     3713   * <p>Invoking the <tt>domain</tt> method with no arguments returns the
     3714   * current domain as an array.
     3715   *
     3716   * @function
     3717   * @name pv.Scale.ordinal.prototype.domain
     3718   * @param {...} domain... domain values.
     3719   * @returns {pv.Scale.ordinal} <tt>this</tt>, or the current domain.
     3720   */
     3721  scale.domain = function(array, f) {
     3722    if (arguments.length) {
     3723      array = (array instanceof Array)
     3724          ? ((arguments.length > 1) ? pv.map(array, f) : array)
     3725          : Array.prototype.slice.call(arguments);
     3726
     3727      /* Filter the specified ordinals to their unique values. */
     3728      d = [];
     3729      var seen = {};
     3730      for (var j = 0; j < array.length; j++) {
     3731        var o = array[j];
     3732        if (!(o in seen)) {
     3733          seen[o] = true;
     3734          d.push(o);
     3735        }
     3736      }
     3737
     3738      i = pv.numerate(d);
     3739      return this;
     3740    }
     3741    return d;
     3742  };
     3743
     3744  /**
     3745   * Sets or gets the output range. This method can be invoked several ways:
     3746   *
     3747   * <p>1. <tt>range(values...)</tt>
     3748   *
     3749   * <p>Specifying the range as a series of values is the most explicit and
     3750   * recommended approach. However, if the range values are derived from data,
     3751   * you may find the second method more appropriate.
     3752   *
     3753   * <p>2. <tt>range(array, f)</tt>
     3754   *
     3755   * <p>Rather than enumerating the range values as explicit arguments to this
     3756   * method, you can specify a single argument of an array. In addition, you can
     3757   * specify an optional accessor function to extract the range values from the
     3758   * array.
     3759   *
     3760   * <p>3. <tt>range()</tt>
     3761   *
     3762   * <p>Invoking the <tt>range</tt> method with no arguments returns the
     3763   * current range as an array.
     3764   *
     3765   * @function
     3766   * @name pv.Scale.ordinal.prototype.range
     3767   * @param {...} range... range values.
     3768   * @returns {pv.Scale.ordinal} <tt>this</tt>, or the current range.
     3769   */
     3770  scale.range = function(array, f) {
     3771    if (arguments.length) {
     3772      r = (array instanceof Array)
     3773          ? ((arguments.length > 1) ? pv.map(array, f) : array)
     3774          : Array.prototype.slice.call(arguments);
     3775      if (typeof r[0] == "string") r = r.map(pv.color);
     3776      return this;
     3777    }
     3778    return r;
     3779  };
     3780
     3781  /**
     3782   * Sets the range from the given continuous interval. The interval
     3783   * [<i>min</i>, <i>max</i>] is subdivided into <i>n</i> equispaced points,
     3784   * where <i>n</i> is the number of (unique) values in the domain. The first
     3785   * and last point are offset from the edge of the range by half the distance
     3786   * between points.
     3787   *
     3788   * <p>This method must be called <i>after</i> the domain is set.
     3789   *
     3790   * @function
     3791   * @name pv.Scale.ordinal.prototype.split
     3792   * @param {number} min minimum value of the output range.
     3793   * @param {number} max maximum value of the output range.
     3794   * @returns {pv.Scale.ordinal} <tt>this</tt>.
     3795   * @see #splitFlush
     3796   * @see #splitBanded
     3797   */
     3798  scale.split = function(min, max) {
     3799    var step = (max - min) / this.domain().length;
     3800    r = pv.range(min + step / 2, max, step);
     3801    return this;
     3802  };
     3803
     3804  /**
     3805   * Sets the range from the given continuous interval. The interval
     3806   * [<i>min</i>, <i>max</i>] is subdivided into <i>n</i> equispaced points,
     3807   * where <i>n</i> is the number of (unique) values in the domain. The first
     3808   * and last point are exactly on the edge of the range.
     3809   *
     3810   * <p>This method must be called <i>after</i> the domain is set.
     3811   *
     3812   * @function
     3813   * @name pv.Scale.ordinal.prototype.splitFlush
     3814   * @param {number} min minimum value of the output range.
     3815   * @param {number} max maximum value of the output range.
     3816   * @returns {pv.Scale.ordinal} <tt>this</tt>.
     3817   * @see #split
     3818   */
     3819  scale.splitFlush = function(min, max) {
     3820    var n = this.domain().length, step = (max - min) / (n - 1);
     3821    r = (n == 1) ? [(min + max) / 2]
     3822        : pv.range(min, max + step / 2, step);
     3823    return this;
     3824  };
     3825
     3826  /**
     3827   * Sets the range from the given continuous interval. The interval
     3828   * [<i>min</i>, <i>max</i>] is subdivided into <i>n</i> equispaced bands,
     3829   * where <i>n</i> is the number of (unique) values in the domain. The first
     3830   * and last band are offset from the edge of the range by the distance between
     3831   * bands.
     3832   *
     3833   * <p>The band width argument, <tt>band</tt>, is typically in the range [0, 1]
     3834   * and defaults to 1. This fraction corresponds to the amount of space in the
     3835   * range to allocate to the bands, as opposed to padding. A value of 0.5 means
     3836   * that the band width will be equal to the padding width. The computed
     3837   * absolute band width can be retrieved from the range as
     3838   * <tt>scale.range().band</tt>.
     3839   *
     3840   * <p>If the band width argument is negative, this method will allocate bands
     3841   * of a <i>fixed</i> width <tt>-band</tt>, rather than a relative fraction of
     3842   * the available space.
     3843   *
     3844   * <p>Tip: to inset the bands by a fixed amount <tt>p</tt>, specify a minimum
     3845   * value of <tt>min + p</tt> (or simply <tt>p</tt>, if <tt>min</tt> is
     3846   * 0). Then set the mark width to <tt>scale.range().band - p</tt>.
     3847   *
     3848   * <p>This method must be called <i>after</i> the domain is set.
     3849   *
     3850   * @function
     3851   * @name pv.Scale.ordinal.prototype.splitBanded
     3852   * @param {number} min minimum value of the output range.
     3853   * @param {number} max maximum value of the output range.
     3854   * @param {number} [band] the fractional band width in [0, 1]; defaults to 1.
     3855   * @returns {pv.Scale.ordinal} <tt>this</tt>.
     3856   * @see #split
     3857   */
     3858  scale.splitBanded = function(min, max, band) {
     3859    if (arguments.length < 3) band = 1;
     3860    if (band < 0) {
     3861      var n = this.domain().length,
     3862          total = -band * n,
     3863          remaining = max - min - total,
     3864          padding = remaining / (n + 1);
     3865      r = pv.range(min + padding, max, padding - band);
     3866      r.band = -band;
     3867    } else {
     3868      var step = (max - min) / (this.domain().length + (1 - band));
     3869      r = pv.range(min + step * (1 - band), max, step);
     3870      r.band = step * band;
     3871    }
     3872    return this;
     3873  };
     3874
     3875  /**
     3876   * Returns a view of this scale by the specified accessor function <tt>f</tt>.
     3877   * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
     3878   * <tt>function(d) y(d.foo)</tt>. This method should be used judiciously; it
     3879   * is typically more clear to invoke the scale directly, passing in the value
     3880   * to be scaled.
     3881   *
     3882   * @function
     3883   * @name pv.Scale.ordinal.prototype.by
     3884   * @param {function} f an accessor function.
     3885   * @returns {pv.Scale.ordinal} a view of this scale by the specified accessor
     3886   * function.
     3887   */
     3888  scale.by = function(f) {
     3889    function by() { return scale(f.apply(this, arguments)); }
     3890    for (var method in scale) by[method] = scale[method];
     3891    return by;
     3892  };
     3893
     3894  scale.domain.apply(scale, arguments);
     3895  return scale;
     3896};
     3897/**
     3898 * Constructs a default quantile scale. The arguments to this constructor are
     3899 * optional, and equivalent to calling {@link #domain}. The default domain is
     3900 * the empty set, and the default range is [0,1].
     3901 *
     3902 * @class Represents a quantile scale; a function that maps from a value within
     3903 * a sortable domain to a quantized numeric range. Typically, the domain is a
     3904 * set of numbers, but any sortable value (such as strings) can be used as the
     3905 * domain of a quantile scale. The range defaults to [0,1], with 0 corresponding
     3906 * to the smallest value in the domain, 1 the largest, .5 the median, etc.
     3907 *
     3908 * <p>By default, the number of quantiles in the range corresponds to the number
     3909 * of values in the domain. The {@link #quantiles} method can be used to specify
     3910 * an explicit number of quantiles; for example, <tt>quantiles(4)</tt> produces
     3911 * a standard quartile scale. A quartile scale's range is a set of four discrete
     3912 * values, such as [0, 1/3, 2/3, 1]. Calling the {@link #range} method will
     3913 * scale these discrete values accordingly, similar to {@link
     3914 * pv.Scale.ordinal#splitFlush}.
     3915 *
     3916 * <p>For example, given the strings ["c", "a", "b"], a default quantile scale:
     3917 *
     3918 * <pre>pv.Scale.quantile("c", "a", "b")</pre>
     3919 *
     3920 * will return 0 for "a", .5 for "b", and 1 for "c".
     3921 *
     3922 * @extends pv.Scale
     3923 */
     3924pv.Scale.quantile = function() {
     3925  var n = -1, // number of quantiles
     3926      j = -1, // max quantile index
     3927      q = [], // quantile boundaries
     3928      d = [], // domain
     3929      y = pv.Scale.linear(); // range
     3930
     3931  /** @private */
     3932  function scale(x) {
     3933    return y(Math.max(0, Math.min(j, pv.search.index(q, x) - 1)) / j);
     3934  }
     3935
     3936  /**
     3937   * Sets or gets the quantile boundaries. By default, each element in the
     3938   * domain is in its own quantile. If the argument to this method is a number,
     3939   * it specifies the number of equal-sized quantiles by which to divide the
     3940   * domain.
     3941   *
     3942   * <p>If no arguments are specified, this method returns the quantile
     3943   * boundaries; the first element is always the minimum value of the domain,
     3944   * and the last element is the maximum value of the domain. Thus, the length
     3945   * of the returned array is always one greater than the number of quantiles.
     3946   *
     3947   * @function
     3948   * @name pv.Scale.quantile.prototype.quantiles
     3949   * @param {number} x the number of quantiles.
     3950   */
     3951  scale.quantiles = function(x) {
     3952    if (arguments.length) {
     3953      n = Number(x);
     3954      if (n < 0) {
     3955        q = [d[0]].concat(d);
     3956        j = d.length - 1;
     3957      } else {
     3958        q = [];
     3959        q[0] = d[0];
     3960        for (var i = 1; i <= n; i++) {
     3961          q[i] = d[~~(i * (d.length - 1) / n)];
     3962        }
     3963        j = n - 1;
     3964      }
     3965      return this;
     3966    }
     3967    return q;
     3968  };
     3969
     3970  /**
     3971   * Sets or gets the input domain. This method can be invoked several ways:
     3972   *
     3973   * <p>1. <tt>domain(values...)</tt>
     3974   *
     3975   * <p>Specifying the domain as a series of values is the most explicit and
     3976   * recommended approach. However, if the domain values are derived from data,
     3977   * you may find the second method more appropriate.
     3978   *
     3979   * <p>2. <tt>domain(array, f)</tt>
     3980   *
     3981   * <p>Rather than enumerating the domain values as explicit arguments to this
     3982   * method, you can specify a single argument of an array. In addition, you can
     3983   * specify an optional accessor function to extract the domain values from the
     3984   * array.
     3985   *
     3986   * <p>3. <tt>domain()</tt>
     3987   *
     3988   * <p>Invoking the <tt>domain</tt> method with no arguments returns the
     3989   * current domain as an array.
     3990   *
     3991   * @function
     3992   * @name pv.Scale.quantile.prototype.domain
     3993   * @param {...} domain... domain values.
     3994   * @returns {pv.Scale.quantile} <tt>this</tt>, or the current domain.
     3995   */
     3996  scale.domain = function(array, f) {
     3997    if (arguments.length) {
     3998      d = (array instanceof Array)
     3999          ? pv.map(array, f)
     4000          : Array.prototype.slice.call(arguments);
     4001      d.sort(pv.naturalOrder);
     4002      scale.quantiles(n); // recompute quantiles
     4003      return this;
     4004    }
     4005    return d;
     4006  };
     4007
     4008  /**
     4009   * Sets or gets the output range. This method can be invoked several ways:
     4010   *
     4011   * <p>1. <tt>range(min, ..., max)</tt>
     4012   *
     4013   * <p>The range may be specified as a series of numbers or colors. Most
     4014   * commonly, two numbers are specified: the minimum and maximum pixel values.
     4015   * For a color scale, values may be specified as {@link pv.Color}s or
     4016   * equivalent strings. For a diverging scale, or other subdivided non-uniform
     4017   * scales, multiple values can be specified. For example:
     4018   *
     4019   * <pre>    .range("red", "white", "green")</pre>
     4020   *
     4021   * <p>Currently, only numbers and colors are supported as range values. The
     4022   * number of range values must exactly match the number of domain values, or
     4023   * the behavior of the scale is undefined.
     4024   *
     4025   * <p>2. <tt>range()</tt>
     4026   *
     4027   * <p>Invoking the <tt>range</tt> method with no arguments returns the current
     4028   * range as an array of numbers or colors.
     4029   *
     4030   * @function
     4031   * @name pv.Scale.quantile.prototype.range
     4032   * @param {...} range... range values.
     4033   * @returns {pv.Scale.quantile} <tt>this</tt>, or the current range.
     4034   */
     4035  scale.range = function() {
     4036    if (arguments.length) {
     4037      y.range.apply(y, arguments);
     4038      return this;
     4039    }
     4040    return y.range();
     4041  };
     4042
     4043  /**
     4044   * Returns a view of this scale by the specified accessor function <tt>f</tt>.
     4045   * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to
     4046   * <tt>function(d) y(d.foo)</tt>.
     4047   *
     4048   * <p>This method is provided for convenience, such that scales can be
     4049   * succinctly defined inline. For example, given an array of data elements
     4050   * that have a <tt>score</tt> attribute with the domain [0, 1], the height
     4051   * property could be specified as:
     4052   *
     4053   * <pre>.height(pv.Scale.linear().range(0, 480).by(function(d) d.score))</pre>
     4054   *
     4055   * This is equivalent to:
     4056   *
     4057   * <pre>.height(function(d) d.score * 480)</pre>
     4058   *
     4059   * This method should be used judiciously; it is typically more clear to
     4060   * invoke the scale directly, passing in the value to be scaled.
     4061   *
     4062   * @function
     4063   * @name pv.Scale.quantile.prototype.by
     4064   * @param {function} f an accessor function.
     4065   * @returns {pv.Scale.quantile} a view of this scale by the specified
     4066   * accessor function.
     4067   */
     4068  scale.by = function(f) {
     4069    function by() { return scale(f.apply(this, arguments)); }
     4070    for (var method in scale) by[method] = scale[method];
     4071    return by;
     4072  };
     4073
     4074  scale.domain.apply(scale, arguments);
     4075  return scale;
     4076};
     4077/**
     4078 * Returns a histogram operator for the specified data, with an optional
     4079 * accessor function. If the data specified is not an array of numbers, an
     4080 * accessor function must be specified to map the data to numeric values.
     4081 *
     4082 * @class Represents a histogram operator.
     4083 *
     4084 * @param {array} data an array of numbers or objects.
     4085 * @param {function} [f] an optional accessor function.
     4086 */
     4087pv.histogram = function(data, f) {
     4088  var frequency = true;
     4089  return {
     4090
     4091    /**
     4092     * Returns the computed histogram bins. An optional array of numbers,
     4093     * <tt>ticks</tt>, may be specified as the break points. If the ticks are
     4094     * not specified, default ticks will be computed using a linear scale on the
     4095     * data domain.
     4096     *
     4097     * <p>The returned array contains {@link pv.histogram.Bin}s. The <tt>x</tt>
     4098     * attribute corresponds to the bin's start value (inclusive), while the
     4099     * <tt>dx</tt> attribute stores the bin size (end - start). The <tt>y</tt>
     4100     * attribute stores either the frequency count or probability, depending on
     4101     * how the histogram operator has been configured.
     4102     *
     4103     * <p>The {@link pv.histogram.Bin} objects are themselves arrays, containing
     4104     * the data elements present in each bin, i.e., the elements in the
     4105     * <tt>data</tt> array (prior to invoking the accessor function, if any).
     4106     * For example, if the data represented countries, and the accessor function
     4107     * returned the GDP of each country, the returned bins would be arrays of
     4108     * countries (not GDPs).
     4109     *
     4110     * @function
     4111     * @name pv.histogram.prototype.bins
     4112     * @param {array} [ticks]
     4113     * @returns {array}
     4114     */ /** @private */
     4115    bins: function(ticks) {
     4116      var x = pv.map(data, f), bins = [];
     4117
     4118      /* Initialize default ticks. */
     4119      if (!arguments.length) ticks = pv.Scale.linear(x).ticks();
     4120
     4121      /* Initialize the bins. */
     4122      for (var i = 0; i < ticks.length - 1; i++) {
     4123        var bin = bins[i] = [];
     4124        bin.x = ticks[i];
     4125        bin.dx = ticks[i + 1] - ticks[i];
     4126        bin.y = 0;
     4127      }
     4128
     4129      /* Count the number of samples per bin. */
     4130      for (var i = 0; i < x.length; i++) {
     4131        var j = pv.search.index(ticks, x[i]) - 1,
     4132            bin = bins[Math.max(0, Math.min(bins.length - 1, j))];
     4133        bin.y++;
     4134        bin.push(data[i]);
     4135      }
     4136
     4137      /* Convert frequencies to probabilities. */
     4138      if (!frequency) for (var i = 0; i < bins.length; i++) {
     4139        bins[i].y /= x.length;
     4140      }
     4141
     4142      return bins;
     4143    },
     4144
     4145    /**
     4146     * Sets or gets whether this histogram operator returns frequencies or
     4147     * probabilities.
     4148     *
     4149     * @function
     4150     * @name pv.histogram.prototype.frequency
     4151     * @param {boolean} [x]
     4152     * @returns {pv.histogram} this.
     4153     */ /** @private */
     4154    frequency: function(x) {
     4155      if (arguments.length) {
     4156        frequency = Boolean(x);
     4157        return this;
     4158      }
     4159      return frequency;
     4160    }
     4161  };
     4162};
     4163
     4164/**
     4165 * @class Represents a bin returned by the {@link pv.histogram} operator. Bins
     4166 * are themselves arrays containing the data elements present in the given bin
     4167 * (prior to the accessor function being invoked to convert the data object to a
     4168 * numeric value). These bin arrays have additional attributes with meta
     4169 * information about the bin.
     4170 *
     4171 * @name pv.histogram.Bin
     4172 * @extends array
     4173 * @see pv.histogram
     4174 */
     4175
     4176/**
     4177 * The start value of the bin's range.
     4178 *
     4179 * @type number
     4180 * @name pv.histogram.Bin.prototype.x
     4181 */
     4182
     4183/**
     4184 * The magnitude value of the bin's range; end - start.
     4185 *
     4186 * @type number
     4187 * @name pv.histogram.Bin.prototype.dx
     4188 */
     4189
     4190/**
     4191 * The frequency or probability of the bin, depending on how the histogram
     4192 * operator was configured.
     4193 *
     4194 * @type number
     4195 * @name pv.histogram.Bin.prototype.y
     4196 */
     4197/**
     4198 * Returns the {@link pv.Color} for the specified color format string. Colors
     4199 * may have an associated opacity, or alpha channel. Color formats are specified
     4200 * by CSS Color Modular Level 3, using either in RGB or HSL color space. For
     4201 * example:<ul>
     4202 *
     4203 * <li>#f00 // #rgb
     4204 * <li>#ff0000 // #rrggbb
     4205 * <li>rgb(255, 0, 0)
     4206 * <li>rgb(100%, 0%, 0%)
     4207 * <li>hsl(0, 100%, 50%)
     4208 * <li>rgba(0, 0, 255, 0.5)
     4209 * <li>hsla(120, 100%, 50%, 1)
     4210 *
     4211 * </ul>The SVG 1.0 color keywords names are also supported, such as "aliceblue"
     4212 * and "yellowgreen". The "transparent" keyword is supported for fully-
     4213 * transparent black.
     4214 *
     4215 * <p>If the <tt>format</tt> argument is already an instance of <tt>Color</tt>,
     4216 * the argument is returned with no further processing.
     4217 *
     4218 * @param {string} format the color specification string, such as "#f00".
     4219 * @returns {pv.Color} the corresponding <tt>Color</tt>.
     4220 * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG color
     4221 * keywords</a>
     4222 * @see <a href="http://www.w3.org/TR/css3-color/">CSS3 color module</a>
     4223 */
     4224pv.color = function(format) {
     4225  if (format.rgb) return format.rgb();
     4226
     4227  /* Handle hsl, rgb. */
     4228  var m1 = /([a-z]+)\((.*)\)/i.exec(format);
     4229  if (m1) {
     4230    var m2 = m1[2].split(","), a = 1;
     4231    switch (m1[1]) {
     4232      case "hsla":
     4233      case "rgba": {
     4234        a = parseFloat(m2[3]);
     4235        if (!a) return pv.Color.transparent;
     4236        break;
     4237      }
     4238    }
     4239    switch (m1[1]) {
     4240      case "hsla":
     4241      case "hsl": {
     4242        var h = parseFloat(m2[0]), // degrees
     4243            s = parseFloat(m2[1]) / 100, // percentage
     4244            l = parseFloat(m2[2]) / 100; // percentage
     4245        return (new pv.Color.Hsl(h, s, l, a)).rgb();
     4246      }
     4247      case "rgba":
     4248      case "rgb": {
     4249        function parse(c) { // either integer or percentage
     4250          var f = parseFloat(c);
     4251          return (c[c.length - 1] == '%') ? Math.round(f * 2.55) : f;
     4252        }
     4253        var r = parse(m2[0]), g = parse(m2[1]), b = parse(m2[2]);
     4254        return pv.rgb(r, g, b, a);
     4255      }
     4256    }
     4257  }
     4258
     4259  /* Named colors. */
     4260  var named = pv.Color.names[format];
     4261  if (named) return named;
     4262
     4263  /* Hexadecimal colors: #rgb and #rrggbb. */
     4264  if (format.charAt(0) == "#") {
     4265    var r, g, b;
     4266    if (format.length == 4) {
     4267      r = format.charAt(1); r += r;
     4268      g = format.charAt(2); g += g;
     4269      b = format.charAt(3); b += b;
     4270    } else if (format.length == 7) {
     4271      r = format.substring(1, 3);
     4272      g = format.substring(3, 5);
     4273      b = format.substring(5, 7);
     4274    }
     4275    return pv.rgb(parseInt(r, 16), parseInt(g, 16), parseInt(b, 16), 1);
     4276  }
     4277
     4278  /* Otherwise, pass-through unsupported colors. */
     4279  return new pv.Color(format, 1);
     4280};
     4281
     4282/**
     4283 * Constructs a color with the specified color format string and opacity. This
     4284 * constructor should not be invoked directly; use {@link pv.color} instead.
     4285 *
     4286 * @class Represents an abstract (possibly translucent) color. The color is
     4287 * divided into two parts: the <tt>color</tt> attribute, an opaque color format
     4288 * string, and the <tt>opacity</tt> attribute, a float in [0, 1]. The color
     4289 * space is dependent on the implementing class; all colors support the
     4290 * {@link #rgb} method to convert to RGB color space for interpolation.
     4291 *
     4292 * <p>See also the <a href="../../api/Color.html">Color guide</a>.
     4293 *
     4294 * @param {string} color an opaque color format string, such as "#f00".
     4295 * @param {number} opacity the opacity, in [0,1].
     4296 * @see pv.color
     4297 */
     4298pv.Color = function(color, opacity) {
     4299  /**
     4300   * An opaque color format string, such as "#f00".
     4301   *
     4302   * @type string
     4303   * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG color
     4304   * keywords</a>
     4305   * @see <a href="http://www.w3.org/TR/css3-color/">CSS3 color module</a>
     4306   */
     4307  this.color = color;
     4308
     4309  /**
     4310   * The opacity, a float in [0, 1].
     4311   *
     4312   * @type number
     4313   */
     4314  this.opacity = opacity;
     4315};
     4316
     4317/**
     4318 * Returns a new color that is a brighter version of this color. The behavior of
     4319 * this method may vary slightly depending on the underlying color space.
     4320 * Although brighter and darker are inverse operations, the results of a series
     4321 * of invocations of these two methods might be inconsistent because of rounding
     4322 * errors.
     4323 *
     4324 * @param [k] {number} an optional scale factor; defaults to 1.
     4325 * @see #darker
     4326 * @returns {pv.Color} a brighter color.
     4327 */
     4328pv.Color.prototype.brighter = function(k) {
     4329  return this.rgb().brighter(k);
     4330};
     4331
     4332/**
     4333 * Returns a new color that is a brighter version of this color. The behavior of
     4334 * this method may vary slightly depending on the underlying color space.
     4335 * Although brighter and darker are inverse operations, the results of a series
     4336 * of invocations of these two methods might be inconsistent because of rounding
     4337 * errors.
     4338 *
     4339 * @param [k] {number} an optional scale factor; defaults to 1.
     4340 * @see #brighter
     4341 * @returns {pv.Color} a darker color.
     4342 */
     4343pv.Color.prototype.darker = function(k) {
     4344  return this.rgb().darker(k);
     4345};
     4346
     4347/**
     4348 * Constructs a new RGB color with the specified channel values.
     4349 *
     4350 * @param {number} r the red channel, an integer in [0,255].
     4351 * @param {number} g the green channel, an integer in [0,255].
     4352 * @param {number} b the blue channel, an integer in [0,255].
     4353 * @param {number} [a] the alpha channel, a float in [0,1].
     4354 * @returns pv.Color.Rgb
     4355 */
     4356pv.rgb = function(r, g, b, a) {
     4357  return new pv.Color.Rgb(r, g, b, (arguments.length == 4) ? a : 1);
     4358};
     4359
     4360/**
     4361 * Constructs a new RGB color with the specified channel values.
     4362 *
     4363 * @class Represents a color in RGB space.
     4364 *
     4365 * @param {number} r the red channel, an integer in [0,255].
     4366 * @param {number} g the green channel, an integer in [0,255].
     4367 * @param {number} b the blue channel, an integer in [0,255].
     4368 * @param {number} a the alpha channel, a float in [0,1].
     4369 * @extends pv.Color
     4370 */
     4371pv.Color.Rgb = function(r, g, b, a) {
     4372  pv.Color.call(this, a ? ("rgb(" + r + "," + g + "," + b + ")") : "none", a);
     4373
     4374  /**
     4375   * The red channel, an integer in [0, 255].
     4376   *
     4377   * @type number
     4378   */
     4379  this.r = r;
     4380
     4381  /**
     4382   * The green channel, an integer in [0, 255].
     4383   *
     4384   * @type number
     4385   */
     4386  this.g = g;
     4387
     4388  /**
     4389   * The blue channel, an integer in [0, 255].
     4390   *
     4391   * @type number
     4392   */
     4393  this.b = b;
     4394
     4395  /**
     4396   * The alpha channel, a float in [0, 1].
     4397   *
     4398   * @type number
     4399   */
     4400  this.a = a;
     4401};
     4402pv.Color.Rgb.prototype = pv.extend(pv.Color);
     4403
     4404/**
     4405 * Constructs a new RGB color with the same green, blue and alpha channels as
     4406 * this color, with the specified red channel.
     4407 *
     4408 * @param {number} r the red channel, an integer in [0,255].
     4409 */
     4410pv.Color.Rgb.prototype.red = function(r) {
     4411  return pv.rgb(r, this.g, this.b, this.a);
     4412};
     4413
     4414/**
     4415 * Constructs a new RGB color with the same red, blue and alpha channels as this
     4416 * color, with the specified green channel.
     4417 *
     4418 * @param {number} g the green channel, an integer in [0,255].
     4419 */
     4420pv.Color.Rgb.prototype.green = function(g) {
     4421  return pv.rgb(this.r, g, this.b, this.a);
     4422};
     4423
     4424/**
     4425 * Constructs a new RGB color with the same red, green and alpha channels as
     4426 * this color, with the specified blue channel.
     4427 *
     4428 * @param {number} b the blue channel, an integer in [0,255].
     4429 */
     4430pv.Color.Rgb.prototype.blue = function(b) {
     4431  return pv.rgb(this.r, this.g, b, this.a);
     4432};
     4433
     4434/**
     4435 * Constructs a new RGB color with the same red, green and blue channels as this
     4436 * color, with the specified alpha channel.
     4437 *
     4438 * @param {number} a the alpha channel, a float in [0,1].
     4439 */
     4440pv.Color.Rgb.prototype.alpha = function(a) {
     4441  return pv.rgb(this.r, this.g, this.b, a);
     4442};
     4443
     4444/**
     4445 * Returns the RGB color equivalent to this color. This method is abstract and
     4446 * must be implemented by subclasses.
     4447 *
     4448 * @returns {pv.Color.Rgb} an RGB color.
     4449 * @function
     4450 * @name pv.Color.prototype.rgb
     4451 */
     4452
     4453/**
     4454 * Returns this.
     4455 *
     4456 * @returns {pv.Color.Rgb} this.
     4457 */
     4458pv.Color.Rgb.prototype.rgb = function() { return this; };
     4459
     4460/**
     4461 * Returns a new color that is a brighter version of this color. This method
     4462 * applies an arbitrary scale factor to each of the three RGB components of this
     4463 * color to create a brighter version of this color. Although brighter and
     4464 * darker are inverse operations, the results of a series of invocations of
     4465 * these two methods might be inconsistent because of rounding errors.
     4466 *
     4467 * @param [k] {number} an optional scale factor; defaults to 1.
     4468 * @see #darker
     4469 * @returns {pv.Color.Rgb} a brighter color.
     4470 */
     4471pv.Color.Rgb.prototype.brighter = function(k) {
     4472  k = Math.pow(0.7, arguments.length ? k : 1);
     4473  var r = this.r, g = this.g, b = this.b, i = 30;
     4474  if (!r && !g && !b) return pv.rgb(i, i, i, this.a);
     4475  if (r && (r < i)) r = i;
     4476  if (g && (g < i)) g = i;
     4477  if (b && (b < i)) b = i;
     4478  return pv.rgb(
     4479      Math.min(255, Math.floor(r / k)),
     4480      Math.min(255, Math.floor(g / k)),
     4481      Math.min(255, Math.floor(b / k)),
     4482      this.a);
     4483};
     4484
     4485/**
     4486 * Returns a new color that is a darker version of this color. This method
     4487 * applies an arbitrary scale factor to each of the three RGB components of this
     4488 * color to create a darker version of this color. Although brighter and darker
     4489 * are inverse operations, the results of a series of invocations of these two
     4490 * methods might be inconsistent because of rounding errors.
     4491 *
     4492 * @param [k] {number} an optional scale factor; defaults to 1.
     4493 * @see #brighter
     4494 * @returns {pv.Color.Rgb} a darker color.
     4495 */
     4496pv.Color.Rgb.prototype.darker = function(k) {
     4497  k = Math.pow(0.7, arguments.length ? k : 1);
     4498  return pv.rgb(
     4499      Math.max(0, Math.floor(k * this.r)),
     4500      Math.max(0, Math.floor(k * this.g)),
     4501      Math.max(0, Math.floor(k * this.b)),
     4502      this.a);
     4503};
     4504
     4505/**
     4506 * Constructs a new HSL color with the specified values.
     4507 *
     4508 * @param {number} h the hue, an integer in [0, 360].
     4509 * @param {number} s the saturation, a float in [0, 1].
     4510 * @param {number} l the lightness, a float in [0, 1].
     4511 * @param {number} [a] the opacity, a float in [0, 1].
     4512 * @returns pv.Color.Hsl
     4513 */
     4514pv.hsl = function(h, s, l, a) {
     4515  return new pv.Color.Hsl(h, s, l,  (arguments.length == 4) ? a : 1);
     4516};
     4517
     4518/**
     4519 * Constructs a new HSL color with the specified values.
     4520 *
     4521 * @class Represents a color in HSL space.
     4522 *
     4523 * @param {number} h the hue, an integer in [0, 360].
     4524 * @param {number} s the saturation, a float in [0, 1].
     4525 * @param {number} l the lightness, a float in [0, 1].
     4526 * @param {number} a the opacity, a float in [0, 1].
     4527 * @extends pv.Color
     4528 */
     4529pv.Color.Hsl = function(h, s, l, a) {
     4530  pv.Color.call(this, "hsl(" + h + "," + (s * 100) + "%," + (l * 100) + "%)", a);
     4531
     4532  /**
     4533   * The hue, an integer in [0, 360].
     4534   *
     4535   * @type number
     4536   */
     4537  this.h = h;
     4538
     4539  /**
     4540   * The saturation, a float in [0, 1].
     4541   *
     4542   * @type number
     4543   */
     4544  this.s = s;
     4545
     4546  /**
     4547   * The lightness, a float in [0, 1].
     4548   *
     4549   * @type number
     4550   */
     4551  this.l = l;
     4552
     4553  /**
     4554   * The opacity, a float in [0, 1].
     4555   *
     4556   * @type number
     4557   */
     4558  this.a = a;
     4559};
     4560pv.Color.Hsl.prototype = pv.extend(pv.Color);
     4561
     4562/**
     4563 * Constructs a new HSL color with the same saturation, lightness and alpha as
     4564 * this color, and the specified hue.
     4565 *
     4566 * @param {number} h the hue, an integer in [0, 360].
     4567 */
     4568pv.Color.Hsl.prototype.hue = function(h) {
     4569  return pv.hsl(h, this.s, this.l, this.a);
     4570};
     4571
     4572/**
     4573 * Constructs a new HSL color with the same hue, lightness and alpha as this
     4574 * color, and the specified saturation.
     4575 *
     4576 * @param {number} s the saturation, a float in [0, 1].
     4577 */
     4578pv.Color.Hsl.prototype.saturation = function(s) {
     4579  return pv.hsl(this.h, s, this.l, this.a);
     4580};
     4581
     4582/**
     4583 * Constructs a new HSL color with the same hue, saturation and alpha as this
     4584 * color, and the specified lightness.
     4585 *
     4586 * @param {number} l the lightness, a float in [0, 1].
     4587 */
     4588pv.Color.Hsl.prototype.lightness = function(l) {
     4589  return pv.hsl(this.h, this.s, l, this.a);
     4590};
     4591
     4592/**
     4593 * Constructs a new HSL color with the same hue, saturation and lightness as
     4594 * this color, and the specified alpha.
     4595 *
     4596 * @param {number} a the opacity, a float in [0, 1].
     4597 */
     4598pv.Color.Hsl.prototype.alpha = function(a) {
     4599  return pv.hsl(this.h, this.s, this.l, a);
     4600};
     4601
     4602/**
     4603 * Returns the RGB color equivalent to this HSL color.
     4604 *
     4605 * @returns {pv.Color.Rgb} an RGB color.
     4606 */
     4607pv.Color.Hsl.prototype.rgb = function() {
     4608  var h = this.h, s = this.s, l = this.l;
     4609
     4610  /* Some simple corrections for h, s and l. */
     4611  h = h % 360; if (h < 0) h += 360;
     4612  s = Math.max(0, Math.min(s, 1));
     4613  l = Math.max(0, Math.min(l, 1));
     4614
     4615  /* From FvD 13.37, CSS Color Module Level 3 */
     4616  var m2 = (l <= .5) ? (l * (1 + s)) : (l + s - l * s);
     4617  var m1 = 2 * l - m2;
     4618  function v(h) {
     4619    if (h > 360) h -= 360;
     4620    else if (h < 0) h += 360;
     4621    if (h < 60) return m1 + (m2 - m1) * h / 60;
     4622    if (h < 180) return m2;
     4623    if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
     4624    return m1;
     4625  }
     4626  function vv(h) {
     4627    return Math.round(v(h) * 255);
     4628  }
     4629
     4630  return pv.rgb(vv(h + 120), vv(h), vv(h - 120), this.a);
     4631};
     4632
     4633/**
     4634 * @private SVG color keywords, per CSS Color Module Level 3.
     4635 *
     4636 * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG color
     4637 * keywords</a>
     4638 */
     4639pv.Color.names = {
     4640  aliceblue: "#f0f8ff",
     4641  antiquewhite: "#faebd7",
     4642  aqua: "#00ffff",
     4643  aquamarine: "#7fffd4",
     4644  azure: "#f0ffff",
     4645  beige: "#f5f5dc",
     4646  bisque: "#ffe4c4",
     4647  black: "#000000",
     4648  blanchedalmond: "#ffebcd",
     4649  blue: "#0000ff",
     4650  blueviolet: "#8a2be2",
     4651  brown: "#a52a2a",
     4652  burlywood: "#deb887",
     4653  cadetblue: "#5f9ea0",
     4654  chartreuse: "#7fff00",
     4655  chocolate: "#d2691e",
     4656  coral: "#ff7f50",
     4657  cornflowerblue: "#6495ed",
     4658  cornsilk: "#fff8dc",
     4659  crimson: "#dc143c",
     4660  cyan: "#00ffff",
     4661  darkblue: "#00008b",
     4662  darkcyan: "#008b8b",
     4663  darkgoldenrod: "#b8860b",
     4664  darkgray: "#a9a9a9",
     4665  darkgreen: "#006400",
     4666  darkgrey: "#a9a9a9",
     4667  darkkhaki: "#bdb76b",
     4668  darkmagenta: "#8b008b",
     4669  darkolivegreen: "#556b2f",
     4670  darkorange: "#ff8c00",
     4671  darkorchid: "#9932cc",
     4672  darkred: "#8b0000",
     4673  darksalmon: "#e9967a",
     4674  darkseagreen: "#8fbc8f",
     4675  darkslateblue: "#483d8b",
     4676  darkslategray: "#2f4f4f",
     4677  darkslategrey: "#2f4f4f",
     4678  darkturquoise: "#00ced1",
     4679  darkviolet: "#9400d3",
     4680  deeppink: "#ff1493",
     4681  deepskyblue: "#00bfff",
     4682  dimgray: "#696969",
     4683  dimgrey: "#696969",
     4684  dodgerblue: "#1e90ff",
     4685  firebrick: "#b22222",
     4686  floralwhite: "#fffaf0",
     4687  forestgreen: "#228b22",
     4688  fuchsia: "#ff00ff",
     4689  gainsboro: "#dcdcdc",
     4690  ghostwhite: "#f8f8ff",
     4691  gold: "#ffd700",
     4692  goldenrod: "#daa520",
     4693  gray: "#808080",
     4694  green: "#008000",
     4695  greenyellow: "#adff2f",
     4696  grey: "#808080",
     4697  honeydew: "#f0fff0",
     4698  hotpink: "#ff69b4",
     4699  indianred: "#cd5c5c",
     4700  indigo: "#4b0082",
     4701  ivory: "#fffff0",
     4702  khaki: "#f0e68c",
     4703  lavender: "#e6e6fa",
     4704  lavenderblush: "#fff0f5",
     4705  lawngreen: "#7cfc00",
     4706  lemonchiffon: "#fffacd",
     4707  lightblue: "#add8e6",
     4708  lightcoral: "#f08080",
     4709  lightcyan: "#e0ffff",
     4710  lightgoldenrodyellow: "#fafad2",
     4711  lightgray: "#d3d3d3",
     4712  lightgreen: "#90ee90",
     4713  lightgrey: "#d3d3d3",
     4714  lightpink: "#ffb6c1",
     4715  lightsalmon: "#ffa07a",
     4716  lightseagreen: "#20b2aa",
     4717  lightskyblue: "#87cefa",
     4718  lightslategray: "#778899",
     4719  lightslategrey: "#778899",
     4720  lightsteelblue: "#b0c4de",
     4721  lightyellow: "#ffffe0",
     4722  lime: "#00ff00",
     4723  limegreen: "#32cd32",
     4724  linen: "#faf0e6",
     4725  magenta: "#ff00ff",
     4726  maroon: "#800000",
     4727  mediumaquamarine: "#66cdaa",
     4728  mediumblue: "#0000cd",
     4729  mediumorchid: "#ba55d3",
     4730  mediumpurple: "#9370db",
     4731  mediumseagreen: "#3cb371",
     4732  mediumslateblue: "#7b68ee",
     4733  mediumspringgreen: "#00fa9a",
     4734  mediumturquoise: "#48d1cc",
     4735  mediumvioletred: "#c71585",
     4736  midnightblue: "#191970",
     4737  mintcream: "#f5fffa",
     4738  mistyrose: "#ffe4e1",
     4739  moccasin: "#ffe4b5",
     4740  navajowhite: "#ffdead",
     4741  navy: "#000080",
     4742  oldlace: "#fdf5e6",
     4743  olive: "#808000",
     4744  olivedrab: "#6b8e23",
     4745  orange: "#ffa500",
     4746  orangered: "#ff4500",
     4747  orchid: "#da70d6",
     4748  palegoldenrod: "#eee8aa",
     4749  palegreen: "#98fb98",
     4750  paleturquoise: "#afeeee",
     4751  palevioletred: "#db7093",
     4752  papayawhip: "#ffefd5",
     4753  peachpuff: "#ffdab9",
     4754  peru: "#cd853f",
     4755  pink: "#ffc0cb",
     4756  plum: "#dda0dd",
     4757  powderblue: "#b0e0e6",
     4758  purple: "#800080",
     4759  red: "#ff0000",
     4760  rosybrown: "#bc8f8f",
     4761  royalblue: "#4169e1",
     4762  saddlebrown: "#8b4513",
     4763  salmon: "#fa8072",
     4764  sandybrown: "#f4a460",
     4765  seagreen: "#2e8b57",
     4766  seashell: "#fff5ee",
     4767  sienna: "#a0522d",
     4768  silver: "#c0c0c0",
     4769  skyblue: "#87ceeb",
     4770  slateblue: "#6a5acd",
     4771  slategray: "#708090",
     4772  slategrey: "#708090",
     4773  snow: "#fffafa",
     4774  springgreen: "#00ff7f",
     4775  steelblue: "#4682b4",
     4776  tan: "#d2b48c",
     4777  teal: "#008080",
     4778  thistle: "#d8bfd8",
     4779  tomato: "#ff6347",
     4780  turquoise: "#40e0d0",
     4781  violet: "#ee82ee",
     4782  wheat: "#f5deb3",
     4783  white: "#ffffff",
     4784  whitesmoke: "#f5f5f5",
     4785  yellow: "#ffff00",
     4786  yellowgreen: "#9acd32",
     4787  transparent: pv.Color.transparent = pv.rgb(0, 0, 0, 0)
     4788};
     4789
     4790/* Initialized named colors. */
     4791(function() {
     4792  var names = pv.Color.names;
     4793  for (var name in names) names[name] = pv.color(names[name]);
     4794})();
     4795/**
     4796 * Returns a new categorical color encoding using the specified colors.  The
     4797 * arguments to this method are an array of colors; see {@link pv.color}. For
     4798 * example, to create a categorical color encoding using the <tt>species</tt>
     4799 * attribute:
     4800 *
     4801 * <pre>pv.colors("red", "green", "blue").by(function(d) d.species)</pre>
     4802 *
     4803 * The result of this expression can be used as a fill- or stroke-style
     4804 * property. This assumes that the data's <tt>species</tt> attribute is a
     4805 * string.
     4806 *
     4807 * @param {string} colors... categorical colors.
     4808 * @see pv.Scale.ordinal
     4809 * @returns {pv.Scale.ordinal} an ordinal color scale.
     4810 */
     4811pv.colors = function() {
     4812  var scale = pv.Scale.ordinal();
     4813  scale.range.apply(scale, arguments);
     4814  return scale;
     4815};
     4816
     4817/**
     4818 * A collection of standard color palettes for categorical encoding.
     4819 *
     4820 * @namespace A collection of standard color palettes for categorical encoding.
     4821 */
     4822pv.Colors = {};
     4823
     4824/**
     4825 * Returns a new 10-color scheme. The arguments to this constructor are
     4826 * optional, and equivalent to calling {@link pv.Scale.OrdinalScale#domain}. The
     4827 * following colors are used:
     4828 *
     4829 * <div style="background:#1f77b4;">#1f77b4</div>
     4830 * <div style="background:#ff7f0e;">#ff7f0e</div>
     4831 * <div style="background:#2ca02c;">#2ca02c</div>
     4832 * <div style="background:#d62728;">#d62728</div>
     4833 * <div style="background:#9467bd;">#9467bd</div>
     4834 * <div style="background:#8c564b;">#8c564b</div>
     4835 * <div style="background:#e377c2;">#e377c2</div>
     4836 * <div style="background:#7f7f7f;">#7f7f7f</div>
     4837 * <div style="background:#bcbd22;">#bcbd22</div>
     4838 * <div style="background:#17becf;">#17becf</div>
     4839 *
     4840 * @param {number...} domain... domain values.
     4841 * @returns {pv.Scale.ordinal} a new ordinal color scale.
     4842 * @see pv.color
     4843 */
     4844pv.Colors.category10 = function() {
     4845  var scale = pv.colors(
     4846      "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd",
     4847      "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf");
     4848  scale.domain.apply(scale, arguments);
     4849  return scale;
     4850};
     4851
     4852/**
     4853 * Returns a new 20-color scheme. The arguments to this constructor are
     4854 * optional, and equivalent to calling {@link pv.Scale.OrdinalScale#domain}. The
     4855 * following colors are used:
     4856 *
     4857 * <div style="background:#1f77b4;">#1f77b4</div>
     4858 * <div style="background:#aec7e8;">#aec7e8</div>
     4859 * <div style="background:#ff7f0e;">#ff7f0e</div>
     4860 * <div style="background:#ffbb78;">#ffbb78</div>
     4861 * <div style="background:#2ca02c;">#2ca02c</div>
     4862 * <div style="background:#98df8a;">#98df8a</div>
     4863 * <div style="background:#d62728;">#d62728</div>
     4864 * <div style="background:#ff9896;">#ff9896</div>
     4865 * <div style="background:#9467bd;">#9467bd</div>
     4866 * <div style="background:#c5b0d5;">#c5b0d5</div>
     4867 * <div style="background:#8c564b;">#8c564b</div>
     4868 * <div style="background:#c49c94;">#c49c94</div>
     4869 * <div style="background:#e377c2;">#e377c2</div>
     4870 * <div style="background:#f7b6d2;">#f7b6d2</div>
     4871 * <div style="background:#7f7f7f;">#7f7f7f</div>
     4872 * <div style="background:#c7c7c7;">#c7c7c7</div>
     4873 * <div style="background:#bcbd22;">#bcbd22</div>
     4874 * <div style="background:#dbdb8d;">#dbdb8d</div>
     4875 * <div style="background:#17becf;">#17becf</div>
     4876 * <div style="background:#9edae5;">#9edae5</div>
     4877 *
     4878 * @param {number...} domain... domain values.
     4879 * @returns {pv.Scale.ordinal} a new ordinal color scale.
     4880 * @see pv.color
     4881*/
     4882pv.Colors.category20 = function() {
     4883  var scale = pv.colors(
     4884      "#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", "#2ca02c",
     4885      "#98df8a", "#d62728", "#ff9896", "#9467bd", "#c5b0d5",
     4886      "#8c564b", "#c49c94", "#e377c2", "#f7b6d2", "#7f7f7f",
     4887      "#c7c7c7", "#bcbd22", "#dbdb8d", "#17becf", "#9edae5");
     4888  scale.domain.apply(scale, arguments);
     4889  return scale;
     4890};
     4891
     4892/**
     4893 * Returns a new alternative 19-color scheme. The arguments to this constructor
     4894 * are optional, and equivalent to calling
     4895 * {@link pv.Scale.OrdinalScale#domain}. The following colors are used:
     4896 *
     4897 * <div style="background:#9c9ede;">#9c9ede</div>
     4898 * <div style="background:#7375b5;">#7375b5</div>
     4899 * <div style="background:#4a5584;">#4a5584</div>
     4900 * <div style="background:#cedb9c;">#cedb9c</div>
     4901 * <div style="background:#b5cf6b;">#b5cf6b</div>
     4902 * <div style="background:#8ca252;">#8ca252</div>
     4903 * <div style="background:#637939;">#637939</div>
     4904 * <div style="background:#e7cb94;">#e7cb94</div>
     4905 * <div style="background:#e7ba52;">#e7ba52</div>
     4906 * <div style="background:#bd9e39;">#bd9e39</div>
     4907 * <div style="background:#8c6d31;">#8c6d31</div>
     4908 * <div style="background:#e7969c;">#e7969c</div>
     4909 * <div style="background:#d6616b;">#d6616b</div>
     4910 * <div style="background:#ad494a;">#ad494a</div>
     4911 * <div style="background:#843c39;">#843c39</div>
     4912 * <div style="background:#de9ed6;">#de9ed6</div>
     4913 * <div style="background:#ce6dbd;">#ce6dbd</div>
     4914 * <div style="background:#a55194;">#a55194</div>
     4915 * <div style="background:#7b4173;">#7b4173</div>
     4916 *
     4917 * @param {number...} domain... domain values.
     4918 * @returns {pv.Scale.ordinal} a new ordinal color scale.
     4919 * @see pv.color
     4920 */
     4921pv.Colors.category19 = function() {
     4922  var scale = pv.colors(
     4923      "#9c9ede", "#7375b5", "#4a5584", "#cedb9c", "#b5cf6b",
     4924      "#8ca252", "#637939", "#e7cb94", "#e7ba52", "#bd9e39",
     4925      "#8c6d31", "#e7969c", "#d6616b", "#ad494a", "#843c39",
     4926      "#de9ed6", "#ce6dbd", "#a55194", "#7b4173");
     4927  scale.domain.apply(scale, arguments);
     4928  return scale;
     4929};
     4930/**
     4931 * Returns a linear color ramp from the specified <tt>start</tt> color to the
     4932 * specified <tt>end</tt> color. The color arguments may be specified either as
     4933 * <tt>string</tt>s or as {@link pv.Color}s. This is equivalent to:
     4934 *
     4935 * <pre>    pv.Scale.linear().domain(0, 1).range(...)</pre>
     4936 *
     4937 * @param {string} start the start color; may be a <tt>pv.Color</tt>.
     4938 * @param {string} end the end color; may be a <tt>pv.Color</tt>.
     4939 * @returns {Function} a color ramp from <tt>start</tt> to <tt>end</tt>.
     4940 * @see pv.Scale.linear
     4941 */
     4942pv.ramp = function(start, end) {
     4943  var scale = pv.Scale.linear();
     4944  scale.range.apply(scale, arguments);
     4945  return scale;
     4946};
     4947/**
     4948 * @private
     4949 * @namespace
     4950 */
     4951pv.Scene = pv.SvgScene = {
     4952  /* Various namespaces. */
     4953  svg: "http://www.w3.org/2000/svg",
     4954  xmlns: "http://www.w3.org/2000/xmlns",
     4955  xlink: "http://www.w3.org/1999/xlink",
     4956  xhtml: "http://www.w3.org/1999/xhtml",
     4957
     4958  /** The pre-multipled scale, based on any enclosing transforms. */
     4959  scale: 1,
     4960
     4961  /** The set of supported events. */
     4962  events: [
     4963    "DOMMouseScroll", // for Firefox
     4964    "mousewheel",
     4965    "mousedown",
     4966    "mouseup",
     4967    "mouseover",
     4968    "mouseout",
     4969    "mousemove",
     4970    "click",
     4971    "dblclick"
     4972  ],
     4973
     4974  /** Implicit values for SVG and CSS properties. */
     4975  implicit: {
     4976    svg: {
     4977      "shape-rendering": "auto",
     4978      "pointer-events": "painted",
     4979      "x": 0,
     4980      "y": 0,
     4981      "dy": 0,
     4982      "text-anchor": "start",
     4983      "transform": "translate(0,0)",
     4984      "fill": "none",
     4985      "fill-opacity": 1,
     4986      "stroke": "none",
     4987      "stroke-opacity": 1,
     4988      "stroke-width": 1.5,
     4989      "stroke-linejoin": "miter"
     4990    },
     4991    css: {
     4992      "font": "10px sans-serif"
     4993    }
     4994  }
     4995};
     4996
     4997/**
     4998 * Updates the display for the specified array of scene nodes.
     4999 *
     5000 * @param scenes {array} an array of scene nodes.
     5001 */
     5002pv.SvgScene.updateAll = function(scenes) {
     5003  if (scenes.length
     5004      && scenes[0].reverse
     5005      && (scenes.type != "line")
     5006      && (scenes.type != "area")) {
     5007    var reversed = pv.extend(scenes);
     5008    for (var i = 0, j = scenes.length - 1; j >= 0; i++, j--) {
     5009      reversed[i] = scenes[j];
     5010    }
     5011    scenes = reversed;
     5012  }
     5013  this.removeSiblings(this[scenes.type](scenes));
     5014};
     5015
     5016/**
     5017 * Creates a new SVG element of the specified type.
     5018 *
     5019 * @param type {string} an SVG element type, such as "rect".
     5020 * @returns a new SVG element.
     5021 */
     5022pv.SvgScene.create = function(type) {
     5023  return document.createElementNS(this.svg, type);
     5024};
     5025
     5026/**
     5027 * Expects the element <i>e</i> to be the specified type. If the element does
     5028 * not exist, a new one is created. If the element does exist but is the wrong
     5029 * type, it is replaced with the specified element.
     5030 *
     5031 * @param e the current SVG element.
     5032 * @param type {string} an SVG element type, such as "rect".
     5033 * @param attributes an optional attribute map.
     5034 * @param style an optional style map.
     5035 * @returns a new SVG element.
     5036 */
     5037pv.SvgScene.expect = function(e, type, attributes, style) {
     5038  if (e) {
     5039    if (e.tagName == "a") e = e.firstChild;
     5040    if (e.tagName != type) {
     5041      var n = this.create(type);
     5042      e.parentNode.replaceChild(n, e);
     5043      e = n;
     5044    }
     5045  } else {
     5046    e = this.create(type);
     5047  }
     5048  for (var name in attributes) {
     5049    var value = attributes[name];
     5050    if (value == this.implicit.svg[name]) value = null;
     5051    if (value == null) e.removeAttribute(name);
     5052    else e.setAttribute(name, value);
     5053  }
     5054  for (var name in style) {
     5055    var value = style[name];
     5056    if (value == this.implicit.css[name]) value = null;
     5057    if (value == null) e.style.removeProperty(name);
     5058    else e.style[name] = value;
     5059  }
     5060  return e;
     5061};
     5062
     5063/** TODO */
     5064pv.SvgScene.append = function(e, scenes, index) {
     5065  e.$scene = {scenes:scenes, index:index};
     5066  e = this.title(e, scenes[index]);
     5067  if (!e.parentNode) scenes.$g.appendChild(e);
     5068  return e.nextSibling;
     5069};
     5070
     5071/**
     5072 * Applies a title tooltip to the specified element <tt>e</tt>, using the
     5073 * <tt>title</tt> property of the specified scene node <tt>s</tt>. Note that
     5074 * this implementation does not create an SVG <tt>title</tt> element as a child
     5075 * of <tt>e</tt>; although this is the recommended standard, it is only
     5076 * supported in Opera. Instead, an anchor element is created around the element
     5077 * <tt>e</tt>, and the <tt>xlink:title</tt> attribute is set accordingly.
     5078 *
     5079 * @param e an SVG element.
     5080 * @param s a scene node.
     5081 */
     5082pv.SvgScene.title = function(e, s) {
     5083  var a = e.parentNode;
     5084  if (a && (a.tagName != "a")) a = null;
     5085  if (s.title) {
     5086    if (!a) {
     5087      a = this.create("a");
     5088      if (e.parentNode) e.parentNode.replaceChild(a, e);
     5089      a.appendChild(e);
     5090    }
     5091    a.setAttributeNS(this.xlink, "title", s.title);
     5092    return a;
     5093  }
     5094  if (a) a.parentNode.replaceChild(e, a);
     5095  return e;
     5096};
     5097
     5098/** TODO */
     5099pv.SvgScene.dispatch = pv.listener(function(e) {
     5100  var t = e.target.$scene;
     5101  if (t) {
     5102    var type = e.type;
     5103
     5104    /* Fixes for mousewheel support on Firefox & Opera. */
     5105    switch (type) {
     5106      case "DOMMouseScroll": {
     5107        type = "mousewheel";
     5108        e.wheel = -480 * e.detail;
     5109        break;
     5110      }
     5111      case "mousewheel": {
     5112        e.wheel = (window.opera ? 12 : 1) * e.wheelDelta;
     5113        break;
     5114      }
     5115    }
     5116
     5117    if (pv.Mark.dispatch(type, t.scenes, t.index)) e.preventDefault();
     5118  }
     5119});
     5120
     5121/** @private Remove siblings following element <i>e</i>. */
     5122pv.SvgScene.removeSiblings = function(e) {
     5123  while (e) {
     5124    var n = e.nextSibling;
     5125    e.parentNode.removeChild(e);
     5126    e = n;
     5127  }
     5128};
     5129
     5130/** @private Do nothing when rendering undefined mark types. */
     5131pv.SvgScene.undefined = function() {};
     5132/**
     5133 * @private Converts the specified b-spline curve segment to a bezier curve
     5134 * compatible with SVG "C".
     5135 *
     5136 * @param p0 the first control point.
     5137 * @param p1 the second control point.
     5138 * @param p2 the third control point.
     5139 * @param p3 the fourth control point.
     5140 */
     5141pv.SvgScene.pathBasis = (function() {
     5142
     5143  /**
     5144   * Matrix to transform basis (b-spline) control points to bezier control
     5145   * points. Derived from FvD 11.2.8.
     5146   */
     5147  var basis = [
     5148    [ 1/6, 2/3, 1/6,   0 ],
     5149    [   0, 2/3, 1/3,   0 ],
     5150    [   0, 1/3, 2/3,   0 ],
     5151    [   0, 1/6, 2/3, 1/6 ]
     5152  ];
     5153
     5154  /**
     5155   * Returns the point that is the weighted sum of the specified control points,
     5156   * using the specified weights. This method requires that there are four
     5157   * weights and four control points.
     5158   */
     5159  function weight(w, p0, p1, p2, p3) {
     5160    return {
     5161      x: w[0] * p0.left + w[1] * p1.left + w[2] * p2.left + w[3] * p3.left,
     5162      y: w[0] * p0.top  + w[1] * p1.top  + w[2] * p2.top  + w[3] * p3.top
     5163    };
     5164  }
     5165
     5166  var convert = function(p0, p1, p2, p3) {
     5167    var b1 = weight(basis[1], p0, p1, p2, p3),
     5168        b2 = weight(basis[2], p0, p1, p2, p3),
     5169        b3 = weight(basis[3], p0, p1, p2, p3);
     5170    return "C" + b1.x + "," + b1.y
     5171         + "," + b2.x + "," + b2.y
     5172         + "," + b3.x + "," + b3.y;
     5173  };
     5174
     5175  convert.segment = function(p0, p1, p2, p3) {
     5176    var b0 = weight(basis[0], p0, p1, p2, p3),
     5177        b1 = weight(basis[1], p0, p1, p2, p3),
     5178        b2 = weight(basis[2], p0, p1, p2, p3),
     5179        b3 = weight(basis[3], p0, p1, p2, p3);
     5180    return "M" + b0.x + "," + b0.y
     5181         + "C" + b1.x + "," + b1.y
     5182         + "," + b2.x + "," + b2.y
     5183         + "," + b3.x + "," + b3.y;
     5184  };
     5185
     5186  return convert;
     5187})();
     5188
     5189/**
     5190 * @private Interpolates the given points using the basis spline interpolation.
     5191 * Returns an SVG path without the leading M instruction to allow path
     5192 * appending.
     5193 *
     5194 * @param points the array of points.
     5195 */
     5196pv.SvgScene.curveBasis = function(points) {
     5197  if (points.length <= 2) return "";
     5198  var path = "",
     5199      p0 = points[0],
     5200      p1 = p0,
     5201      p2 = p0,
     5202      p3 = points[1];
     5203  path += this.pathBasis(p0, p1, p2, p3);
     5204  for (var i = 2; i < points.length; i++) {
     5205    p0 = p1;
     5206    p1 = p2;
     5207    p2 = p3;
     5208    p3 = points[i];
     5209    path += this.pathBasis(p0, p1, p2, p3);
     5210  }
     5211  /* Cycle through to get the last point. */
     5212  path += this.pathBasis(p1, p2, p3, p3);
     5213  path += this.pathBasis(p2, p3, p3, p3);
     5214  return path;
     5215};
     5216
     5217/**
     5218 * @private Interpolates the given points using the basis spline interpolation.
     5219 * If points.length == tangents.length then a regular Hermite interpolation is
     5220 * performed, if points.length == tangents.length + 2 then the first and last
     5221 * segments are filled in with cubic bazier segments.  Returns an array of path
     5222 * strings.
     5223 *
     5224 * @param points the array of points.
     5225 */
     5226pv.SvgScene.curveBasisSegments = function(points) {
     5227  if (points.length <= 2) return "";
     5228  var paths = [],
     5229      p0 = points[0],
     5230      p1 = p0,
     5231      p2 = p0,
     5232      p3 = points[1],
     5233      firstPath = this.pathBasis.segment(p0, p1, p2, p3);
     5234
     5235  p0 = p1;
     5236  p1 = p2;
     5237  p2 = p3;
     5238  p3 = points[2];
     5239  paths.push(firstPath + this.pathBasis(p0, p1, p2, p3)); // merge first & second path
     5240  for (var i = 3; i < points.length; i++) {
     5241    p0 = p1;
     5242    p1 = p2;
     5243    p2 = p3;
     5244    p3 = points[i];
     5245    paths.push(this.pathBasis.segment(p0, p1, p2, p3));
     5246  }
     5247
     5248  // merge last & second-to-last path
     5249  paths.push(this.pathBasis.segment(p1, p2, p3, p3) + this.pathBasis(p2, p3, p3, p3));
     5250  return paths;
     5251};
     5252
     5253/**
     5254 * @private Interpolates the given points with respective tangents using the cubic
     5255 * Hermite spline interpolation. If points.length == tangents.length then a regular
     5256 * Hermite interpolation is performed, if points.length == tangents.length + 2 then
     5257 * the first and last segments are filled in with cubic bazier segments.
     5258 * Returns an SVG path without the leading M instruction to allow path appending.
     5259 *
     5260 * @param points the array of points.
     5261 * @param tangents the array of tangent vectors.
     5262 */
     5263pv.SvgScene.curveHermite = function(points, tangents) {
     5264  if (tangents.length < 1
     5265      || (points.length != tangents.length
     5266      && points.length != tangents.length + 2)) return "";
     5267  var quad = points.length != tangents.length,
     5268      path = "",
     5269      p0 = points[0],
     5270      p = points[1],
     5271      t0 = tangents[0],
     5272      t = t0,
     5273      pi = 1;
     5274
     5275  if (quad) {
     5276    path += "Q" + (p.left - t0.x * 2 / 3) + ","  + (p.top - t0.y * 2 / 3)
     5277        + "," + p.left + "," + p.top;
     5278    p0 = points[1];
     5279    pi = 2;
     5280  }
     5281
     5282  if (tangents.length > 1) {
     5283    t = tangents[1];
     5284    p = points[pi];
     5285    pi++;
     5286    path += "C" + (p0.left + t0.x) + "," + (p0.top + t0.y)
     5287        + "," + (p.left - t.x) + "," + (p.top - t.y)
     5288        + "," + p.left + "," + p.top;
     5289    for (var i = 2; i < tangents.length; i++, pi++) {
     5290      p = points[pi];
     5291      t = tangents[i];
     5292      path += "S" + (p.left - t.x) + "," + (p.top - t.y)
     5293          + "," + p.left + "," + p.top;
     5294    }
     5295  }
     5296
     5297  if (quad) {
     5298    var lp = points[pi];
     5299    path += "Q" + (p.left + t.x * 2 / 3) + ","  + (p.top + t.y * 2 / 3) + ","
     5300        + lp.left + "," + lp.top;
     5301  }
     5302
     5303  return path;
     5304};
     5305
     5306/**
     5307 * @private Interpolates the given points with respective tangents using the
     5308 * cubic Hermite spline interpolation. Returns an array of path strings.
     5309 *
     5310 * @param points the array of points.
     5311 * @param tangents the array of tangent vectors.
     5312 */
     5313pv.SvgScene.curveHermiteSegments = function(points, tangents) {
     5314  if (tangents.length < 1
     5315      || (points.length != tangents.length
     5316      && points.length != tangents.length + 2)) return [];
     5317  var quad = points.length != tangents.length,
     5318      paths = [],
     5319      p0 = points[0],
     5320      p = p0,
     5321      t0 = tangents[0],
     5322      t = t0,
     5323      pi = 1;
     5324
     5325  if (quad) {
     5326    p = points[1];
     5327    paths.push("M" + p0.left + "," + p0.top
     5328        + "Q" + (p.left - t.x * 2 / 3) + "," + (p.top - t.y * 2 / 3)
     5329        + "," + p.left + "," + p.top);
     5330    pi = 2;
     5331  }
     5332
     5333  for (var i = 1; i < tangents.length; i++, pi++) {
     5334    p0 = p;
     5335    t0 = t;
     5336    p = points[pi];
     5337    t = tangents[i];
     5338    paths.push("M" + p0.left + "," + p0.top
     5339        + "C" + (p0.left + t0.x) + "," + (p0.top + t0.y)
     5340        + "," + (p.left - t.x) + "," + (p.top - t.y)
     5341        + "," + p.left + "," + p.top);
     5342  }
     5343
     5344  if (quad) {
     5345    var lp = points[pi];
     5346    paths.push("M" + p.left + "," + p.top
     5347        + "Q" + (p.left + t.x * 2 / 3) + ","  + (p.top + t.y * 2 / 3) + ","
     5348        + lp.left + "," + lp.top);
     5349  }
     5350
     5351  return paths;
     5352};
     5353
     5354/**
     5355 * @private Computes the tangents for the given points needed for cardinal
     5356 * spline interpolation. Returns an array of tangent vectors. Note: that for n
     5357 * points only the n-2 well defined tangents are returned.
     5358 *
     5359 * @param points the array of points.
     5360 * @param tension the tension of hte cardinal spline.
     5361 */
     5362pv.SvgScene.cardinalTangents = function(points, tension) {
     5363  var tangents = [],
     5364      a = (1 - tension) / 2,
     5365      p0 = points[0],
     5366      p1 = points[1],
     5367      p2 = points[2];
     5368
     5369  for (var i = 3; i < points.length; i++) {
     5370    tangents.push({x: a * (p2.left - p0.left), y: a * (p2.top - p0.top)});
     5371    p0 = p1;
     5372    p1 = p2;
     5373    p2 = points[i];
     5374  }
     5375
     5376  tangents.push({x: a * (p2.left - p0.left), y: a * (p2.top - p0.top)});
     5377  return tangents;
     5378};
     5379
     5380/**
     5381 * @private Interpolates the given points using cardinal spline interpolation.
     5382 * Returns an SVG path without the leading M instruction to allow path
     5383 * appending.
     5384 *
     5385 * @param points the array of points.
     5386 * @param tension the tension of hte cardinal spline.
     5387 */
     5388pv.SvgScene.curveCardinal = function(points, tension) {
     5389  if (points.length <= 2) return "";
     5390  return this.curveHermite(points, this.cardinalTangents(points, tension));
     5391};
     5392
     5393/**
     5394 * @private Interpolates the given points using cardinal spline interpolation.
     5395 * Returns an array of path strings.
     5396 *
     5397 * @param points the array of points.
     5398 * @param tension the tension of hte cardinal spline.
     5399 */
     5400pv.SvgScene.curveCardinalSegments = function(points, tension) {
     5401  if (points.length <= 2) return "";
     5402  return this.curveHermiteSegments(points, this.cardinalTangents(points, tension));
     5403};
     5404
     5405/**
     5406 * @private Interpolates the given points using Fritsch-Carlson Monotone cubic
     5407 * Hermite interpolation. Returns an array of tangent vectors.
     5408 *
     5409 * @param points the array of points.
     5410 */
     5411pv.SvgScene.monotoneTangents = function(points) {
     5412  var tangents = [],
     5413      d = [],
     5414      m = [],
     5415      dx = [],
     5416      k = 0;
     5417
     5418  /* Compute the slopes of the secant lines between successive points. */
     5419  for (k = 0; k < points.length-1; k++) {
     5420    d[k] = (points[k+1].top - points[k].top)/(points[k+1].left - points[k].left);
     5421  }
     5422
     5423  /* Initialize the tangents at every point as the average of the secants. */
     5424  m[0] = d[0];
     5425  dx[0] = points[1].left - points[0].left;
     5426  for (k = 1; k < points.length - 1; k++) {
     5427    m[k] = (d[k-1]+d[k])/2;
     5428    dx[k] = (points[k+1].left - points[k-1].left)/2;
     5429  }
     5430  m[k] = d[k-1];
     5431  dx[k] = (points[k].left - points[k-1].left);
     5432
     5433  /* Step 3. Very important, step 3. Yep. Wouldn't miss it. */
     5434  for (k = 0; k < points.length - 1; k++) {
     5435    if (d[k] == 0) {
     5436      m[ k ] = 0;
     5437      m[k+1] = 0;
     5438    }
     5439  }
     5440
     5441  /* Step 4 + 5. Out of 5 or more steps. */
     5442  for (k = 0; k < points.length - 1; k++) {
     5443    if ((Math.abs(m[k]) < 1e-5) || (Math.abs(m[k+1]) < 1e-5)) continue;
     5444    var ak = m[k] / d[k],
     5445        bk = m[k + 1] / d[k],
     5446        s = ak * ak + bk * bk; // monotone constant (?)
     5447    if (s > 9) {
     5448      var tk = 3 / Math.sqrt(s);
     5449      m[k] = tk * ak * d[k];
     5450      m[k + 1] = tk * bk * d[k];
     5451    }
     5452  }
     5453
     5454  var len;
     5455  for (var i = 0; i < points.length; i++) {
     5456    len = 1 + m[i] * m[i]; // pv.vector(1, m[i]).norm().times(dx[i]/3)
     5457    tangents.push({x: dx[i] / 3 / len, y: m[i] * dx[i] / 3 / len});
     5458  }
     5459
     5460  return tangents;
     5461};
     5462
     5463/**
     5464 * @private Interpolates the given points using Fritsch-Carlson Monotone cubic
     5465 * Hermite interpolation. Returns an SVG path without the leading M instruction
     5466 * to allow path appending.
     5467 *
     5468 * @param points the array of points.
     5469 */
     5470pv.SvgScene.curveMonotone = function(points) {
     5471  if (points.length <= 2) return "";
     5472  return this.curveHermite(points, this.monotoneTangents(points));
     5473}
     5474
     5475/**
     5476 * @private Interpolates the given points using Fritsch-Carlson Monotone cubic
     5477 * Hermite interpolation.
     5478 * Returns an array of path strings.
     5479 *
     5480 * @param points the array of points.
     5481 */
     5482pv.SvgScene.curveMonotoneSegments = function(points) {
     5483  if (points.length <= 2) return "";
     5484  return this.curveHermiteSegments(points, this.monotoneTangents(points));
     5485};
     5486pv.SvgScene.area = function(scenes) {
     5487  var e = scenes.$g.firstChild;
     5488  if (!scenes.length) return e;
     5489  var s = scenes[0];
     5490
     5491  /* segmented */
     5492  if (s.segmented) return this.areaSegment(scenes);
     5493
     5494  /* visible */
     5495  if (!s.visible) return e;
     5496  var fill = s.fillStyle, stroke = s.strokeStyle;
     5497  if (!fill.opacity && !stroke.opacity) return e;
     5498
     5499  /** @private Computes the straight path for the range [i, j]. */
     5500  function path(i, j) {
     5501    var p1 = [], p2 = [];
     5502    for (var k = j; i <= k; i++, j--) {
     5503      var si = scenes[i],
     5504          sj = scenes[j],
     5505          pi = si.left + "," + si.top,
     5506          pj = (sj.left + sj.width) + "," + (sj.top + sj.height);
     5507
     5508      /* interpolate */
     5509      if (i < k) {
     5510        var sk = scenes[i + 1], sl = scenes[j - 1];
     5511        switch (s.interpolate) {
     5512          case "step-before": {
     5513            pi += "V" + sk.top;
     5514            pj += "H" + (sl.left + sl.width);
     5515            break;
     5516          }
     5517          case "step-after": {
     5518            pi += "H" + sk.left;
     5519            pj += "V" + (sl.top + sl.height);
     5520            break;
     5521          }
     5522        }
     5523      }
     5524
     5525      p1.push(pi);
     5526      p2.push(pj);
     5527    }
     5528    return p1.concat(p2).join("L");
     5529  }
     5530
     5531  /** @private Computes the curved path for the range [i, j]. */
     5532  function pathCurve(i, j) {
     5533    var pointsT = [], pointsB = [], pathT, pathB;
     5534
     5535    for (var k = j; i <= k; i++, j--) {
     5536      var sj = scenes[j];
     5537      pointsT.push(scenes[i]);
     5538      pointsB.push({left: sj.left + sj.width, top: sj.top + sj.height});
     5539    }
     5540
     5541    if (s.interpolate == "basis") {
     5542      pathT = pv.SvgScene.curveBasis(pointsT);
     5543      pathB = pv.SvgScene.curveBasis(pointsB);
     5544    } else if (s.interpolate == "cardinal") {
     5545      pathT = pv.SvgScene.curveCardinal(pointsT, s.tension);
     5546      pathB = pv.SvgScene.curveCardinal(pointsB, s.tension);
     5547    } else { // monotone
     5548      pathT = pv.SvgScene.curveMonotone(pointsT);
     5549      pathB = pv.SvgScene.curveMonotone(pointsB);
     5550    }
     5551
     5552    return pointsT[0].left + "," + pointsT[0].top + pathT
     5553         + "L" + pointsB[0].left + "," + pointsB[0].top + pathB;
     5554  }
     5555
     5556  /* points */
     5557  var d = [], si, sj;
     5558  for (var i = 0; i < scenes.length; i++) {
     5559    si = scenes[i]; if (!si.width && !si.height) continue;
     5560    for (var j = i + 1; j < scenes.length; j++) {
     5561      sj = scenes[j]; if (!sj.width && !sj.height) break;
     5562    }
     5563    if (i && (s.interpolate != "step-after")) i--;
     5564    if ((j < scenes.length) && (s.interpolate != "step-before")) j++;
     5565    d.push(((j - i > 2
     5566        && (s.interpolate == "basis"
     5567        || s.interpolate == "cardinal"
     5568        || s.interpolate == "monotone"))
     5569        ? pathCurve : path)(i, j - 1));
     5570    i = j - 1;
     5571  }
     5572  if (!d.length) return e;
     5573
     5574  e = this.expect(e, "path", {
     5575      "shape-rendering": s.antialias ? null : "crispEdges",
     5576      "pointer-events": s.events,
     5577      "cursor": s.cursor,
     5578      "d": "M" + d.join("ZM") + "Z",
     5579      "fill": fill.color,
     5580      "fill-opacity": fill.opacity || null,
     5581      "stroke": stroke.color,
     5582      "stroke-opacity": stroke.opacity || null,
     5583      "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null
     5584    });
     5585  return this.append(e, scenes, 0);
     5586};
     5587
     5588pv.SvgScene.areaSegment = function(scenes) {
     5589  var e = scenes.$g.firstChild, s = scenes[0], pathsT, pathsB;
     5590  if (s.interpolate == "basis"
     5591      || s.interpolate == "cardinal"
     5592      || s.interpolate == "monotone") {
     5593    var pointsT = [], pointsB = [];
     5594
     5595    for (var i = 0, n = scenes.length; i < n; i++) {
     5596      var sj = scenes[n - i - 1];
     5597      pointsT.push(scenes[i]);
     5598      pointsB.push({left: sj.left + sj.width, top: sj.top + sj.height});
     5599    }
     5600
     5601    if (s.interpolate == "basis") {
     5602      pathsT = this.curveBasisSegments(pointsT);
     5603      pathsB = this.curveBasisSegments(pointsB);
     5604    } else if (s.interpolate == "cardinal") {
     5605      pathsT = this.curveCardinalSegments(pointsT, s.tension);
     5606      pathsB = this.curveCardinalSegments(pointsB, s.tension);
     5607    } else { // monotone
     5608      pathsT = this.curveMonotoneSegments(pointsT);
     5609      pathsB = this.curveMonotoneSegments(pointsB);
     5610    }
     5611  }
     5612
     5613  for (var i = 0, n = scenes.length - 1; i < n; i++) {
     5614    var s1 = scenes[i], s2 = scenes[i + 1];
     5615
     5616    /* visible */
     5617    if (!s1.visible || !s2.visible) continue;
     5618    var fill = s1.fillStyle, stroke = s1.strokeStyle;
     5619    if (!fill.opacity && !stroke.opacity) continue;
     5620
     5621    var d;
     5622    if (pathsT) {
     5623      var pathT = pathsT[i],
     5624          pathB = "L" + pathsB[n - i - 1].substr(1);
     5625
     5626      d = pathT + pathB + "Z";
     5627    } else {
     5628      /* interpolate */
     5629      var si = s1, sj = s2;
     5630      switch (s1.interpolate) {
     5631        case "step-before": si = s2; break;
     5632        case "step-after": sj = s1; break;
     5633      }
     5634
     5635      /* path */
     5636      d = "M" + s1.left + "," + si.top
     5637        + "L" + s2.left + "," + sj.top
     5638        + "L" + (s2.left + s2.width) + "," + (sj.top + sj.height)
     5639        + "L" + (s1.left + s1.width) + "," + (si.top + si.height)
     5640        + "Z";
     5641    }
     5642
     5643    e = this.expect(e, "path", {
     5644        "shape-rendering": s1.antialias ? null : "crispEdges",
     5645        "pointer-events": s1.events,
     5646        "cursor": s1.cursor,
     5647        "d": d,
     5648        "fill": fill.color,
     5649        "fill-opacity": fill.opacity || null,
     5650        "stroke": stroke.color,
     5651        "stroke-opacity": stroke.opacity || null,
     5652        "stroke-width": stroke.opacity ? s1.lineWidth / this.scale : null
     5653      });
     5654    e = this.append(e, scenes, i);
     5655  }
     5656  return e;
     5657};
     5658pv.SvgScene.bar = function(scenes) {
     5659  var e = scenes.$g.firstChild;
     5660  for (var i = 0; i < scenes.length; i++) {
     5661    var s = scenes[i];
     5662
     5663    /* visible */
     5664    if (!s.visible) continue;
     5665    var fill = s.fillStyle, stroke = s.strokeStyle;
     5666    if (!fill.opacity && !stroke.opacity) continue;
     5667
     5668    e = this.expect(e, "rect", {
     5669        "shape-rendering": s.antialias ? null : "crispEdges",
     5670        "pointer-events": s.events,
     5671        "cursor": s.cursor,
     5672        "x": s.left,
     5673        "y": s.top,
     5674        "width": Math.max(1E-10, s.width),
     5675        "height": Math.max(1E-10, s.height),
     5676        "fill": fill.color,
     5677        "fill-opacity": fill.opacity || null,
     5678        "stroke": stroke.color,
     5679        "stroke-opacity": stroke.opacity || null,
     5680        "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null
     5681      });
     5682    e = this.append(e, scenes, i);
     5683  }
     5684  return e;
     5685};
     5686pv.SvgScene.dot = function(scenes) {
     5687  var e = scenes.$g.firstChild;
     5688  for (var i = 0; i < scenes.length; i++) {
     5689    var s = scenes[i];
     5690
     5691    /* visible */
     5692    if (!s.visible) continue;
     5693    var fill = s.fillStyle, stroke = s.strokeStyle;
     5694    if (!fill.opacity && !stroke.opacity) continue;
     5695
     5696    /* points */
     5697    var radius = s.radius, path = null;
     5698    switch (s.shape) {
     5699      case "cross": {
     5700        path = "M" + -radius + "," + -radius
     5701            + "L" + radius + "," + radius
     5702            + "M" + radius + "," + -radius
     5703            + "L" + -radius + "," + radius;
     5704        break;
     5705      }
     5706      case "triangle": {
     5707        var h = radius, w = radius * 1.1547; // 2 / Math.sqrt(3)
     5708        path = "M0," + h
     5709            + "L" + w +"," + -h
     5710            + " " + -w + "," + -h
     5711            + "Z";
     5712        break;
     5713      }
     5714      case "diamond": {
     5715        radius *= Math.SQRT2;
     5716        path = "M0," + -radius
     5717            + "L" + radius + ",0"
     5718            + " 0," + radius
     5719            + " " + -radius + ",0"
     5720            + "Z";
     5721        break;
     5722      }
     5723      case "square": {
     5724        path = "M" + -radius + "," + -radius
     5725            + "L" + radius + "," + -radius
     5726            + " " + radius + "," + radius
     5727            + " " + -radius + "," + radius
     5728            + "Z";
     5729        break;
     5730      }
     5731      case "tick": {
     5732        path = "M0,0L0," + -s.size;
     5733        break;
     5734      }
     5735      case "bar": {
     5736        path = "M0," + (s.size / 2) + "L0," + -(s.size / 2);
     5737        break;
     5738      }
     5739    }
     5740
     5741    /* Use <circle> for circles, <path> for everything else. */
     5742    var svg = {
     5743      "shape-rendering": s.antialias ? null : "crispEdges",
     5744      "pointer-events": s.events,
     5745      "cursor": s.cursor,
     5746      "fill": fill.color,
     5747      "fill-opacity": fill.opacity || null,
     5748      "stroke": stroke.color,
     5749      "stroke-opacity": stroke.opacity || null,
     5750      "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null
     5751    };
     5752    if (path) {
     5753      svg.transform = "translate(" + s.left + "," + s.top + ")";
     5754      if (s.angle) svg.transform += " rotate(" + 180 * s.angle / Math.PI + ")";
     5755      svg.d = path;
     5756      e = this.expect(e, "path", svg);
     5757    } else {
     5758      svg.cx = s.left;
     5759      svg.cy = s.top;
     5760      svg.r = radius;
     5761      e = this.expect(e, "circle", svg);
     5762    }
     5763    e = this.append(e, scenes, i);
     5764  }
     5765  return e;
     5766};
     5767pv.SvgScene.image = function(scenes) {
     5768  var e = scenes.$g.firstChild;
     5769  for (var i = 0; i < scenes.length; i++) {
     5770    var s = scenes[i];
     5771
     5772    /* visible */
     5773    if (!s.visible) continue;
     5774
     5775    /* fill */
     5776    e = this.fill(e, scenes, i);
     5777
     5778    /* image */
     5779    if (s.image) {
     5780      e = this.expect(e, "foreignObject", {
     5781          "cursor": s.cursor,
     5782          "x": s.left,
     5783          "y": s.top,
     5784          "width": s.width,
     5785          "height": s.height
     5786        });
     5787      var c = e.firstChild || e.appendChild(document.createElementNS(this.xhtml, "canvas"));
     5788      c.$scene = {scenes:scenes, index:i};
     5789      c.style.width = s.width;
     5790      c.style.height = s.height;
     5791      c.width = s.imageWidth;
     5792      c.height = s.imageHeight;
     5793      c.getContext("2d").putImageData(s.image, 0, 0);
     5794    } else {
     5795      e = this.expect(e, "image", {
     5796          "preserveAspectRatio": "none",
     5797          "cursor": s.cursor,
     5798          "x": s.left,
     5799          "y": s.top,
     5800          "width": s.width,
     5801          "height": s.height
     5802        });
     5803      e.setAttributeNS(this.xlink, "href", s.url);
     5804    }
     5805    e = this.append(e, scenes, i);
     5806
     5807    /* stroke */
     5808    e = this.stroke(e, scenes, i);
     5809  }
     5810  return e;
     5811};
     5812pv.SvgScene.label = function(scenes) {
     5813  var e = scenes.$g.firstChild;
     5814  for (var i = 0; i < scenes.length; i++) {
     5815    var s = scenes[i];
     5816
     5817    /* visible */
     5818    if (!s.visible) continue;
     5819    var fill = s.textStyle;
     5820    if (!fill.opacity || !s.text) continue;
     5821
     5822    /* text-baseline, text-align */
     5823    var x = 0, y = 0, dy = 0, anchor = "start";
     5824    switch (s.textBaseline) {
     5825      case "middle": dy = ".35em"; break;
     5826      case "top": dy = ".71em"; y = s.textMargin; break;
     5827      case "bottom": y = "-" + s.textMargin; break;
     5828    }
     5829    switch (s.textAlign) {
     5830      case "right": anchor = "end"; x = "-" + s.textMargin; break;
     5831      case "center": anchor = "middle"; break;
     5832      case "left": x = s.textMargin; break;
     5833    }
     5834
     5835    e = this.expect(e, "text", {
     5836        "pointer-events": s.events,
     5837        "cursor": s.cursor,
     5838        "x": x,
     5839        "y": y,
     5840        "dy": dy,
     5841        "transform": "translate(" + s.left + "," + s.top + ")"
     5842            + (s.textAngle ? " rotate(" + 180 * s.textAngle / Math.PI + ")" : "")
     5843            + (this.scale != 1 ? " scale(" + 1 / this.scale + ")" : ""),
     5844        "fill": fill.color,
     5845        "fill-opacity": fill.opacity || null,
     5846        "text-anchor": anchor
     5847      }, {
     5848        "font": s.font,
     5849        "text-shadow": s.textShadow,
     5850        "text-decoration": s.textDecoration
     5851      });
     5852    if (e.firstChild) e.firstChild.nodeValue = s.text;
     5853    else e.appendChild(document.createTextNode(s.text));
     5854    e = this.append(e, scenes, i);
     5855  }
     5856  return e;
     5857};
     5858pv.SvgScene.line = function(scenes) {
     5859  var e = scenes.$g.firstChild;
     5860  if (scenes.length < 2) return e;
     5861  var s = scenes[0];
     5862
     5863  /* segmented */
     5864  if (s.segmented) return this.lineSegment(scenes);
     5865
     5866  /* visible */
     5867  if (!s.visible) return e;
     5868  var fill = s.fillStyle, stroke = s.strokeStyle;
     5869  if (!fill.opacity && !stroke.opacity) return e;
     5870
     5871  /* points */
     5872  var d = "M" + s.left + "," + s.top;
     5873
     5874  if (scenes.length > 2 && (s.interpolate == "basis" || s.interpolate == "cardinal" || s.interpolate == "monotone")) {
     5875    switch (s.interpolate) {
     5876      case "basis": d += this.curveBasis(scenes); break;
     5877      case "cardinal": d += this.curveCardinal(scenes, s.tension); break;
     5878      case "monotone": d += this.curveMonotone(scenes); break;
     5879    }
     5880  } else {
     5881    for (var i = 1; i < scenes.length; i++) {
     5882      d += this.pathSegment(scenes[i - 1], scenes[i]);
     5883    }
     5884  }
     5885
     5886  e = this.expect(e, "path", {
     5887      "shape-rendering": s.antialias ? null : "crispEdges",
     5888      "pointer-events": s.events,
     5889      "cursor": s.cursor,
     5890      "d": d,
     5891      "fill": fill.color,
     5892      "fill-opacity": fill.opacity || null,
     5893      "stroke": stroke.color,
     5894      "stroke-opacity": stroke.opacity || null,
     5895      "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null,
     5896      "stroke-linejoin": s.lineJoin
     5897    });
     5898  return this.append(e, scenes, 0);
     5899};
     5900
     5901pv.SvgScene.lineSegment = function(scenes) {
     5902  var e = scenes.$g.firstChild;
     5903
     5904  var s = scenes[0];
     5905  var paths;
     5906  switch (s.interpolate) {
     5907    case "basis": paths = this.curveBasisSegments(scenes); break;
     5908    case "cardinal": paths = this.curveCardinalSegments(scenes, s.tension); break;
     5909    case "monotone": paths = this.curveMonotoneSegments(scenes); break;
     5910  }
     5911
     5912  for (var i = 0, n = scenes.length - 1; i < n; i++) {
     5913    var s1 = scenes[i], s2 = scenes[i + 1];
     5914
     5915    /* visible */
     5916    if (!s1.visible || !s2.visible) continue;
     5917    var stroke = s1.strokeStyle, fill = pv.Color.transparent;
     5918    if (!stroke.opacity) continue;
     5919
     5920    /* interpolate */
     5921    var d;
     5922    if ((s1.interpolate == "linear") && (s1.lineJoin == "miter")) {
     5923      fill = stroke;
     5924      stroke = pv.Color.transparent;
     5925      d = this.pathJoin(scenes[i - 1], s1, s2, scenes[i + 2]);
     5926    } else if(paths) {
     5927      d = paths[i];
     5928    } else {
     5929      d = "M" + s1.left + "," + s1.top + this.pathSegment(s1, s2);
     5930    }
     5931
     5932    e = this.expect(e, "path", {
     5933        "shape-rendering": s1.antialias ? null : "crispEdges",
     5934        "pointer-events": s1.events,
     5935        "cursor": s1.cursor,
     5936        "d": d,
     5937        "fill": fill.color,
     5938        "fill-opacity": fill.opacity || null,
     5939        "stroke": stroke.color,
     5940        "stroke-opacity": stroke.opacity || null,
     5941        "stroke-width": stroke.opacity ? s1.lineWidth / this.scale : null,
     5942        "stroke-linejoin": s1.lineJoin
     5943      });
     5944    e = this.append(e, scenes, i);
     5945  }
     5946  return e;
     5947};
     5948
     5949/** @private Returns the path segment for the specified points. */
     5950pv.SvgScene.pathSegment = function(s1, s2) {
     5951  var l = 1; // sweep-flag
     5952  switch (s1.interpolate) {
     5953    case "polar-reverse":
     5954      l = 0;
     5955    case "polar": {
     5956      var dx = s2.left - s1.left,
     5957          dy = s2.top - s1.top,
     5958          e = 1 - s1.eccentricity,
     5959          r = Math.sqrt(dx * dx + dy * dy) / (2 * e);
     5960      if ((e <= 0) || (e > 1)) break; // draw a straight line
     5961      return "A" + r + "," + r + " 0 0," + l + " " + s2.left + "," + s2.top;
     5962    }
     5963    case "step-before": return "V" + s2.top + "H" + s2.left;
     5964    case "step-after": return "H" + s2.left + "V" + s2.top;
     5965  }
     5966  return "L" + s2.left + "," + s2.top;
     5967};
     5968
     5969/** @private Line-line intersection, per Akenine-Moller 16.16.1. */
     5970pv.SvgScene.lineIntersect = function(o1, d1, o2, d2) {
     5971  return o1.plus(d1.times(o2.minus(o1).dot(d2.perp()) / d1.dot(d2.perp())));
     5972}
     5973
     5974/** @private Returns the miter join path for the specified points. */
     5975pv.SvgScene.pathJoin = function(s0, s1, s2, s3) {
     5976  /*
     5977   * P1-P2 is the current line segment. V is a vector that is perpendicular to
     5978   * the line segment, and has length lineWidth / 2. ABCD forms the initial
     5979   * bounding box of the line segment (i.e., the line segment if we were to do
     5980   * no joins).
     5981   */
     5982  var p1 = pv.vector(s1.left, s1.top),
     5983      p2 = pv.vector(s2.left, s2.top),
     5984      p = p2.minus(p1),
     5985      v = p.perp().norm(),
     5986      w = v.times(s1.lineWidth / (2 * this.scale)),
     5987      a = p1.plus(w),
     5988      b = p2.plus(w),
     5989      c = p2.minus(w),
     5990      d = p1.minus(w);
     5991
     5992  /*
     5993   * Start join. P0 is the previous line segment's start point. We define the
     5994   * cutting plane as the average of the vector perpendicular to P0-P1, and
     5995   * the vector perpendicular to P1-P2. This insures that the cross-section of
     5996   * the line on the cutting plane is equal if the line-width is unchanged.
     5997   * Note that we don't implement miter limits, so these can get wild.
     5998   */
     5999  if (s0 && s0.visible) {
     6000    var v1 = p1.minus(s0.left, s0.top).perp().norm().plus(v);
     6001    d = this.lineIntersect(p1, v1, d, p);
     6002    a = this.lineIntersect(p1, v1, a, p);
     6003  }
     6004
     6005  /* Similarly, for end join. */
     6006  if (s3 && s3.visible) {
     6007    var v2 = pv.vector(s3.left, s3.top).minus(p2).perp().norm().plus(v);
     6008    c = this.lineIntersect(p2, v2, c, p);
     6009    b = this.lineIntersect(p2, v2, b, p);
     6010  }
     6011
     6012  return "M" + a.x + "," + a.y
     6013       + "L" + b.x + "," + b.y
     6014       + " " + c.x + "," + c.y
     6015       + " " + d.x + "," + d.y;
     6016};
     6017pv.SvgScene.panel = function(scenes) {
     6018  var g = scenes.$g, e = g && g.firstChild;
     6019  for (var i = 0; i < scenes.length; i++) {
     6020    var s = scenes[i];
     6021
     6022    /* visible */
     6023    if (!s.visible) continue;
     6024
     6025    /* svg */
     6026    if (!scenes.parent) {
     6027      s.canvas.style.display = "inline-block";
     6028      if (g && (g.parentNode != s.canvas)) {
     6029        g = s.canvas.firstChild;
     6030        e = g && g.firstChild;
     6031      }
     6032      if (!g) {
     6033        g = s.canvas.appendChild(this.create("svg"));
     6034        g.setAttribute("font-size", "10px");
     6035        g.setAttribute("font-family", "sans-serif");
     6036        g.setAttribute("fill", "none");
     6037        g.setAttribute("stroke", "none");
     6038        g.setAttribute("stroke-width", 1.5);
     6039        for (var j = 0; j < this.events.length; j++) {
     6040          g.addEventListener(this.events[j], this.dispatch, false);
     6041        }
     6042        e = g.firstChild;
     6043      }
     6044      scenes.$g = g;
     6045      g.setAttribute("width", s.width + s.left + s.right);
     6046      g.setAttribute("height", s.height + s.top + s.bottom);
     6047    }
     6048
     6049    /* clip (nest children) */
     6050    if (s.overflow == "hidden") {
     6051      var id = pv.id().toString(36),
     6052          c = this.expect(e, "g", {"clip-path": "url(#" + id + ")"});
     6053      if (!c.parentNode) g.appendChild(c);
     6054      scenes.$g = g = c;
     6055      e = c.firstChild;
     6056
     6057      e = this.expect(e, "clipPath", {"id": id});
     6058      var r = e.firstChild || e.appendChild(this.create("rect"));
     6059      r.setAttribute("x", s.left);
     6060      r.setAttribute("y", s.top);
     6061      r.setAttribute("width", s.width);
     6062      r.setAttribute("height", s.height);
     6063      if (!e.parentNode) g.appendChild(e);
     6064      e = e.nextSibling;
     6065    }
     6066
     6067    /* fill */
     6068    e = this.fill(e, scenes, i);
     6069
     6070    /* transform (push) */
     6071    var k = this.scale,
     6072        t = s.transform,
     6073        x = s.left + t.x,
     6074        y = s.top + t.y;
     6075    this.scale *= t.k;
     6076
     6077    /* children */
     6078    for (var j = 0; j < s.children.length; j++) {
     6079      s.children[j].$g = e = this.expect(e, "g", {
     6080          "transform": "translate(" + x + "," + y + ")"
     6081              + (t.k != 1 ? " scale(" + t.k + ")" : "")
     6082        });
     6083      this.updateAll(s.children[j]);
     6084      if (!e.parentNode) g.appendChild(e);
     6085      e = e.nextSibling;
     6086    }
     6087
     6088    /* transform (pop) */
     6089    this.scale = k;
     6090
     6091    /* stroke */
     6092    e = this.stroke(e, scenes, i);
     6093
     6094    /* clip (restore group) */
     6095    if (s.overflow == "hidden") {
     6096      scenes.$g = g = c.parentNode;
     6097      e = c.nextSibling;
     6098    }
     6099  }
     6100  return e;
     6101};
     6102
     6103pv.SvgScene.fill = function(e, scenes, i) {
     6104  var s = scenes[i], fill = s.fillStyle;
     6105  if (fill.opacity || s.events == "all") {
     6106    e = this.expect(e, "rect", {
     6107        "shape-rendering": s.antialias ? null : "crispEdges",
     6108        "pointer-events": s.events,
     6109        "cursor": s.cursor,
     6110        "x": s.left,
     6111        "y": s.top,
     6112        "width": s.width,
     6113        "height": s.height,
     6114        "fill": fill.color,
     6115        "fill-opacity": fill.opacity,
     6116        "stroke": null
     6117      });
     6118    e = this.append(e, scenes, i);
     6119  }
     6120  return e;
     6121};
     6122
     6123pv.SvgScene.stroke = function(e, scenes, i) {
     6124  var s = scenes[i], stroke = s.strokeStyle;
     6125  if (stroke.opacity || s.events == "all") {
     6126    e = this.expect(e, "rect", {
     6127        "shape-rendering": s.antialias ? null : "crispEdges",
     6128        "pointer-events": s.events == "all" ? "stroke" : s.events,
     6129        "cursor": s.cursor,
     6130        "x": s.left,
     6131        "y": s.top,
     6132        "width": Math.max(1E-10, s.width),
     6133        "height": Math.max(1E-10, s.height),
     6134        "fill": null,
     6135        "stroke": stroke.color,
     6136        "stroke-opacity": stroke.opacity,
     6137        "stroke-width": s.lineWidth / this.scale
     6138      });
     6139    e = this.append(e, scenes, i);
     6140  }
     6141  return e;
     6142};
     6143pv.SvgScene.rule = function(scenes) {
     6144  var e = scenes.$g.firstChild;
     6145  for (var i = 0; i < scenes.length; i++) {
     6146    var s = scenes[i];
     6147
     6148    /* visible */
     6149    if (!s.visible) continue;
     6150    var stroke = s.strokeStyle;
     6151    if (!stroke.opacity) continue;
     6152
     6153    e = this.expect(e, "line", {
     6154        "shape-rendering": s.antialias ? null : "crispEdges",
     6155        "pointer-events": s.events,
     6156        "cursor": s.cursor,
     6157        "x1": s.left,
     6158        "y1": s.top,
     6159        "x2": s.left + s.width,
     6160        "y2": s.top + s.height,
     6161        "stroke": stroke.color,
     6162        "stroke-opacity": stroke.opacity,
     6163        "stroke-width": s.lineWidth / this.scale
     6164      });
     6165    e = this.append(e, scenes, i);
     6166  }
     6167  return e;
     6168};
     6169pv.SvgScene.wedge = function(scenes) {
     6170  var e = scenes.$g.firstChild;
     6171  for (var i = 0; i < scenes.length; i++) {
     6172    var s = scenes[i];
     6173
     6174    /* visible */
     6175    if (!s.visible) continue;
     6176    var fill = s.fillStyle, stroke = s.strokeStyle;
     6177    if (!fill.opacity && !stroke.opacity) continue;
     6178
     6179    /* points */
     6180    var r1 = s.innerRadius, r2 = s.outerRadius, a = Math.abs(s.angle), p;
     6181    if (a >= 2 * Math.PI) {
     6182      if (r1) {
     6183        p = "M0," + r2
     6184            + "A" + r2 + "," + r2 + " 0 1,1 0," + (-r2)
     6185            + "A" + r2 + "," + r2 + " 0 1,1 0," + r2
     6186            + "M0," + r1
     6187            + "A" + r1 + "," + r1 + " 0 1,1 0," + (-r1)
     6188            + "A" + r1 + "," + r1 + " 0 1,1 0," + r1
     6189            + "Z";
     6190      } else {
     6191        p = "M0," + r2
     6192            + "A" + r2 + "," + r2 + " 0 1,1 0," + (-r2)
     6193            + "A" + r2 + "," + r2 + " 0 1,1 0," + r2
     6194            + "Z";
     6195      }
     6196    } else {
     6197      var sa = Math.min(s.startAngle, s.endAngle),
     6198          ea = Math.max(s.startAngle, s.endAngle),
     6199          c1 = Math.cos(sa), c2 = Math.cos(ea),
     6200          s1 = Math.sin(sa), s2 = Math.sin(ea);
     6201      if (r1) {
     6202        p = "M" + r2 * c1 + "," + r2 * s1
     6203            + "A" + r2 + "," + r2 + " 0 "
     6204            + ((a < Math.PI) ? "0" : "1") + ",1 "
     6205            + r2 * c2 + "," + r2 * s2
     6206            + "L" + r1 * c2 + "," + r1 * s2
     6207            + "A" + r1 + "," + r1 + " 0 "
     6208            + ((a < Math.PI) ? "0" : "1") + ",0 "
     6209            + r1 * c1 + "," + r1 * s1 + "Z";
     6210      } else {
     6211        p = "M" + r2 * c1 + "," + r2 * s1
     6212            + "A" + r2 + "," + r2 + " 0 "
     6213            + ((a < Math.PI) ? "0" : "1") + ",1 "
     6214            + r2 * c2 + "," + r2 * s2 + "L0,0Z";
     6215      }
     6216    }
     6217
     6218    e = this.expect(e, "path", {
     6219        "shape-rendering": s.antialias ? null : "crispEdges",
     6220        "pointer-events": s.events,
     6221        "cursor": s.cursor,
     6222        "transform": "translate(" + s.left + "," + s.top + ")",
     6223        "d": p,
     6224        "fill": fill.color,
     6225        "fill-rule": "evenodd",
     6226        "fill-opacity": fill.opacity || null,
     6227        "stroke": stroke.color,
     6228        "stroke-opacity": stroke.opacity || null,
     6229        "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null
     6230      });
     6231    e = this.append(e, scenes, i);
     6232  }
     6233  return e;
     6234};
     6235/**
     6236 * Constructs a new mark with default properties. Marks, with the exception of
     6237 * the root panel, are not typically constructed directly; instead, they are
     6238 * added to a panel or an existing mark via {@link pv.Mark#add}.
     6239 *
     6240 * @class Represents a data-driven graphical mark. The <tt>Mark</tt> class is
     6241 * the base class for all graphical marks in Protovis; it does not provide any
     6242 * specific rendering functionality, but together with {@link Panel} establishes
     6243 * the core framework.
     6244 *
     6245 * <p>Concrete mark types include familiar visual elements such as bars, lines
     6246 * and labels. Although a bar mark may be used to construct a bar chart, marks
     6247 * know nothing about charts; it is only through their specification and
     6248 * composition that charts are produced. These building blocks permit many
     6249 * combinatorial possibilities.
     6250 *
     6251 * <p>Marks are associated with <b>data</b>: a mark is generated once per
     6252 * associated datum, mapping the datum to visual <b>properties</b> such as
     6253 * position and color. Thus, a single mark specification represents a set of
     6254 * visual elements that share the same data and visual encoding. The type of
     6255 * mark defines the names of properties and their meaning. A property may be
     6256 * static, ignoring the associated datum and returning a constant; or, it may be
     6257 * dynamic, derived from the associated datum or index. Such dynamic encodings
     6258 * can be specified succinctly using anonymous functions. Special properties
     6259 * called event handlers can be registered to add interactivity.
     6260 *
     6261 * <p>Protovis uses <b>inheritance</b> to simplify the specification of related
     6262 * marks: a new mark can be derived from an existing mark, inheriting its
     6263 * properties. The new mark can then override properties to specify new
     6264 * behavior, potentially in terms of the old behavior. In this way, the old mark
     6265 * serves as the <b>prototype</b> for the new mark. Most mark types share the
     6266 * same basic properties for consistency and to facilitate inheritance.
     6267 *
     6268 * <p>The prioritization of redundant properties is as follows:<ol>
     6269 *
     6270 * <li>If the <tt>width</tt> property is not specified (i.e., null), its value
     6271 * is the width of the parent panel, minus this mark's left and right margins;
     6272 * the left and right margins are zero if not specified.
     6273 *
     6274 * <li>Otherwise, if the <tt>right</tt> margin is not specified, its value is
     6275 * the width of the parent panel, minus this mark's width and left margin; the
     6276 * left margin is zero if not specified.
     6277 *
     6278 * <li>Otherwise, if the <tt>left</tt> property is not specified, its value is
     6279 * the width of the parent panel, minus this mark's width and the right margin.
     6280 *
     6281 * </ol>This prioritization is then duplicated for the <tt>height</tt>,
     6282 * <tt>bottom</tt> and <tt>top</tt> properties, respectively.
     6283 *
     6284 * <p>While most properties are <i>variable</i>, some mark types, such as lines
     6285 * and areas, generate a single visual element rather than a distinct visual
     6286 * element per datum. With these marks, some properties may be <b>fixed</b>.
     6287 * Fixed properties can vary per mark, but not <i>per datum</i>! These
     6288 * properties are evaluated solely for the first (0-index) datum, and typically
     6289 * are specified as a constant. However, it is valid to use a function if the
     6290 * property varies between panels or is dynamically generated.
     6291 *
     6292 * <p>See also the <a href="../../api/">Protovis guide</a>.
     6293 */
     6294pv.Mark = function() {
     6295  /*
     6296   * TYPE 0 constant defs
     6297   * TYPE 1 function defs
     6298   * TYPE 2 constant properties
     6299   * TYPE 3 function properties
     6300   * in order of evaluation!
     6301   */
     6302  this.$properties = [];
     6303  this.$handlers = {};
     6304};
     6305
     6306/** @private Records which properties are defined on this mark type. */
     6307pv.Mark.prototype.properties = {};
     6308
     6309/** @private Records the cast function for each property. */
     6310pv.Mark.cast = {};
     6311
     6312/**
     6313 * @private Defines and registers a property method for the property with the
     6314 * given name.  This method should be called on a mark class prototype to define
     6315 * each exposed property. (Note this refers to the JavaScript
     6316 * <tt>prototype</tt>, not the Protovis mark prototype, which is the {@link
     6317 * #proto} field.)
     6318 *
     6319 * <p>The created property method supports several modes of invocation: <ol>
     6320 *
     6321 * <li>If invoked with a <tt>Function</tt> argument, this function is evaluated
     6322 * for each associated datum. The return value of the function is used as the
     6323 * computed property value. The context of the function (<tt>this</tt>) is this
     6324 * mark. The arguments to the function are the associated data of this mark and
     6325 * any enclosing panels. For example, a linear encoding of numerical data to
     6326 * height is specified as
     6327 *
     6328 * <pre>m.height(function(d) d * 100);</pre>
     6329 *
     6330 * The expression <tt>d * 100</tt> will be evaluated for the height property of
     6331 * each mark instance. The return value of the property method (e.g.,
     6332 * <tt>m.height</tt>) is this mark (<tt>m</tt>)).<p>
     6333 *
     6334 * <li>If invoked with a non-function argument, the property is treated as a
     6335 * constant. The return value of the property method (e.g., <tt>m.height</tt>)
     6336 * is this mark.<p>
     6337 *
     6338 * <li>If invoked with no arguments, the computed property value for the current
     6339 * mark instance in the scene graph is returned. This facilitates <i>property
     6340 * chaining</i>, where one mark's properties are defined in terms of another's.
     6341 * For example, to offset a mark's location from its prototype, you might say
     6342 *
     6343 * <pre>m.top(function() this.proto.top() + 10);</pre>
     6344 *
     6345 * Note that the index of the mark being evaluated (in the above example,
     6346 * <tt>this.proto</tt>) is inherited from the <tt>Mark</tt> class and set by
     6347 * this mark. So, if the fifth element's top property is being evaluated, the
     6348 * fifth instance of <tt>this.proto</tt> will similarly be queried for the value
     6349 * of its top property. If the mark being evaluated has a different number of
     6350 * instances, or its data is unrelated, the behavior of this method is
     6351 * undefined. In these cases it may be better to index the <tt>scene</tt>
     6352 * explicitly to specify the exact instance.
     6353 *
     6354 * </ol><p>Property names should follow standard JavaScript method naming
     6355 * conventions, using lowerCamel-style capitalization.
     6356 *
     6357 * <p>In addition to creating the property method, every property is registered
     6358 * in the {@link #properties} map on the <tt>prototype</tt>. Although this is an
     6359 * instance field, it is considered immutable and shared by all instances of a
     6360 * given mark type. The <tt>properties</tt> map can be queried to see if a mark
     6361 * type defines a particular property, such as width or height.
     6362 *
     6363 * @param {string} name the property name.
     6364 * @param {function} [cast] the cast function for this property.
     6365 */
     6366pv.Mark.prototype.property = function(name, cast) {
     6367  if (!this.hasOwnProperty("properties")) {
     6368    this.properties = pv.extend(this.properties);
     6369  }
     6370  this.properties[name] = true;
     6371
     6372  /*
     6373   * Define the setter-getter globally, since the default behavior should be the
     6374   * same for all properties, and since the Protovis inheritance chain is
     6375   * independent of the JavaScript inheritance chain. For example, anchors
     6376   * define a "name" property that is evaluated on derived marks, even though
     6377   * those marks don't normally have a name.
     6378   */
     6379  pv.Mark.prototype.propertyMethod(name, false, pv.Mark.cast[name] = cast);
     6380  return this;
     6381};
     6382
     6383/**
     6384 * @private Defines a setter-getter for the specified property.
     6385 *
     6386 * <p>If a cast function has been assigned to the specified property name, the
     6387 * property function is wrapped by the cast function, or, if a constant is
     6388 * specified, the constant is immediately cast. Note, however, that if the
     6389 * property value is null, the cast function is not invoked.
     6390 *
     6391 * @param {string} name the property name.
     6392 * @param {boolean} [def] whether is a property or a def.
     6393 * @param {function} [cast] the cast function for this property.
     6394 */
     6395pv.Mark.prototype.propertyMethod = function(name, def, cast) {
     6396  if (!cast) cast = pv.Mark.cast[name];
     6397  this[name] = function(v) {
     6398
     6399      /* If this is a def, use it rather than property. */
     6400      if (def && this.scene) {
     6401        var defs = this.scene.defs;
     6402        if (arguments.length) {
     6403          defs[name] = {
     6404            id: (v == null) ? 0 : pv.id(),
     6405            value: ((v != null) && cast) ? cast(v) : v
     6406          };
     6407          return this;
     6408        }
     6409        return defs[name] ? defs[name].value : null;
     6410      }
     6411
     6412      /* If arguments are specified, set the property value. */
     6413      if (arguments.length) {
     6414        var type = !def << 1 | (typeof v == "function");
     6415        this.propertyValue(name, (type & 1 && cast) ? function() {
     6416            var x = v.apply(this, arguments);
     6417            return (x != null) ? cast(x) : null;
     6418          } : (((v != null) && cast) ? cast(v) : v)).type = type;
     6419        return this;
     6420      }
     6421
     6422      return this.instance()[name];
     6423    };
     6424};
     6425
     6426/** @private Sets the value of the property <i>name</i> to <i>v</i>. */
     6427pv.Mark.prototype.propertyValue = function(name, v) {
     6428  var properties = this.$properties, p = {name: name, id: pv.id(), value: v};
     6429  for (var i = 0; i < properties.length; i++) {
     6430    if (properties[i].name == name) {
     6431      properties.splice(i, 1);
     6432      break;
     6433    }
     6434  }
     6435  properties.push(p);
     6436  return p;
     6437};
     6438
     6439/* Define all global properties. */
     6440pv.Mark.prototype
     6441    .property("data")
     6442    .property("visible", Boolean)
     6443    .property("left", Number)
     6444    .property("right", Number)
     6445    .property("top", Number)
     6446    .property("bottom", Number)
     6447    .property("cursor", String)
     6448    .property("title", String)
     6449    .property("reverse", Boolean)
     6450    .property("antialias", Boolean)
     6451    .property("events", String);
     6452
     6453/**
     6454 * The mark type; a lower camelCase name. The type name controls rendering
     6455 * behavior, and unless the rendering engine is extended, must be one of the
     6456 * built-in concrete mark types: area, bar, dot, image, label, line, panel,
     6457 * rule, or wedge.
     6458 *
     6459 * @type string
     6460 * @name pv.Mark.prototype.type
     6461 */
     6462
     6463/**
     6464 * The mark prototype, possibly undefined, from which to inherit property
     6465 * functions. The mark prototype is not necessarily of the same type as this
     6466 * mark. Any properties defined on this mark will override properties inherited
     6467 * either from the prototype or from the type-specific defaults.
     6468 *
     6469 * @type pv.Mark
     6470 * @name pv.Mark.prototype.proto
     6471 */
     6472
     6473/**
     6474 * The enclosing parent panel. The parent panel is generally undefined only for
     6475 * the root panel; however, it is possible to create "offscreen" marks that are
     6476 * used only for inheritance purposes.
     6477 *
     6478 * @type pv.Panel
     6479 * @name pv.Mark.prototype.parent
     6480 */
     6481
     6482/**
     6483 * The child index. -1 if the enclosing parent panel is null; otherwise, the
     6484 * zero-based index of this mark into the parent panel's <tt>children</tt> array.
     6485 *
     6486 * @type number
     6487 */
     6488pv.Mark.prototype.childIndex = -1;
     6489
     6490/**
     6491 * The mark index. The value of this field depends on which instance (i.e.,
     6492 * which element of the data array) is currently being evaluated. During the
     6493 * build phase, the index is incremented over each datum; when handling events,
     6494 * the index is set to the instance that triggered the event.
     6495 *
     6496 * @type number
     6497 */
     6498pv.Mark.prototype.index = -1;
     6499
     6500/**
     6501 * The current scale factor, based on any enclosing transforms. The current
     6502 * scale can be used to create scale-independent graphics. For example, to
     6503 * define a dot that has a radius of 10 irrespective of any zooming, say:
     6504 *
     6505 * <pre>dot.radius(function() 10 / this.scale)</pre>
     6506 *
     6507 * Note that the stroke width and font size are defined irrespective of scale
     6508 * (i.e., in screen space) already. Also note that when a transform is applied
     6509 * to a panel, the scale affects only the child marks, not the panel itself.
     6510 *
     6511 * @type number
     6512 * @see pv.Panel#transform
     6513 */
     6514pv.Mark.prototype.scale = 1;
     6515
     6516/**
     6517 * @private The scene graph. The scene graph is an array of objects; each object
     6518 * (or "node") corresponds to an instance of this mark and an element in the
     6519 * data array. The scene graph can be traversed to lookup previously-evaluated
     6520 * properties.
     6521 *
     6522 * @name pv.Mark.prototype.scene
     6523 */
     6524
     6525/**
     6526 * The root parent panel. This may be undefined for "offscreen" marks that are
     6527 * created for inheritance purposes only.
     6528 *
     6529 * @type pv.Panel
     6530 * @name pv.Mark.prototype.root
     6531 */
     6532
     6533/**
     6534 * The data property; an array of objects. The size of the array determines the
     6535 * number of marks that will be instantiated; each element in the array will be
     6536 * passed to property functions to compute the property values. Typically, the
     6537 * data property is specified as a constant array, such as
     6538 *
     6539 * <pre>m.data([1, 2, 3, 4, 5]);</pre>
     6540 *
     6541 * However, it is perfectly acceptable to define the data property as a
     6542 * function. This function might compute the data dynamically, allowing
     6543 * different data to be used per enclosing panel. For instance, in the stacked
     6544 * area graph example (see {@link #scene}), the data function on the area mark
     6545 * dereferences each series.
     6546 *
     6547 * @type array
     6548 * @name pv.Mark.prototype.data
     6549 */
     6550
     6551/**
     6552 * The visible property; a boolean determining whether or not the mark instance
     6553 * is visible. If a mark instance is not visible, its other properties will not
     6554 * be evaluated. Similarly, for panels no child marks will be rendered.
     6555 *
     6556 * @type boolean
     6557 * @name pv.Mark.prototype.visible
     6558 */
     6559
     6560/**
     6561 * The left margin; the distance, in pixels, between the left edge of the
     6562 * enclosing panel and the left edge of this mark. Note that in some cases this
     6563 * property may be redundant with the right property, or with the conjunction of
     6564 * right and width.
     6565 *
     6566 * @type number
     6567 * @name pv.Mark.prototype.left
     6568 */
     6569
     6570/**
     6571 * The right margin; the distance, in pixels, between the right edge of the
     6572 * enclosing panel and the right edge of this mark. Note that in some cases this
     6573 * property may be redundant with the left property, or with the conjunction of
     6574 * left and width.
     6575 *
     6576 * @type number
     6577 * @name pv.Mark.prototype.right
     6578 */
     6579
     6580/**
     6581 * The top margin; the distance, in pixels, between the top edge of the
     6582 * enclosing panel and the top edge of this mark. Note that in some cases this
     6583 * property may be redundant with the bottom property, or with the conjunction
     6584 * of bottom and height.
     6585 *
     6586 * @type number
     6587 * @name pv.Mark.prototype.top
     6588 */
     6589
     6590/**
     6591 * The bottom margin; the distance, in pixels, between the bottom edge of the
     6592 * enclosing panel and the bottom edge of this mark. Note that in some cases
     6593 * this property may be redundant with the top property, or with the conjunction
     6594 * of top and height.
     6595 *
     6596 * @type number
     6597 * @name pv.Mark.prototype.bottom
     6598 */
     6599
     6600/**
     6601 * The cursor property; corresponds to the CSS cursor property. This is
     6602 * typically used in conjunction with event handlers to indicate interactivity.
     6603 *
     6604 * @type string
     6605 * @name pv.Mark.prototype.cursor
     6606 * @see <a href="http://www.w3.org/TR/CSS2/ui.html#propdef-cursor">CSS2 cursor</a>
     6607 */
     6608
     6609/**
     6610 * The title property; corresponds to the HTML/SVG title property, allowing the
     6611 * general of simple plain text tooltips.
     6612 *
     6613 * @type string
     6614 * @name pv.Mark.prototype.title
     6615 */
     6616
     6617/**
     6618 * The events property; corresponds to the SVG pointer-events property,
     6619 * specifying how the mark should participate in mouse events. The default value
     6620 * is "painted". Supported values are:
     6621 *
     6622 * <p>"painted": The given mark may receive events when the mouse is over a
     6623 * "painted" area. The painted areas are the interior (i.e., fill) of the mark
     6624 * if a 'fillStyle' is specified, and the perimeter (i.e., stroke) of the mark
     6625 * if a 'strokeStyle' is specified.
     6626 *
     6627 * <p>"all": The given mark may receive events when the mouse is over either the
     6628 * interior (i.e., fill) or the perimeter (i.e., stroke) of the mark, regardless
     6629 * of the specified fillStyle and strokeStyle.
     6630 *
     6631 * <p>"none": The given mark may not receive events.
     6632 *
     6633 * @type string
     6634 * @name pv.Mark.prototype.events
     6635 */
     6636
     6637/**
     6638 * The reverse property; a boolean determining whether marks are ordered from
     6639 * front-to-back or back-to-front. SVG does not support explicit z-ordering;
     6640 * shapes are rendered in the order they appear. Thus, by default, marks are
     6641 * rendered in data order. Setting the reverse property to false reverses the
     6642 * order in which they are rendered; however, the properties are still evaluated
     6643 * (i.e., built) in forward order.
     6644 *
     6645 * @type boolean
     6646 * @name pv.Mark.prototype.reverse
     6647 */
     6648
     6649/**
     6650 * Default properties for all mark types. By default, the data array is the
     6651 * parent data as a single-element array; if the data property is not specified,
     6652 * this causes each mark to be instantiated as a singleton with the parents
     6653 * datum. The visible property is true by default, and the reverse property is
     6654 * false.
     6655 *
     6656 * @type pv.Mark
     6657 */
     6658pv.Mark.prototype.defaults = new pv.Mark()
     6659    .data(function(d) { return [d]; })
     6660    .visible(true)
     6661    .antialias(true)
     6662    .events("painted");
     6663
     6664/**
     6665 * Sets the prototype of this mark to the specified mark. Any properties not
     6666 * defined on this mark may be inherited from the specified prototype mark, or
     6667 * its prototype, and so on. The prototype mark need not be the same type of
     6668 * mark as this mark. (Note that for inheritance to be useful, properties with
     6669 * the same name on different mark types should have equivalent meaning.)
     6670 *
     6671 * @param {pv.Mark} proto the new prototype.
     6672 * @returns {pv.Mark} this mark.
     6673 * @see #add
     6674 */
     6675pv.Mark.prototype.extend = function(proto) {
     6676  this.proto = proto;
     6677  return this;
     6678};
     6679
     6680/**
     6681 * Adds a new mark of the specified type to the enclosing parent panel, whilst
     6682 * simultaneously setting the prototype of the new mark to be this mark.
     6683 *
     6684 * @param {function} type the type of mark to add; a constructor, such as
     6685 * <tt>pv.Bar</tt>.
     6686 * @returns {pv.Mark} the new mark.
     6687 * @see #extend
     6688 */
     6689pv.Mark.prototype.add = function(type) {
     6690  return this.parent.add(type).extend(this);
     6691};
     6692
     6693/**
     6694 * Defines a custom property on this mark. Custom properties are currently
     6695 * fixed, in that they are initialized once per mark set (i.e., per parent panel
     6696 * instance). Custom properties can be used to store local state for the mark,
     6697 * such as data needed by other properties (e.g., a custom scale) or interaction
     6698 * state.
     6699 *
     6700 * <p>WARNING We plan on changing this feature in a future release to define
     6701 * standard properties, as opposed to <i>fixed</i> properties that behave
     6702 * idiosyncratically within event handlers. Furthermore, we recommend storing
     6703 * state in an external data structure, rather than tying it to the
     6704 * visualization specification as with defs.
     6705 *
     6706 * @param {string} name the name of the local variable.
     6707 * @param {function} [v] an optional initializer; may be a constant or a
     6708 * function.
     6709 */
     6710pv.Mark.prototype.def = function(name, v) {
     6711  this.propertyMethod(name, true);
     6712  return this[name](arguments.length > 1 ? v : null);
     6713};
     6714
     6715/**
     6716 * Returns an anchor with the specified name. All marks support the five
     6717 * standard anchor names:<ul>
     6718 *
     6719 * <li>top
     6720 * <li>left
     6721 * <li>center
     6722 * <li>bottom
     6723 * <li>right
     6724 *
     6725 * </ul>In addition to positioning properties (left, right, top bottom), the
     6726 * anchors support text rendering properties (text-align, text-baseline). Text is
     6727 * rendered to appear inside the mark by default.
     6728 *
     6729 * <p>To facilitate stacking, anchors are defined in terms of their opposite
     6730 * edge. For example, the top anchor defines the bottom property, such that the
     6731 * mark extends upwards; the bottom anchor instead defines the top property,
     6732 * such that the mark extends downwards. See also {@link pv.Layout.Stack}.
     6733 *
     6734 * <p>While anchor names are typically constants, the anchor name is a true
     6735 * property, which means you can specify a function to compute the anchor name
     6736 * dynamically. See the {@link pv.Anchor#name} property for details.
     6737 *
     6738 * @param {string} name the anchor name; either a string or a property function.
     6739 * @returns {pv.Anchor} the new anchor.
     6740 */
     6741pv.Mark.prototype.anchor = function(name) {
     6742  var target = this, scene;
     6743
     6744  /* Default anchor name. */
     6745  if (!name) name = "center";
     6746
     6747  /** @private Find the instances of target that match source. */
     6748  function instances(source) {
     6749    var mark = target, index = [];
     6750
     6751    /* Mirrored descent. */
     6752    while (!(scene = mark.scene)) {
     6753      source = source.parent;
     6754      index.push({index: source.index, childIndex: mark.childIndex});
     6755      mark = mark.parent;
     6756    }
     6757    while (index.length) {
     6758      var i = index.pop();
     6759      scene = scene[i.index].children[i.childIndex];
     6760    }
     6761
     6762    /*
     6763     * When the anchor target is also an ancestor, as in the case of adding
     6764     * to a panel anchor, only generate one instance per panel. Also, set
     6765     * the margins to zero, since they are offset by the enclosing panel.
     6766     */
     6767    if (target.hasOwnProperty("index")) {
     6768      var s = pv.extend(scene[target.index]);
     6769      s.right = s.top = s.left = s.bottom = 0;
     6770      return [s];
     6771    }
     6772    return scene;
     6773  }
     6774
     6775  return new pv.Anchor(this)
     6776    .name(name)
     6777    .def("$mark.anchor", function() {
     6778        scene = this.scene.target = instances(this);
     6779      })
     6780    .data(function() {
     6781        return scene.map(function(s) { return s.data; });
     6782      })
     6783    .visible(function() {
     6784        return scene[this.index].visible;
     6785      })
     6786    .left(function() {
     6787        var s = scene[this.index], w = s.width || 0;
     6788        switch (this.name()) {
     6789          case "bottom":
     6790          case "top":
     6791          case "center": return s.left + w / 2;
     6792          case "left": return null;
     6793        }
     6794        return s.left + w;
     6795      })
     6796    .top(function() {
     6797        var s = scene[this.index], h = s.height || 0;
     6798        switch (this.name()) {
     6799          case "left":
     6800          case "right":
     6801          case "center": return s.top + h / 2;
     6802          case "top": return null;
     6803        }
     6804        return s.top + h;
     6805      })
     6806    .right(function() {
     6807        var s = scene[this.index];
     6808        return this.name() == "left" ? s.right + (s.width || 0) : null;
     6809      })
     6810    .bottom(function() {
     6811        var s = scene[this.index];
     6812        return this.name() == "top" ? s.bottom + (s.height || 0) : null;
     6813      })
     6814    .textAlign(function() {
     6815        switch (this.name()) {
     6816          case "bottom":
     6817          case "top":
     6818          case "center": return "center";
     6819          case "right": return "right";
     6820        }
     6821        return "left";
     6822      })
     6823    .textBaseline(function() {
     6824        switch (this.name()) {
     6825          case "right":
     6826          case "left":
     6827          case "center": return "middle";
     6828          case "top": return "top";
     6829        }
     6830        return "bottom";
     6831      });
     6832};
     6833
     6834/**
     6835 * Returns the anchor target of this mark, if it is derived from an anchor;
     6836 * otherwise returns null. For example, if a label is derived from a bar anchor,
     6837 *
     6838 * <pre>bar.anchor("top").add(pv.Label);</pre>
     6839 *
     6840 * then property functions on the label can refer to the bar via the
     6841 * <tt>anchorTarget</tt> method. This method is also useful for mark types
     6842 * defining properties on custom anchors.
     6843 *
     6844 * @returns {pv.Mark} the anchor target of this mark; possibly null.
     6845 */
     6846pv.Mark.prototype.anchorTarget = function() {
     6847  return this.proto.anchorTarget();
     6848};
     6849
     6850/**
     6851 * Alias for setting the left, right, top and bottom properties simultaneously.
     6852 *
     6853 * @see #left
     6854 * @see #right
     6855 * @see #top
     6856 * @see #bottom
     6857 * @returns {pv.Mark} this.
     6858 */
     6859pv.Mark.prototype.margin = function(n) {
     6860  return this.left(n).right(n).top(n).bottom(n);
     6861};
     6862
     6863/**
     6864 * @private Returns the current instance of this mark in the scene graph. This
     6865 * is typically equivalent to <tt>this.scene[this.index]</tt>, however if the
     6866 * scene or index is unset, the default instance of the mark is returned. If no
     6867 * default is set, the default is the last instance. Similarly, if the scene or
     6868 * index of the parent panel is unset, the default instance of this mark in the
     6869 * last instance of the enclosing panel is returned, and so on.
     6870 *
     6871 * @returns a node in the scene graph.
     6872 */
     6873pv.Mark.prototype.instance = function(defaultIndex) {
     6874  var scene = this.scene || this.parent.instance(-1).children[this.childIndex],
     6875      index = !arguments.length || this.hasOwnProperty("index") ? this.index : defaultIndex;
     6876  return scene[index < 0 ? scene.length - 1 : index];
     6877};
     6878
     6879/**
     6880 * @private Returns the first instance of this mark in the scene graph. This
     6881 * method can only be called when the mark is bound to the scene graph (for
     6882 * example, from an event handler, or within a property function).
     6883 *
     6884 * @returns a node in the scene graph.
     6885 */
     6886pv.Mark.prototype.first = function() {
     6887  return this.scene[0];
     6888};
     6889
     6890/**
     6891 * @private Returns the last instance of this mark in the scene graph. This
     6892 * method can only be called when the mark is bound to the scene graph (for
     6893 * example, from an event handler, or within a property function). In addition,
     6894 * note that mark instances are built sequentially, so the last instance of this
     6895 * mark may not yet be constructed.
     6896 *
     6897 * @returns a node in the scene graph.
     6898 */
     6899pv.Mark.prototype.last = function() {
     6900  return this.scene[this.scene.length - 1];
     6901};
     6902
     6903/**
     6904 * @private Returns the previous instance of this mark in the scene graph, or
     6905 * null if this is the first instance.
     6906 *
     6907 * @returns a node in the scene graph, or null.
     6908 */
     6909pv.Mark.prototype.sibling = function() {
     6910  return (this.index == 0) ? null : this.scene[this.index - 1];
     6911};
     6912
     6913/**
     6914 * @private Returns the current instance in the scene graph of this mark, in the
     6915 * previous instance of the enclosing parent panel. May return null if this
     6916 * instance could not be found.
     6917 *
     6918 * @returns a node in the scene graph, or null.
     6919 */
     6920pv.Mark.prototype.cousin = function() {
     6921  var p = this.parent, s = p && p.sibling();
     6922  return (s && s.children) ? s.children[this.childIndex][this.index] : null;
     6923};
     6924
     6925/**
     6926 * Renders this mark, including recursively rendering all child marks if this is
     6927 * a panel. This method finds all instances of this mark and renders them. This
     6928 * method descends recursively to the level of the mark to be rendered, finding
     6929 * all visible instances of the mark. After the marks are rendered, the scene
     6930 * and index attributes are removed from the mark to restore them to a clean
     6931 * state.
     6932 *
     6933 * <p>If an enclosing panel has an index property set (as is the case inside in
     6934 * an event handler), then only instances of this mark inside the given instance
     6935 * of the panel will be rendered; otherwise, all visible instances of the mark
     6936 * will be rendered.
     6937 */
     6938pv.Mark.prototype.render = function() {
     6939  var parent = this.parent,
     6940      stack = pv.Mark.stack;
     6941
     6942  /* For the first render, take it from the top. */
     6943  if (parent && !this.root.scene) {
     6944    this.root.render();
     6945    return;
     6946  }
     6947
     6948  /* Record the path to this mark. */
     6949  var indexes = [];
     6950  for (var mark = this; mark.parent; mark = mark.parent) {
     6951    indexes.unshift(mark.childIndex);
     6952  }
     6953
     6954  /** @private */
     6955  function render(mark, depth, scale) {
     6956    mark.scale = scale;
     6957    if (depth < indexes.length) {
     6958      stack.unshift(null);
     6959      if (mark.hasOwnProperty("index")) {
     6960        renderInstance(mark, depth, scale);
     6961      } else {
     6962        for (var i = 0, n = mark.scene.length; i < n; i++) {
     6963          mark.index = i;
     6964          renderInstance(mark, depth, scale);
     6965        }
     6966        delete mark.index;
     6967      }
     6968      stack.shift();
     6969    } else {
     6970      mark.build();
     6971
     6972      /*
     6973       * In the update phase, the scene is rendered by creating and updating
     6974       * elements and attributes in the SVG image. No properties are evaluated
     6975       * during the update phase; instead the values computed previously in the
     6976       * build phase are simply translated into SVG. The update phase is
     6977       * decoupled (see pv.Scene) to allow different rendering engines.
     6978       */
     6979      pv.Scene.scale = scale;
     6980      pv.Scene.updateAll(mark.scene);
     6981    }
     6982    delete mark.scale;
     6983  }
     6984
     6985  /**
     6986   * @private Recursively renders the current instance of the specified mark.
     6987   * This is slightly tricky because `index` and `scene` properties may or may
     6988   * not already be set; if they are set, it means we are rendering only a
     6989   * specific instance; if they are unset, we are rendering all instances.
     6990   * Furthermore, we must preserve the original context of these properties when
     6991   * rendering completes.
     6992   *
     6993   * <p>Another tricky aspect is that the `scene` attribute should be set for
     6994   * any preceding children, so as to allow property chaining. This is
     6995   * consistent with first-pass rendering.
     6996   */
     6997  function renderInstance(mark, depth, scale) {
     6998    var s = mark.scene[mark.index], i;
     6999    if (s.visible) {
     7000      var childIndex = indexes[depth],
     7001          child = mark.children[childIndex];
     7002
     7003      /* Set preceding child scenes. */
     7004      for (i = 0; i < childIndex; i++) {
     7005        mark.children[i].scene = s.children[i];
     7006      }
     7007
     7008      /* Set current child scene, if necessary. */
     7009      stack[0] = s.data;
     7010      if (child.scene) {
     7011        render(child, depth + 1, scale * s.transform.k);
     7012      } else {
     7013        child.scene = s.children[childIndex];
     7014        render(child, depth + 1, scale * s.transform.k);
     7015        delete child.scene;
     7016      }
     7017
     7018      /* Clear preceding child scenes. */
     7019      for (i = 0; i < childIndex; i++) {
     7020        delete mark.children[i].scene;
     7021      }
     7022    }
     7023  }
     7024
     7025  /* Bind this mark's property definitions. */
     7026  this.bind();
     7027
     7028  /* The render context is the first ancestor with an explicit index. */
     7029  while (parent && !parent.hasOwnProperty("index")) parent = parent.parent;
     7030
     7031  /* Recursively render all instances of this mark. */
     7032  this.context(
     7033      parent ? parent.scene : undefined,
     7034      parent ? parent.index : -1,
     7035      function() { render(this.root, 0, 1); });
     7036};
     7037
     7038/** @private Stores the current data stack. */
     7039pv.Mark.stack = [];
     7040
     7041/**
     7042 * @private In the bind phase, inherited property definitions are cached so they
     7043 * do not need to be queried during build.
     7044 */
     7045pv.Mark.prototype.bind = function() {
     7046  var seen = {}, types = [[], [], [], []], data, visible;
     7047
     7048  /** Scans the proto chain for the specified mark. */
     7049  function bind(mark) {
     7050    do {
     7051      var properties = mark.$properties;
     7052      for (var i = properties.length - 1; i >= 0 ; i--) {
     7053        var p = properties[i];
     7054        if (!(p.name in seen)) {
     7055          seen[p.name] = p;
     7056          switch (p.name) {
     7057            case "data": data = p; break;
     7058            case "visible": visible = p; break;
     7059            default: types[p.type].push(p); break;
     7060          }
     7061        }
     7062      }
     7063    } while (mark = mark.proto);
     7064  }
     7065
     7066  /* Scan the proto chain for all defined properties. */
     7067  bind(this);
     7068  bind(this.defaults);
     7069  types[1].reverse();
     7070  types[3].reverse();
     7071
     7072  /* Any undefined properties are null. */
     7073  var mark = this;
     7074  do for (var name in mark.properties) {
     7075    if (!(name in seen)) {
     7076      types[2].push(seen[name] = {name: name, type: 2, value: null});
     7077    }
     7078  } while (mark = mark.proto);
     7079
     7080  /* Define setter-getter for inherited defs. */
     7081  var defs = types[0].concat(types[1]);
     7082  for (var i = 0; i < defs.length; i++) {
     7083    this.propertyMethod(defs[i].name, true);
     7084  }
     7085
     7086  /* Setup binds to evaluate constants before functions. */
     7087  this.binds = {
     7088    properties: seen,
     7089    data: data,
     7090    defs: defs,
     7091    required: [visible],
     7092    optional: pv.blend(types)
     7093  };
     7094};
     7095
     7096/**
     7097 * @private Evaluates properties and computes implied properties. Properties are
     7098 * stored in the {@link #scene} array for each instance of this mark.
     7099 *
     7100 * <p>As marks are built recursively, the {@link #index} property is updated to
     7101 * match the current index into the data array for each mark. Note that the
     7102 * index property is only set for the mark currently being built and its
     7103 * enclosing parent panels. The index property for other marks is unset, but is
     7104 * inherited from the global <tt>Mark</tt> class prototype. This allows mark
     7105 * properties to refer to properties on other marks <i>in the same panel</i>
     7106 * conveniently; however, in general it is better to reference mark instances
     7107 * specifically through the scene graph rather than depending on the magical
     7108 * behavior of {@link #index}.
     7109 *
     7110 * <p>The root scene array has a special property, <tt>data</tt>, which stores
     7111 * the current data stack. The first element in this stack is the current datum,
     7112 * followed by the datum of the enclosing parent panel, and so on. The data
     7113 * stack should not be accessed directly; instead, property functions are passed
     7114 * the current data stack as arguments.
     7115 *
     7116 * <p>The evaluation of the <tt>data</tt> and <tt>visible</tt> properties is
     7117 * special. The <tt>data</tt> property is evaluated first; unlike the other
     7118 * properties, the data stack is from the parent panel, rather than the current
     7119 * mark, since the data is not defined until the data property is evaluated.
     7120 * The <tt>visisble</tt> property is subsequently evaluated for each instance;
     7121 * only if true will the {@link #buildInstance} method be called, evaluating
     7122 * other properties and recursively building the scene graph.
     7123 *
     7124 * <p>If this mark is being re-built, any old instances of this mark that no
     7125 * longer exist (because the new data array contains fewer elements) will be
     7126 * cleared using {@link #clearInstance}.
     7127 *
     7128 * @param parent the instance of the parent panel from the scene graph.
     7129 */
     7130pv.Mark.prototype.build = function() {
     7131  var scene = this.scene, stack = pv.Mark.stack;
     7132  if (!scene) {
     7133    scene = this.scene = [];
     7134    scene.mark = this;
     7135    scene.type = this.type;
     7136    scene.childIndex = this.childIndex;
     7137    if (this.parent) {
     7138      scene.parent = this.parent.scene;
     7139      scene.parentIndex = this.parent.index;
     7140    }
     7141  }
     7142
     7143  /* Evaluate defs. */
     7144  if (this.binds.defs.length) {
     7145    var defs = scene.defs;
     7146    if (!defs) scene.defs = defs = {};
     7147    for (var i = 0; i < this.binds.defs.length; i++) {
     7148      var p = this.binds.defs[i], d = defs[p.name];
     7149      if (!d || (p.id > d.id)) {
     7150        defs[p.name] = {
     7151          id: 0, // this def will be re-evaluated on next build
     7152          value: (p.type & 1) ? p.value.apply(this, stack) : p.value
     7153        };
     7154      }
     7155    }
     7156  }
     7157
     7158  /* Evaluate special data property. */
     7159  var data = this.binds.data;
     7160  data = data.type & 1 ? data.value.apply(this, stack) : data.value;
     7161
     7162  /* Create, update and delete scene nodes. */
     7163  stack.unshift(null);
     7164  scene.length = data.length;
     7165  for (var i = 0; i < data.length; i++) {
     7166    pv.Mark.prototype.index = this.index = i;
     7167    var s = scene[i];
     7168    if (!s) scene[i] = s = {};
     7169    s.data = stack[0] = data[i];
     7170    this.buildInstance(s);
     7171  }
     7172  pv.Mark.prototype.index = -1;
     7173  delete this.index;
     7174  stack.shift();
     7175
     7176  return this;
     7177};
     7178
     7179/**
     7180 * @private Evaluates the specified array of properties for the specified
     7181 * instance <tt>s</tt> in the scene graph.
     7182 *
     7183 * @param s a node in the scene graph; the instance of the mark to build.
     7184 * @param properties an array of properties.
     7185 */
     7186pv.Mark.prototype.buildProperties = function(s, properties) {
     7187  for (var i = 0, n = properties.length; i < n; i++) {
     7188    var p = properties[i], v = p.value; // assume case 2 (constant)
     7189    switch (p.type) {
     7190      case 0:
     7191      case 1: v = this.scene.defs[p.name].value; break;
     7192      case 3: v = v.apply(this, pv.Mark.stack); break;
     7193    }
     7194    s[p.name] = v;
     7195  }
     7196};
     7197
     7198/**
     7199 * @private Evaluates all of the properties for this mark for the specified
     7200 * instance <tt>s</tt> in the scene graph. The set of properties to evaluate is
     7201 * retrieved from the {@link #properties} array for this mark type (see {@link
     7202 * #type}).  After these properties are evaluated, any <b>implied</b> properties
     7203 * may be computed by the mark and set on the scene graph; see
     7204 * {@link #buildImplied}.
     7205 *
     7206 * <p>For panels, this method recursively builds the scene graph for all child
     7207 * marks as well. In general, this method should not need to be overridden by
     7208 * concrete mark types.
     7209 *
     7210 * @param s a node in the scene graph; the instance of the mark to build.
     7211 */
     7212pv.Mark.prototype.buildInstance = function(s) {
     7213  this.buildProperties(s, this.binds.required);
     7214  if (s.visible) {
     7215    this.buildProperties(s, this.binds.optional);
     7216    this.buildImplied(s);
     7217  }
     7218};
     7219
     7220/**
     7221 * @private Computes the implied properties for this mark for the specified
     7222 * instance <tt>s</tt> in the scene graph. Implied properties are those with
     7223 * dependencies on multiple other properties; for example, the width property
     7224 * may be implied if the left and right properties are set. This method can be
     7225 * overridden by concrete mark types to define new implied properties, if
     7226 * necessary.
     7227 *
     7228 * @param s a node in the scene graph; the instance of the mark to build.
     7229 */
     7230pv.Mark.prototype.buildImplied = function(s) {
     7231  var l = s.left;
     7232  var r = s.right;
     7233  var t = s.top;
     7234  var b = s.bottom;
     7235
     7236  /* Assume width and height are zero if not supported by this mark type. */
     7237  var p = this.properties;
     7238  var w = p.width ? s.width : 0;
     7239  var h = p.height ? s.height : 0;
     7240
     7241  /* Compute implied width, right and left. */
     7242  var width = this.parent ? this.parent.width() : (w + l + r);
     7243  if (w == null) {
     7244    w = width - (r = r || 0) - (l = l || 0);
     7245  } else if (r == null) {
     7246    r = width - w - (l = l || 0);
     7247  } else if (l == null) {
     7248    l = width - w - (r = r || 0);
     7249  }
     7250
     7251  /* Compute implied height, bottom and top. */
     7252  var height = this.parent ? this.parent.height() : (h + t + b);
     7253  if (h == null) {
     7254    h = height - (t = t || 0) - (b = b || 0);
     7255  } else if (b == null) {
     7256    b = height - h - (t = t || 0);
     7257  } else if (t == null) {
     7258    t = height - h - (b = b || 0);
     7259  }
     7260
     7261  s.left = l;
     7262  s.right = r;
     7263  s.top = t;
     7264  s.bottom = b;
     7265
     7266  /* Only set width and height if they are supported by this mark type. */
     7267  if (p.width) s.width = w;
     7268  if (p.height) s.height = h;
     7269
     7270  /* Set any null colors to pv.Color.transparent. */
     7271  if (p.textStyle && !s.textStyle) s.textStyle = pv.Color.transparent;
     7272  if (p.fillStyle && !s.fillStyle) s.fillStyle = pv.Color.transparent;
     7273  if (p.strokeStyle && !s.strokeStyle) s.strokeStyle = pv.Color.transparent;
     7274};
     7275
     7276/**
     7277 * Returns the current location of the mouse (cursor) relative to this mark's
     7278 * parent. The <i>x</i> coordinate corresponds to the left margin, while the
     7279 * <i>y</i> coordinate corresponds to the top margin.
     7280 *
     7281 * @returns {pv.Vector} the mouse location.
     7282 */
     7283pv.Mark.prototype.mouse = function() {
     7284
     7285  /* Compute xy-coordinates relative to the panel. */
     7286  var x = pv.event.pageX || 0,
     7287      y = pv.event.pageY || 0,
     7288      n = this.root.canvas();
     7289  do {
     7290    x -= n.offsetLeft;
     7291    y -= n.offsetTop;
     7292  } while (n = n.offsetParent);
     7293
     7294  /* Compute the inverse transform of all enclosing panels. */
     7295  var t = pv.Transform.identity,
     7296      p = this.properties.transform ? this : this.parent,
     7297      pz = [];
     7298  do { pz.push(p); } while (p = p.parent);
     7299  while (p = pz.pop()) t = t.translate(p.left(), p.top()).times(p.transform());
     7300  t = t.invert();
     7301
     7302  return pv.vector(x * t.k + t.x, y * t.k + t.y);
     7303};
     7304
     7305/**
     7306 * Registers an event handler for the specified event type with this mark. When
     7307 * an event of the specified type is triggered, the specified handler will be
     7308 * invoked. The handler is invoked in a similar method to property functions:
     7309 * the context is <tt>this</tt> mark instance, and the arguments are the full
     7310 * data stack. Event handlers can use property methods to manipulate the display
     7311 * properties of the mark:
     7312 *
     7313 * <pre>m.event("click", function() this.fillStyle("red"));</pre>
     7314 *
     7315 * Alternatively, the external data can be manipulated and the visualization
     7316 * redrawn:
     7317 *
     7318 * <pre>m.event("click", function(d) {
     7319 *     data = all.filter(function(k) k.name == d);
     7320 *     vis.render();
     7321 *   });</pre>
     7322 *
     7323 * The return value of the event handler determines which mark gets re-rendered.
     7324 * Use defs ({@link #def}) to set temporary state from event handlers.
     7325 *
     7326 * <p>The complete set of event types is defined by SVG; see the reference
     7327 * below. The set of supported event types is:<ul>
     7328 *
     7329 * <li>click
     7330 * <li>mousedown
     7331 * <li>mouseup
     7332 * <li>mouseover
     7333 * <li>mousemove
     7334 * <li>mouseout
     7335 *
     7336 * </ul>Since Protovis does not specify any concept of focus, it does not
     7337 * support key events; these should be handled outside the visualization using
     7338 * standard JavaScript. In the future, support for interaction may be extended
     7339 * to support additional event types, particularly those most relevant to
     7340 * interactive visualization, such as selection.
     7341 *
     7342 * <p>TODO In the current implementation, event handlers are not inherited from
     7343 * prototype marks. They must be defined explicitly on each interactive mark. In
     7344 * addition, only one event handler for a given event type can be defined; when
     7345 * specifying multiple event handlers for the same type, only the last one will
     7346 * be used.
     7347 *
     7348 * @see <a href="http://www.w3.org/TR/SVGTiny12/interact.html#SVGEvents">SVG events</a>
     7349 * @param {string} type the event type.
     7350 * @param {function} handler the event handler.
     7351 * @returns {pv.Mark} this.
     7352 */
     7353pv.Mark.prototype.event = function(type, handler) {
     7354  this.$handlers[type] = pv.functor(handler);
     7355  return this;
     7356};
     7357
     7358/** @private Evaluates the function <i>f</i> with the specified context. */
     7359pv.Mark.prototype.context = function(scene, index, f) {
     7360  var proto = pv.Mark.prototype,
     7361      stack = pv.Mark.stack,
     7362      oscene = pv.Mark.scene,
     7363      oindex = proto.index;
     7364
     7365  /** @private Sets the context. */
     7366  function apply(scene, index) {
     7367    pv.Mark.scene = scene;
     7368    proto.index = index;
     7369    if (!scene) return;
     7370
     7371    var that = scene.mark,
     7372        mark = that,
     7373        ancestors = [];
     7374
     7375    /* Set ancestors' scene and index; populate data stack. */
     7376    do {
     7377      ancestors.push(mark);
     7378      stack.push(scene[index].data);
     7379      mark.index = index;
     7380      mark.scene = scene;
     7381      index = scene.parentIndex;
     7382      scene = scene.parent;
     7383    } while (mark = mark.parent);
     7384
     7385    /* Set ancestors' scale; requires top-down. */
     7386    for (var i = ancestors.length - 1, k = 1; i > 0; i--) {
     7387      mark = ancestors[i];
     7388      mark.scale = k;
     7389      k *= mark.scene[mark.index].transform.k;
     7390    }
     7391
     7392    /* Set children's scene and scale. */
     7393    if (that.children) for (var i = 0, n = that.children.length; i < n; i++) {
     7394      mark = that.children[i];
     7395      mark.scene = that.scene[that.index].children[i];
     7396      mark.scale = k;
     7397    }
     7398  }
     7399
     7400  /** @private Clears the context. */
     7401  function clear(scene, index) {
     7402    if (!scene) return;
     7403    var that = scene.mark,
     7404        mark;
     7405
     7406    /* Reset children. */
     7407    if (that.children) for (var i = 0, n = that.children.length; i < n; i++) {
     7408      mark = that.children[i];
     7409      delete mark.scene;
     7410      delete mark.scale;
     7411    }
     7412
     7413    /* Reset ancestors. */
     7414    mark = that;
     7415    do {
     7416      stack.pop();
     7417      if (mark.parent) {
     7418        delete mark.scene;
     7419        delete mark.scale;
     7420      }
     7421      delete mark.index;
     7422    } while (mark = mark.parent);
     7423  }
     7424
     7425  /* Context switch, invoke the function, then switch back. */
     7426  clear(oscene, oindex);
     7427  apply(scene, index);
     7428  try {
     7429    f.apply(this, stack);
     7430  } finally {
     7431    clear(scene, index);
     7432    apply(oscene, oindex);
     7433  }
     7434};
     7435
     7436/** @private Execute the event listener, then re-render. */
     7437pv.Mark.dispatch = function(type, scene, index) {
     7438  var m = scene.mark, p = scene.parent, l = m.$handlers[type];
     7439  if (!l) return p && pv.Mark.dispatch(type, p, scene.parentIndex);
     7440  m.context(scene, index, function() {
     7441      m = l.apply(m, pv.Mark.stack);
     7442      if (m && m.render) m.render();
     7443    });
     7444  return true;
     7445};
     7446/**
     7447 * Constructs a new mark anchor with default properties.
     7448 *
     7449 * @class Represents an anchor on a given mark. An anchor is itself a mark, but
     7450 * without a visual representation. It serves only to provide useful default
     7451 * properties that can be inherited by other marks. Each type of mark can define
     7452 * any number of named anchors for convenience. If the concrete mark type does
     7453 * not define an anchor implementation specifically, one will be inherited from
     7454 * the mark's parent class.
     7455 *
     7456 * <p>For example, the bar mark provides anchors for its four sides: left,
     7457 * right, top and bottom. Adding a label to the top anchor of a bar,
     7458 *
     7459 * <pre>bar.anchor("top").add(pv.Label);</pre>
     7460 *
     7461 * will render a text label on the top edge of the bar; the top anchor defines
     7462 * the appropriate position properties (top and left), as well as text-rendering
     7463 * properties for convenience (textAlign and textBaseline).
     7464 *
     7465 * <p>Note that anchors do not <i>inherit</i> from their targets; the positional
     7466 * properties are copied from the scene graph, which guarantees that the anchors
     7467 * are positioned correctly, even if the positional properties are not defined
     7468 * deterministically. (In addition, it also improves performance by avoiding
     7469 * re-evaluating expensive properties.) If you want the anchor to inherit from
     7470 * the target, use {@link pv.Mark#extend} before adding. For example:
     7471 *
     7472 * <pre>bar.anchor("top").extend(bar).add(pv.Label);</pre>
     7473 *
     7474 * The anchor defines it's own positional properties, but other properties (such
     7475 * as the title property, say) can be inherited using the above idiom. Also note
     7476 * that you can override positional properties in the anchor for custom
     7477 * behavior.
     7478 *
     7479 * @extends pv.Mark
     7480 * @param {pv.Mark} target the anchor target.
     7481 */
     7482pv.Anchor = function(target) {
     7483  pv.Mark.call(this);
     7484  this.target = target;
     7485  this.parent = target.parent;
     7486};
     7487
     7488pv.Anchor.prototype = pv.extend(pv.Mark)
     7489    .property("name", String);
     7490
     7491/**
     7492 * The anchor name. The set of supported anchor names is dependent on the
     7493 * concrete mark type; see the mark type for details. For example, bars support
     7494 * left, right, top and bottom anchors.
     7495 *
     7496 * <p>While anchor names are typically constants, the anchor name is a true
     7497 * property, which means you can specify a function to compute the anchor name
     7498 * dynamically. For instance, if you wanted to alternate top and bottom anchors,
     7499 * saying
     7500 *
     7501 * <pre>m.anchor(function() (this.index % 2) ? "top" : "bottom").add(pv.Dot);</pre>
     7502 *
     7503 * would have the desired effect.
     7504 *
     7505 * @type string
     7506 * @name pv.Anchor.prototype.name
     7507 */
     7508
     7509/**
     7510 * Returns the anchor target of this mark, if it is derived from an anchor;
     7511 * otherwise returns null. For example, if a label is derived from a bar anchor,
     7512 *
     7513 * <pre>bar.anchor("top").add(pv.Label);</pre>
     7514 *
     7515 * then property functions on the label can refer to the bar via the
     7516 * <tt>anchorTarget</tt> method. This method is also useful for mark types
     7517 * defining properties on custom anchors.
     7518 *
     7519 * @returns {pv.Mark} the anchor target of this mark; possibly null.
     7520 */
     7521pv.Anchor.prototype.anchorTarget = function() {
     7522  return this.target;
     7523};
     7524/**
     7525 * Constructs a new area mark with default properties. Areas are not typically
     7526 * constructed directly, but by adding to a panel or an existing mark via
     7527 * {@link pv.Mark#add}.
     7528 *
     7529 * @class Represents an area mark: the solid area between two series of
     7530 * connected line segments. Unsurprisingly, areas are used most frequently for
     7531 * area charts.
     7532 *
     7533 * <p>Just as a line represents a polyline, the <tt>Area</tt> mark type
     7534 * represents a <i>polygon</i>. However, an area is not an arbitrary polygon;
     7535 * vertices are paired either horizontally or vertically into parallel
     7536 * <i>spans</i>, and each span corresponds to an associated datum. Either the
     7537 * width or the height must be specified, but not both; this determines whether
     7538 * the area is horizontally-oriented or vertically-oriented.  Like lines, areas
     7539 * can be stroked and filled with arbitrary colors.
     7540 *
     7541 * <p>See also the <a href="../../api/Area.html">Area guide</a>.
     7542 *
     7543 * @extends pv.Mark
     7544 */
     7545pv.Area = function() {
     7546  pv.Mark.call(this);
     7547};
     7548
     7549pv.Area.prototype = pv.extend(pv.Mark)
     7550    .property("width", Number)
     7551    .property("height", Number)
     7552    .property("lineWidth", Number)
     7553    .property("strokeStyle", pv.color)
     7554    .property("fillStyle", pv.color)
     7555    .property("segmented", Boolean)
     7556    .property("interpolate", String)
     7557    .property("tension", Number);
     7558
     7559pv.Area.prototype.type = "area";
     7560
     7561/**
     7562 * The width of a given span, in pixels; used for horizontal spans. If the width
     7563 * is specified, the height property should be 0 (the default). Either the top
     7564 * or bottom property should be used to space the spans vertically, typically as
     7565 * a multiple of the index.
     7566 *
     7567 * @type number
     7568 * @name pv.Area.prototype.width
     7569 */
     7570
     7571/**
     7572 * The height of a given span, in pixels; used for vertical spans. If the height
     7573 * is specified, the width property should be 0 (the default). Either the left
     7574 * or right property should be used to space the spans horizontally, typically
     7575 * as a multiple of the index.
     7576 *
     7577 * @type number
     7578 * @name pv.Area.prototype.height
     7579 */
     7580
     7581/**
     7582 * The width of stroked lines, in pixels; used in conjunction with
     7583 * <tt>strokeStyle</tt> to stroke the perimeter of the area. Unlike the
     7584 * {@link Line} mark type, the entire perimeter is stroked, rather than just one
     7585 * edge. The default value of this property is 1.5, but since the default stroke
     7586 * style is null, area marks are not stroked by default.
     7587 *
     7588 * <p>This property is <i>fixed</i> for non-segmented areas. See
     7589 * {@link pv.Mark}.
     7590 *
     7591 * @type number
     7592 * @name pv.Area.prototype.lineWidth
     7593 */
     7594
     7595/**
     7596 * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
     7597 * stroke the perimeter of the area. Unlike the {@link Line} mark type, the
     7598 * entire perimeter is stroked, rather than just one edge. The default value of
     7599 * this property is null, meaning areas are not stroked by default.
     7600 *
     7601 * <p>This property is <i>fixed</i> for non-segmented areas. See
     7602 * {@link pv.Mark}.
     7603 *
     7604 * @type string
     7605 * @name pv.Area.prototype.strokeStyle
     7606 * @see pv.color
     7607 */
     7608
     7609/**
     7610 * The area fill style; if non-null, the interior of the polygon forming the
     7611 * area is filled with the specified color. The default value of this property
     7612 * is a categorical color.
     7613 *
     7614 * <p>This property is <i>fixed</i> for non-segmented areas. See
     7615 * {@link pv.Mark}.
     7616 *
     7617 * @type string
     7618 * @name pv.Area.prototype.fillStyle
     7619 * @see pv.color
     7620 */
     7621
     7622/**
     7623 * Whether the area is segmented; whether variations in fill style, stroke
     7624 * style, and the other properties are treated as fixed. Rendering segmented
     7625 * areas is noticeably slower than non-segmented areas.
     7626 *
     7627 * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
     7628 *
     7629 * @type boolean
     7630 * @name pv.Area.prototype.segmented
     7631 */
     7632
     7633/**
     7634 * How to interpolate between values. Linear interpolation ("linear") is the
     7635 * default, producing a straight line between points. For piecewise constant
     7636 * functions (i.e., step functions), either "step-before" or "step-after" can be
     7637 * specified. To draw open uniform b-splines, specify "basis". To draw cardinal
     7638 * splines, specify "cardinal"; see also {@link #tension}.
     7639 *
     7640 * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
     7641 *
     7642 * @type string
     7643 * @name pv.Area.prototype.interpolate
     7644 */
     7645
     7646/**
     7647 * The tension of cardinal splines; used in conjunction with
     7648 * interpolate("cardinal"). A value between 0 and 1 draws cardinal splines with
     7649 * the given tension. In some sense, the tension can be interpreted as the
     7650 * "length" of the tangent; a tension of 1 will yield all zero tangents (i.e.,
     7651 * linear interpolation), and a tension of 0 yields a Catmull-Rom spline. The
     7652 * default value is 0.7.
     7653 *
     7654 * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
     7655 *
     7656 * @type number
     7657 * @name pv.Area.prototype.tension
     7658 */
     7659
     7660/**
     7661 * Default properties for areas. By default, there is no stroke and the fill
     7662 * style is a categorical color.
     7663 *
     7664 * @type pv.Area
     7665 */
     7666pv.Area.prototype.defaults = new pv.Area()
     7667    .extend(pv.Mark.prototype.defaults)
     7668    .lineWidth(1.5)
     7669    .fillStyle(pv.Colors.category20().by(pv.parent))
     7670    .interpolate("linear")
     7671    .tension(.7);
     7672
     7673/** @private Sets width and height to zero if null. */
     7674pv.Area.prototype.buildImplied = function(s) {
     7675  if (s.height == null) s.height = 0;
     7676  if (s.width == null) s.width = 0;
     7677  pv.Mark.prototype.buildImplied.call(this, s);
     7678};
     7679
     7680/** @private Records which properties may be fixed. */
     7681pv.Area.fixed = {
     7682  lineWidth: 1,
     7683  lineJoin: 1,
     7684  strokeStyle: 1,
     7685  fillStyle: 1,
     7686  segmented: 1,
     7687  interpolate: 1,
     7688  tension: 1
     7689};
     7690
     7691/**
     7692 * @private Make segmented required, such that this fixed property is always
     7693 * evaluated, even if the first segment is not visible. Also cache which
     7694 * properties are normally fixed.
     7695 */
     7696pv.Area.prototype.bind = function() {
     7697  pv.Mark.prototype.bind.call(this);
     7698  var binds = this.binds,
     7699      required = binds.required,
     7700      optional = binds.optional;
     7701  for (var i = 0, n = optional.length; i < n; i++) {
     7702    var p = optional[i];
     7703    p.fixed = p.name in pv.Area.fixed;
     7704    if (p.name == "segmented") {
     7705      required.push(p);
     7706      optional.splice(i, 1);
     7707      i--;
     7708      n--;
     7709    }
     7710  }
     7711
     7712  /* Cache the original arrays so they can be restored on build. */
     7713  this.binds.$required = required;
     7714  this.binds.$optional = optional;
     7715};
     7716
     7717/**
     7718 * @private Override the default build behavior such that fixed properties are
     7719 * determined dynamically, based on the value of the (always) fixed segmented
     7720 * property. Any fixed properties are only evaluated on the first instance,
     7721 * although their values are propagated to subsequent instances, so that they
     7722 * are available for property chaining and the like.
     7723 */
     7724pv.Area.prototype.buildInstance = function(s) {
     7725  var binds = this.binds;
     7726
     7727  /* Handle fixed properties on secondary instances. */
     7728  if (this.index) {
     7729    var fixed = binds.fixed;
     7730
     7731    /* Determine which properties are fixed. */
     7732    if (!fixed) {
     7733      fixed = binds.fixed = [];
     7734      function f(p) { return !p.fixed || (fixed.push(p), false); }
     7735      binds.required = binds.required.filter(f);
     7736      if (!this.scene[0].segmented) binds.optional = binds.optional.filter(f);
     7737    }
     7738
     7739    /* Copy fixed property values from the first instance. */
     7740    for (var i = 0, n = fixed.length; i < n; i++) {
     7741      var p = fixed[i].name;
     7742      s[p] = this.scene[0][p];
     7743    }
     7744  }
     7745
     7746  /* Evaluate all properties on the first instance. */
     7747  else {
     7748    binds.required = binds.$required;
     7749    binds.optional = binds.$optional;
     7750    binds.fixed = null;
     7751  }
     7752
     7753  pv.Mark.prototype.buildInstance.call(this, s);
     7754};
     7755
     7756/**
     7757 * Constructs a new area anchor with default properties. Areas support five
     7758 * different anchors:<ul>
     7759 *
     7760 * <li>top
     7761 * <li>left
     7762 * <li>center
     7763 * <li>bottom
     7764 * <li>right
     7765 *
     7766 * </ul>In addition to positioning properties (left, right, top bottom), the
     7767 * anchors support text rendering properties (text-align, text-baseline). Text
     7768 * is rendered to appear inside the area. The area anchor also propagates the
     7769 * interpolate, eccentricity, and tension properties such that an anchored area
     7770 * or line will match positions between control points.
     7771 *
     7772 * <p>For consistency with the other mark types, the anchor positions are
     7773 * defined in terms of their opposite edge. For example, the top anchor defines
     7774 * the bottom property, such that an area added to the top anchor grows upward.
     7775 *
     7776 * @param {string} name the anchor name; either a string or a property function.
     7777 * @returns {pv.Anchor}
     7778 */
     7779pv.Area.prototype.anchor = function(name) {
     7780  var scene;
     7781  return pv.Mark.prototype.anchor.call(this, name)
     7782    .def("$area.anchor", function() {
     7783        scene = this.scene.target;
     7784      })
     7785    .interpolate(function() {
     7786       return scene[this.index].interpolate;
     7787      })
     7788    .eccentricity(function() {
     7789       return scene[this.index].eccentricity;
     7790      })
     7791    .tension(function() {
     7792        return scene[this.index].tension;
     7793      });
     7794};
     7795/**
     7796 * Constructs a new bar mark with default properties. Bars are not typically
     7797 * constructed directly, but by adding to a panel or an existing mark via
     7798 * {@link pv.Mark#add}.
     7799 *
     7800 * @class Represents a bar: an axis-aligned rectangle that can be stroked and
     7801 * filled. Bars are used for many chart types, including bar charts, histograms
     7802 * and Gantt charts. Bars can also be used as decorations, for example to draw a
     7803 * frame border around a panel; in fact, a panel is a special type (a subclass)
     7804 * of bar.
     7805 *
     7806 * <p>Bars can be positioned in several ways. Most commonly, one of the four
     7807 * corners is fixed using two margins, and then the width and height properties
     7808 * determine the extent of the bar relative to this fixed location. For example,
     7809 * using the bottom and left properties fixes the bottom-left corner; the width
     7810 * then extends to the right, while the height extends to the top. As an
     7811 * alternative to the four corners, a bar can be positioned exclusively using
     7812 * margins; this is convenient as an inset from the containing panel, for
     7813 * example. See {@link pv.Mark} for details on the prioritization of redundant
     7814 * positioning properties.
     7815 *
     7816 * <p>See also the <a href="../../api/Bar.html">Bar guide</a>.
     7817 *
     7818 * @extends pv.Mark
     7819 */
     7820pv.Bar = function() {
     7821  pv.Mark.call(this);
     7822};
     7823
     7824pv.Bar.prototype = pv.extend(pv.Mark)
     7825    .property("width", Number)
     7826    .property("height", Number)
     7827    .property("lineWidth", Number)
     7828    .property("strokeStyle", pv.color)
     7829    .property("fillStyle", pv.color);
     7830
     7831pv.Bar.prototype.type = "bar";
     7832
     7833/**
     7834 * The width of the bar, in pixels. If the left position is specified, the bar
     7835 * extends rightward from the left edge; if the right position is specified, the
     7836 * bar extends leftward from the right edge.
     7837 *
     7838 * @type number
     7839 * @name pv.Bar.prototype.width
     7840 */
     7841
     7842/**
     7843 * The height of the bar, in pixels. If the bottom position is specified, the
     7844 * bar extends upward from the bottom edge; if the top position is specified,
     7845 * the bar extends downward from the top edge.
     7846 *
     7847 * @type number
     7848 * @name pv.Bar.prototype.height
     7849 */
     7850
     7851/**
     7852 * The width of stroked lines, in pixels; used in conjunction with
     7853 * <tt>strokeStyle</tt> to stroke the bar's border.
     7854 *
     7855 * @type number
     7856 * @name pv.Bar.prototype.lineWidth
     7857 */
     7858
     7859/**
     7860 * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
     7861 * stroke the bar's border. The default value of this property is null, meaning
     7862 * bars are not stroked by default.
     7863 *
     7864 * @type string
     7865 * @name pv.Bar.prototype.strokeStyle
     7866 * @see pv.color
     7867 */
     7868
     7869/**
     7870 * The bar fill style; if non-null, the interior of the bar is filled with the
     7871 * specified color. The default value of this property is a categorical color.
     7872 *
     7873 * @type string
     7874 * @name pv.Bar.prototype.fillStyle
     7875 * @see pv.color
     7876 */
     7877
     7878/**
     7879 * Default properties for bars. By default, there is no stroke and the fill
     7880 * style is a categorical color.
     7881 *
     7882 * @type pv.Bar
     7883 */
     7884pv.Bar.prototype.defaults = new pv.Bar()
     7885    .extend(pv.Mark.prototype.defaults)
     7886    .lineWidth(1.5)
     7887    .fillStyle(pv.Colors.category20().by(pv.parent));
     7888/**
     7889 * Constructs a new dot mark with default properties. Dots are not typically
     7890 * constructed directly, but by adding to a panel or an existing mark via
     7891 * {@link pv.Mark#add}.
     7892 *
     7893 * @class Represents a dot; a dot is simply a sized glyph centered at a given
     7894 * point that can also be stroked and filled. The <tt>size</tt> property is
     7895 * proportional to the area of the rendered glyph to encourage meaningful visual
     7896 * encodings. Dots can visually encode up to eight dimensions of data, though
     7897 * this may be unwise due to integrality. See {@link pv.Mark} for details on the
     7898 * prioritization of redundant positioning properties.
     7899 *
     7900 * <p>See also the <a href="../../api/Dot.html">Dot guide</a>.
     7901 *
     7902 * @extends pv.Mark
     7903 */
     7904pv.Dot = function() {
     7905  pv.Mark.call(this);
     7906};
     7907
     7908pv.Dot.prototype = pv.extend(pv.Mark)
     7909    .property("size", Number)
     7910    .property("radius", Number)
     7911    .property("shape", String)
     7912    .property("angle", Number)
     7913    .property("lineWidth", Number)
     7914    .property("strokeStyle", pv.color)
     7915    .property("fillStyle", pv.color);
     7916
     7917pv.Dot.prototype.type = "dot";
     7918
     7919/**
     7920 * The size of the dot, in square pixels. Square pixels are used such that the
     7921 * area of the dot is linearly proportional to the value of the size property,
     7922 * facilitating representative encodings.
     7923 *
     7924 * @see #radius
     7925 * @type number
     7926 * @name pv.Dot.prototype.size
     7927 */
     7928
     7929/**
     7930 * The radius of the dot, in pixels. This is an alternative to using
     7931 * {@link #size}.
     7932 *
     7933 * @see #size
     7934 * @type number
     7935 * @name pv.Dot.prototype.radius
     7936 */
     7937
     7938/**
     7939 * The shape name. Several shapes are supported:<ul>
     7940 *
     7941 * <li>cross
     7942 * <li>triangle
     7943 * <li>diamond
     7944 * <li>square
     7945 * <li>circle
     7946 * <li>tick
     7947 * <li>bar
     7948 *
     7949 * </ul>These shapes can be further changed using the {@link #angle} property;
     7950 * for instance, a cross can be turned into a plus by rotating. Similarly, the
     7951 * tick, which is vertical by default, can be rotated horizontally. Note that
     7952 * some shapes (cross and tick) do not have interior areas, and thus do not
     7953 * support fill style meaningfully.
     7954 *
     7955 * <p>Note: it may be more natural to use the {@link pv.Rule} mark for
     7956 * horizontal and vertical ticks. The tick shape is only necessary if angled
     7957 * ticks are needed.
     7958 *
     7959 * @type string
     7960 * @name pv.Dot.prototype.shape
     7961 */
     7962
     7963/**
     7964 * The rotation angle, in radians. Used to rotate shapes, such as to turn a
     7965 * cross into a plus.
     7966 *
     7967 * @type number
     7968 * @name pv.Dot.prototype.angle
     7969 */
     7970
     7971/**
     7972 * The width of stroked lines, in pixels; used in conjunction with
     7973 * <tt>strokeStyle</tt> to stroke the dot's shape.
     7974 *
     7975 * @type number
     7976 * @name pv.Dot.prototype.lineWidth
     7977 */
     7978
     7979/**
     7980 * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
     7981 * stroke the dot's shape. The default value of this property is a categorical
     7982 * color.
     7983 *
     7984 * @type string
     7985 * @name pv.Dot.prototype.strokeStyle
     7986 * @see pv.color
     7987 */
     7988
     7989/**
     7990 * The fill style; if non-null, the interior of the dot is filled with the
     7991 * specified color. The default value of this property is null, meaning dots are
     7992 * not filled by default.
     7993 *
     7994 * @type string
     7995 * @name pv.Dot.prototype.fillStyle
     7996 * @see pv.color
     7997 */
     7998
     7999/**
     8000 * Default properties for dots. By default, there is no fill and the stroke
     8001 * style is a categorical color. The default shape is "circle" with size 20.
     8002 *
     8003 * @type pv.Dot
     8004 */
     8005pv.Dot.prototype.defaults = new pv.Dot()
     8006    .extend(pv.Mark.prototype.defaults)
     8007    .size(20)
     8008    .shape("circle")
     8009    .lineWidth(1.5)
     8010    .strokeStyle(pv.Colors.category10().by(pv.parent));
     8011
     8012/**
     8013 * Constructs a new dot anchor with default properties. Dots support five
     8014 * different anchors:<ul>
     8015 *
     8016 * <li>top
     8017 * <li>left
     8018 * <li>center
     8019 * <li>bottom
     8020 * <li>right
     8021 *
     8022 * </ul>In addition to positioning properties (left, right, top bottom), the
     8023 * anchors support text rendering properties (text-align, text-baseline). Text is
     8024 * rendered to appear outside the dot. Note that this behavior is different from
     8025 * other mark anchors, which default to rendering text <i>inside</i> the mark.
     8026 *
     8027 * <p>For consistency with the other mark types, the anchor positions are
     8028 * defined in terms of their opposite edge. For example, the top anchor defines
     8029 * the bottom property, such that a bar added to the top anchor grows upward.
     8030 *
     8031 * @param {string} name the anchor name; either a string or a property function.
     8032 * @returns {pv.Anchor}
     8033 */
     8034pv.Dot.prototype.anchor = function(name) {
     8035  var scene;
     8036  return pv.Mark.prototype.anchor.call(this, name)
     8037    .def("$wedge.anchor", function() {
     8038        scene = this.scene.target;
     8039      })
     8040    .left(function() {
     8041        var s = scene[this.index];
     8042        switch (this.name()) {
     8043          case "bottom":
     8044          case "top":
     8045          case "center": return s.left;
     8046          case "left": return null;
     8047        }
     8048        return s.left + s.radius;
     8049      })
     8050    .right(function() {
     8051        var s = scene[this.index];
     8052        return this.name() == "left" ? s.right + s.radius : null;
     8053      })
     8054    .top(function() {
     8055        var s = scene[this.index];
     8056        switch (this.name()) {
     8057          case "left":
     8058          case "right":
     8059          case "center": return s.top;
     8060          case "top": return null;
     8061        }
     8062        return s.top + s.radius;
     8063      })
     8064    .bottom(function() {
     8065        var s = scene[this.index];
     8066        return this.name() == "top" ? s.bottom + s.radius : null;
     8067      })
     8068    .textAlign(function() {
     8069        switch (this.name()) {
     8070          case "left": return "right";
     8071          case "bottom":
     8072          case "top":
     8073          case "center": return "center";
     8074        }
     8075        return "left";
     8076      })
     8077    .textBaseline(function() {
     8078        switch (this.name()) {
     8079          case "right":
     8080          case "left":
     8081          case "center": return "middle";
     8082          case "bottom": return "top";
     8083        }
     8084        return "bottom";
     8085      });
     8086};
     8087
     8088/** @private Sets radius based on size or vice versa. */
     8089pv.Dot.prototype.buildImplied = function(s) {
     8090  if (s.radius == null) s.radius = Math.sqrt(s.size);
     8091  else if (s.size == null) s.size = s.radius * s.radius;
     8092  pv.Mark.prototype.buildImplied.call(this, s);
     8093};
     8094/**
     8095 * Constructs a new label mark with default properties. Labels are not typically
     8096 * constructed directly, but by adding to a panel or an existing mark via
     8097 * {@link pv.Mark#add}.
     8098 *
     8099 * @class Represents a text label, allowing textual annotation of other marks or
     8100 * arbitrary text within the visualization. The character data must be plain
     8101 * text (unicode), though the text can be styled using the {@link #font}
     8102 * property. If rich text is needed, external HTML elements can be overlaid on
     8103 * the canvas by hand.
     8104 *
     8105 * <p>Labels are positioned using the box model, similarly to {@link Dot}. Thus,
     8106 * a label has no width or height, but merely a text anchor location. The text
     8107 * is positioned relative to this anchor location based on the
     8108 * {@link #textAlign}, {@link #textBaseline} and {@link #textMargin} properties.
     8109 * Furthermore, the text may be rotated using {@link #textAngle}.
     8110 *
     8111 * <p>Labels ignore events, so as to not interfere with event handlers on
     8112 * underlying marks, such as bars. In the future, we may support event handlers
     8113 * on labels.
     8114 *
     8115 * <p>See also the <a href="../../api/Label.html">Label guide</a>.
     8116 *
     8117 * @extends pv.Mark
     8118 */
     8119pv.Label = function() {
     8120  pv.Mark.call(this);
     8121};
     8122
     8123pv.Label.prototype = pv.extend(pv.Mark)
     8124    .property("text", String)
     8125    .property("font", String)
     8126    .property("textAngle", Number)
     8127    .property("textStyle", pv.color)
     8128    .property("textAlign", String)
     8129    .property("textBaseline", String)
     8130    .property("textMargin", Number)
     8131    .property("textDecoration", String)
     8132    .property("textShadow", String);
     8133
     8134pv.Label.prototype.type = "label";
     8135
     8136/**
     8137 * The character data to render; a string. The default value of the text
     8138 * property is the identity function, meaning the label's associated datum will
     8139 * be rendered using its <tt>toString</tt>.
     8140 *
     8141 * @type string
     8142 * @name pv.Label.prototype.text
     8143 */
     8144
     8145/**
     8146 * The font format, per the CSS Level 2 specification. The default font is "10px
     8147 * sans-serif", for consistency with the HTML 5 canvas element specification.
     8148 * Note that since text is not wrapped, any line-height property will be
     8149 * ignored. The other font-style, font-variant, font-weight, font-size and
     8150 * font-family properties are supported.
     8151 *
     8152 * @see <a href="http://www.w3.org/TR/CSS2/fonts.html#font-shorthand">CSS2 fonts</a>
     8153 * @type string
     8154 * @name pv.Label.prototype.font
     8155 */
     8156
     8157/**
     8158 * The rotation angle, in radians. Text is rotated clockwise relative to the
     8159 * anchor location. For example, with the default left alignment, an angle of
     8160 * Math.PI / 2 causes text to proceed downwards. The default angle is zero.
     8161 *
     8162 * @type number
     8163 * @name pv.Label.prototype.textAngle
     8164 */
     8165
     8166/**
     8167 * The text color. The name "textStyle" is used for consistency with "fillStyle"
     8168 * and "strokeStyle", although it might be better to rename this property (and
     8169 * perhaps use the same name as "strokeStyle"). The default color is black.
     8170 *
     8171 * @type string
     8172 * @name pv.Label.prototype.textStyle
     8173 * @see pv.color
     8174 */
     8175
     8176/**
     8177 * The horizontal text alignment. One of:<ul>
     8178 *
     8179 * <li>left
     8180 * <li>center
     8181 * <li>right
     8182 *
     8183 * </ul>The default horizontal alignment is left.
     8184 *
     8185 * @type string
     8186 * @name pv.Label.prototype.textAlign
     8187 */
     8188
     8189/**
     8190 * The vertical text alignment. One of:<ul>
     8191 *
     8192 * <li>top
     8193 * <li>middle
     8194 * <li>bottom
     8195 *
     8196 * </ul>The default vertical alignment is bottom.
     8197 *
     8198 * @type string
     8199 * @name pv.Label.prototype.textBaseline
     8200 */
     8201
     8202/**
     8203 * The text margin; may be specified in pixels, or in font-dependent units (such
     8204 * as ".1ex"). The margin can be used to pad text away from its anchor location,
     8205 * in a direction dependent on the horizontal and vertical alignment
     8206 * properties. For example, if the text is left- and middle-aligned, the margin
     8207 * shifts the text to the right. The default margin is 3 pixels.
     8208 *
     8209 * @type number
     8210 * @name pv.Label.prototype.textMargin
     8211 */
     8212
     8213/**
     8214 * A list of shadow effects to be applied to text, per the CSS Text Level 3
     8215 * text-shadow property. An example specification is "0.1em 0.1em 0.1em
     8216 * rgba(0,0,0,.5)"; the first length is the horizontal offset, the second the
     8217 * vertical offset, and the third the blur radius.
     8218 *
     8219 * @see <a href="http://www.w3.org/TR/css3-text/#text-shadow">CSS3 text</a>
     8220 * @type string
     8221 * @name pv.Label.prototype.textShadow
     8222 */
     8223
     8224/**
     8225 * A list of decoration to be applied to text, per the CSS Text Level 3
     8226 * text-decoration property. An example specification is "underline".
     8227 *
     8228 * @see <a href="http://www.w3.org/TR/css3-text/#text-decoration">CSS3 text</a>
     8229 * @type string
     8230 * @name pv.Label.prototype.textDecoration
     8231 */
     8232
     8233/**
     8234 * Default properties for labels. See the individual properties for the default
     8235 * values.
     8236 *
     8237 * @type pv.Label
     8238 */
     8239pv.Label.prototype.defaults = new pv.Label()
     8240    .extend(pv.Mark.prototype.defaults)
     8241    .events("none")
     8242    .text(pv.identity)
     8243    .font("10px sans-serif")
     8244    .textAngle(0)
     8245    .textStyle("black")
     8246    .textAlign("left")
     8247    .textBaseline("bottom")
     8248    .textMargin(3);
     8249/**
     8250 * Constructs a new line mark with default properties. Lines are not typically
     8251 * constructed directly, but by adding to a panel or an existing mark via
     8252 * {@link pv.Mark#add}.
     8253 *
     8254 * @class Represents a series of connected line segments, or <i>polyline</i>,
     8255 * that can be stroked with a configurable color and thickness. Each
     8256 * articulation point in the line corresponds to a datum; for <i>n</i> points,
     8257 * <i>n</i>-1 connected line segments are drawn. The point is positioned using
     8258 * the box model. Arbitrary paths are also possible, allowing radar plots and
     8259 * other custom visualizations.
     8260 *
     8261 * <p>Like areas, lines can be stroked and filled with arbitrary colors. In most
     8262 * cases, lines are only stroked, but the fill style can be used to construct
     8263 * arbitrary polygons.
     8264 *
     8265 * <p>See also the <a href="../../api/Line.html">Line guide</a>.
     8266 *
     8267 * @extends pv.Mark
     8268 */
     8269pv.Line = function() {
     8270  pv.Mark.call(this);
     8271};
     8272
     8273pv.Line.prototype = pv.extend(pv.Mark)
     8274    .property("lineWidth", Number)
     8275    .property("lineJoin", String)
     8276    .property("strokeStyle", pv.color)
     8277    .property("fillStyle", pv.color)
     8278    .property("segmented", Boolean)
     8279    .property("interpolate", String)
     8280    .property("eccentricity", Number)
     8281    .property("tension", Number);
     8282
     8283pv.Line.prototype.type = "line";
     8284
     8285/**
     8286 * The width of stroked lines, in pixels; used in conjunction with
     8287 * <tt>strokeStyle</tt> to stroke the line.
     8288 *
     8289 * @type number
     8290 * @name pv.Line.prototype.lineWidth
     8291 */
     8292
     8293/**
     8294 * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
     8295 * stroke the line. The default value of this property is a categorical color.
     8296 *
     8297 * @type string
     8298 * @name pv.Line.prototype.strokeStyle
     8299 * @see pv.color
     8300 */
     8301
     8302/**
     8303 * The type of corners where two lines meet. Accepted values are "bevel",
     8304 * "round" and "miter". The default value is "miter".
     8305 *
     8306 * <p>For segmented lines, only "miter" joins and "linear" interpolation are
     8307 * currently supported. Any other value, including null, will disable joins,
     8308 * producing disjoint line segments. Note that the miter joins must be computed
     8309 * manually (at least in the current SVG renderer); since this calculation may
     8310 * be expensive and unnecessary for small lines, specifying null can improve
     8311 * performance significantly.
     8312 *
     8313 * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
     8314 *
     8315 * @type string
     8316 * @name pv.Line.prototype.lineJoin
     8317 */
     8318
     8319/**
     8320 * The line fill style; if non-null, the interior of the line is closed and
     8321 * filled with the specified color. The default value of this property is a
     8322 * null, meaning that lines are not filled by default.
     8323 *
     8324 * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
     8325 *
     8326 * @type string
     8327 * @name pv.Line.prototype.fillStyle
     8328 * @see pv.color
     8329 */
     8330
     8331/**
     8332 * Whether the line is segmented; whether variations in stroke style, line width
     8333 * and the other properties are treated as fixed. Rendering segmented lines is
     8334 * noticeably slower than non-segmented lines.
     8335 *
     8336 * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
     8337 *
     8338 * @type boolean
     8339 * @name pv.Line.prototype.segmented
     8340 */
     8341
     8342/**
     8343 * How to interpolate between values. Linear interpolation ("linear") is the
     8344 * default, producing a straight line between points. For piecewise constant
     8345 * functions (i.e., step functions), either "step-before" or "step-after" can be
     8346 * specified. To draw a clockwise circular arc between points, specify "polar";
     8347 * to draw a counterclockwise circular arc between points, specify
     8348 * "polar-reverse". To draw open uniform b-splines, specify "basis". To draw
     8349 * cardinal splines, specify "cardinal"; see also {@link #tension}.
     8350 *
     8351 * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
     8352 *
     8353 * @type string
     8354 * @name pv.Line.prototype.interpolate
     8355 */
     8356
     8357/**
     8358 * The eccentricity of polar line segments; used in conjunction with
     8359 * interpolate("polar"). The default value of 0 means that line segments are
     8360 * drawn as circular arcs. A value of 1 draws a straight line. A value between 0
     8361 * and 1 draws an elliptical arc with the given eccentricity.
     8362 *
     8363 * @type number
     8364 * @name pv.Line.prototype.eccentricity
     8365 */
     8366
     8367/**
     8368 * The tension of cardinal splines; used in conjunction with
     8369 * interpolate("cardinal"). A value between 0 and 1 draws cardinal splines with
     8370 * the given tension. In some sense, the tension can be interpreted as the
     8371 * "length" of the tangent; a tension of 1 will yield all zero tangents (i.e.,
     8372 * linear interpolation), and a tension of 0 yields a Catmull-Rom spline. The
     8373 * default value is 0.7.
     8374 *
     8375 * <p>This property is <i>fixed</i>. See {@link pv.Mark}.
     8376 *
     8377 * @type number
     8378 * @name pv.Line.prototype.tension
     8379 */
     8380
     8381/**
     8382 * Default properties for lines. By default, there is no fill and the stroke
     8383 * style is a categorical color. The default interpolation is linear.
     8384 *
     8385 * @type pv.Line
     8386 */
     8387pv.Line.prototype.defaults = new pv.Line()
     8388    .extend(pv.Mark.prototype.defaults)
     8389    .lineJoin("miter")
     8390    .lineWidth(1.5)
     8391    .strokeStyle(pv.Colors.category10().by(pv.parent))
     8392    .interpolate("linear")
     8393    .eccentricity(0)
     8394    .tension(.7);
     8395
     8396/** @private Reuse Area's implementation for segmented bind & build. */
     8397pv.Line.prototype.bind = pv.Area.prototype.bind;
     8398pv.Line.prototype.buildInstance = pv.Area.prototype.buildInstance;
     8399
     8400/**
     8401 * Constructs a new line anchor with default properties. Lines support five
     8402 * different anchors:<ul>
     8403 *
     8404 * <li>top
     8405 * <li>left
     8406 * <li>center
     8407 * <li>bottom
     8408 * <li>right
     8409 *
     8410 * </ul>In addition to positioning properties (left, right, top bottom), the
     8411 * anchors support text rendering properties (text-align, text-baseline). Text is
     8412 * rendered to appear outside the line. Note that this behavior is different
     8413 * from other mark anchors, which default to rendering text <i>inside</i> the
     8414 * mark.
     8415 *
     8416 * <p>For consistency with the other mark types, the anchor positions are
     8417 * defined in terms of their opposite edge. For example, the top anchor defines
     8418 * the bottom property, such that a bar added to the top anchor grows upward.
     8419 *
     8420 * @param {string} name the anchor name; either a string or a property function.
     8421 * @returns {pv.Anchor}
     8422 */
     8423pv.Line.prototype.anchor = function(name) {
     8424  return pv.Area.prototype.anchor.call(this, name)
     8425    .textAlign(function(d) {
     8426        switch (this.name()) {
     8427          case "left": return "right";
     8428          case "bottom":
     8429          case "top":
     8430          case "center": return "center";
     8431          case "right": return "left";
     8432        }
     8433      })
     8434    .textBaseline(function(d) {
     8435        switch (this.name()) {
     8436          case "right":
     8437          case "left":
     8438          case "center": return "middle";
     8439          case "top": return "bottom";
     8440          case "bottom": return "top";
     8441        }
     8442      });
     8443};
     8444/**
     8445 * Constructs a new rule with default properties. Rules are not typically
     8446 * constructed directly, but by adding to a panel or an existing mark via
     8447 * {@link pv.Mark#add}.
     8448 *
     8449 * @class Represents a horizontal or vertical rule. Rules are frequently used
     8450 * for axes and grid lines. For example, specifying only the bottom property
     8451 * draws horizontal rules, while specifying only the left draws vertical
     8452 * rules. Rules can also be used as thin bars. The visual style is controlled in
     8453 * the same manner as lines.
     8454 *
     8455 * <p>Rules are positioned exclusively the standard box model properties. The
     8456 * following combinations of properties are supported:
     8457 *
     8458 * <table>
     8459 * <thead><th style="width:12em;">Properties</th><th>Orientation</th></thead>
     8460 * <tbody>
     8461 * <tr><td>left</td><td>vertical</td></tr>
     8462 * <tr><td>right</td><td>vertical</td></tr>
     8463 * <tr><td>left, bottom, top</td><td>vertical</td></tr>
     8464 * <tr><td>right, bottom, top</td><td>vertical</td></tr>
     8465 * <tr><td>top</td><td>horizontal</td></tr>
     8466 * <tr><td>bottom</td><td>horizontal</td></tr>
     8467 * <tr><td>top, left, right</td><td>horizontal</td></tr>
     8468 * <tr><td>bottom, left, right</td><td>horizontal</td></tr>
     8469 * <tr><td>left, top, height</td><td>vertical</td></tr>
     8470 * <tr><td>left, bottom, height</td><td>vertical</td></tr>
     8471 * <tr><td>right, top, height</td><td>vertical</td></tr>
     8472 * <tr><td>right, bottom, height</td><td>vertical</td></tr>
     8473 * <tr><td>left, top, width</td><td>horizontal</td></tr>
     8474 * <tr><td>left, bottom, width</td><td>horizontal</td></tr>
     8475 * <tr><td>right, top, width</td><td>horizontal</td></tr>
     8476 * <tr><td>right, bottom, width</td><td>horizontal</td></tr>
     8477 * </tbody>
     8478 * </table>
     8479 *
     8480 * <p>Small rules can be used as tick marks; alternatively, a {@link Dot} with
     8481 * the "tick" shape can be used.
     8482 *
     8483 * <p>See also the <a href="../../api/Rule.html">Rule guide</a>.
     8484 *
     8485 * @see pv.Line
     8486 * @extends pv.Mark
     8487 */
     8488pv.Rule = function() {
     8489  pv.Mark.call(this);
     8490};
     8491
     8492pv.Rule.prototype = pv.extend(pv.Mark)
     8493    .property("width", Number)
     8494    .property("height", Number)
     8495    .property("lineWidth", Number)
     8496    .property("strokeStyle", pv.color);
     8497
     8498pv.Rule.prototype.type = "rule";
     8499
     8500/**
     8501 * The width of the rule, in pixels. If the left position is specified, the rule
     8502 * extends rightward from the left edge; if the right position is specified, the
     8503 * rule extends leftward from the right edge.
     8504 *
     8505 * @type number
     8506 * @name pv.Rule.prototype.width
     8507 */
     8508
     8509/**
     8510 * The height of the rule, in pixels. If the bottom position is specified, the
     8511 * rule extends upward from the bottom edge; if the top position is specified,
     8512 * the rule extends downward from the top edge.
     8513 *
     8514 * @type number
     8515 * @name pv.Rule.prototype.height
     8516 */
     8517
     8518/**
     8519 * The width of stroked lines, in pixels; used in conjunction with
     8520 * <tt>strokeStyle</tt> to stroke the rule. The default value is 1 pixel.
     8521 *
     8522 * @type number
     8523 * @name pv.Rule.prototype.lineWidth
     8524 */
     8525
     8526/**
     8527 * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
     8528 * stroke the rule. The default value of this property is black.
     8529 *
     8530 * @type string
     8531 * @name pv.Rule.prototype.strokeStyle
     8532 * @see pv.color
     8533 */
     8534
     8535/**
     8536 * Default properties for rules. By default, a single-pixel black line is
     8537 * stroked.
     8538 *
     8539 * @type pv.Rule
     8540 */
     8541pv.Rule.prototype.defaults = new pv.Rule()
     8542    .extend(pv.Mark.prototype.defaults)
     8543    .lineWidth(1)
     8544    .strokeStyle("black")
     8545    .antialias(false);
     8546
     8547/**
     8548 * Constructs a new rule anchor with default properties. Rules support five
     8549 * different anchors:<ul>
     8550 *
     8551 * <li>top
     8552 * <li>left
     8553 * <li>center
     8554 * <li>bottom
     8555 * <li>right
     8556 *
     8557 * </ul>In addition to positioning properties (left, right, top bottom), the
     8558 * anchors support text rendering properties (text-align, text-baseline). Text is
     8559 * rendered to appear outside the rule. Note that this behavior is different
     8560 * from other mark anchors, which default to rendering text <i>inside</i> the
     8561 * mark.
     8562 *
     8563 * <p>For consistency with the other mark types, the anchor positions are
     8564 * defined in terms of their opposite edge. For example, the top anchor defines
     8565 * the bottom property, such that a bar added to the top anchor grows upward.
     8566 *
     8567 * @param {string} name the anchor name; either a string or a property function.
     8568 * @returns {pv.Anchor}
     8569 */
     8570pv.Rule.prototype.anchor = pv.Line.prototype.anchor;
     8571
     8572/** @private Sets width or height based on orientation. */
     8573pv.Rule.prototype.buildImplied = function(s) {
     8574  var l = s.left, r = s.right, t = s.top, b = s.bottom;
     8575
     8576  /* Determine horizontal or vertical orientation. */
     8577  if ((s.width != null)
     8578      || ((l == null) && (r == null))
     8579      || ((r != null) && (l != null))) {
     8580    s.height = 0;
     8581  } else {
     8582    s.width = 0;
     8583  }
     8584
     8585  pv.Mark.prototype.buildImplied.call(this, s);
     8586};
     8587/**
     8588 * Constructs a new, empty panel with default properties. Panels, with the
     8589 * exception of the root panel, are not typically constructed directly; instead,
     8590 * they are added to an existing panel or mark via {@link pv.Mark#add}.
     8591 *
     8592 * @class Represents a container mark. Panels allow repeated or nested
     8593 * structures, commonly used in small multiple displays where a small
     8594 * visualization is tiled to facilitate comparison across one or more
     8595 * dimensions. Other types of visualizations may benefit from repeated and
     8596 * possibly overlapping structure as well, such as stacked area charts. Panels
     8597 * can also offset the position of marks to provide padding from surrounding
     8598 * content.
     8599 *
     8600 * <p>All Protovis displays have at least one panel; this is the root panel to
     8601 * which marks are rendered. The box model properties (four margins, width and
     8602 * height) are used to offset the positions of contained marks. The data
     8603 * property determines the panel count: a panel is generated once per associated
     8604 * datum. When nested panels are used, property functions can declare additional
     8605 * arguments to access the data associated with enclosing panels.
     8606 *
     8607 * <p>Panels can be rendered inline, facilitating the creation of sparklines.
     8608 * This allows designers to reuse browser layout features, such as text flow and
     8609 * tables; designers can also overlay HTML elements such as rich text and
     8610 * images.
     8611 *
     8612 * <p>All panels have a <tt>children</tt> array (possibly empty) containing the
     8613 * child marks in the order they were added. Panels also have a <tt>root</tt>
     8614 * field which points to the root (outermost) panel; the root panel's root field
     8615 * points to itself.
     8616 *
     8617 * <p>See also the <a href="../../api/">Protovis guide</a>.
     8618 *
     8619 * @extends pv.Bar
     8620 */
     8621pv.Panel = function() {
     8622  pv.Bar.call(this);
     8623
     8624  /**
     8625   * The child marks; zero or more {@link pv.Mark}s in the order they were
     8626   * added.
     8627   *
     8628   * @see #add
     8629   * @type pv.Mark[]
     8630   */
     8631  this.children = [];
     8632  this.root = this;
     8633
     8634  /**
     8635   * The internal $dom field is set by the Protovis loader; see lang/init.js. It
     8636   * refers to the script element that contains the Protovis specification, so
     8637   * that the panel knows where in the DOM to insert the generated SVG element.
     8638   *
     8639   * @private
     8640   */
     8641  this.$dom = pv.$ && pv.$.s;
     8642};
     8643
     8644pv.Panel.prototype = pv.extend(pv.Bar)
     8645    .property("transform")
     8646    .property("overflow", String)
     8647    .property("canvas", function(c) {
     8648        return (typeof c == "string")
     8649            ? document.getElementById(c)
     8650            : c; // assume that c is the passed-in element
     8651      });
     8652
     8653pv.Panel.prototype.type = "panel";
     8654
     8655/**
     8656 * The canvas element; either the string ID of the canvas element in the current
     8657 * document, or a reference to the canvas element itself. If null, a canvas
     8658 * element will be created and inserted into the document at the location of the
     8659 * script element containing the current Protovis specification. This property
     8660 * only applies to root panels and is ignored on nested panels.
     8661 *
     8662 * <p>Note: the "canvas" element here refers to a <tt>div</tt> (or other suitable
     8663 * HTML container element), <i>not</i> a <tt>canvas</tt> element. The name of
     8664 * this property is a historical anachronism from the first implementation that
     8665 * used HTML 5 canvas, rather than SVG.
     8666 *
     8667 * @type string
     8668 * @name pv.Panel.prototype.canvas
     8669 */
     8670
     8671/**
     8672 * Specifies whether child marks are clipped when they overflow this panel.
     8673 * This affects the clipping of all this panel's descendant marks.
     8674 *
     8675 * @type string
     8676 * @name pv.Panel.prototype.overflow
     8677 * @see <a href="http://www.w3.org/TR/CSS2/visufx.html#overflow">CSS2</a>
     8678 */
     8679
     8680/**
     8681 * The transform to be applied to child marks. The default transform is
     8682 * identity, which has no effect. Note that the panel's own fill and stroke are
     8683 * not affected by the transform, and panel's transform only affects the
     8684 * <tt>scale</tt> of child marks, not the panel itself.
     8685 *
     8686 * @type pv.Transform
     8687 * @name pv.Panel.prototype.transform
     8688 * @see pv.Mark#scale
     8689 */
     8690
     8691/**
     8692 * Default properties for panels. By default, the margins are zero, the fill
     8693 * style is transparent.
     8694 *
     8695 * @type pv.Panel
     8696 */
     8697pv.Panel.prototype.defaults = new pv.Panel()
     8698    .extend(pv.Bar.prototype.defaults)
     8699    .fillStyle(null) // override Bar default
     8700    .overflow("visible");
     8701
     8702/**
     8703 * Returns an anchor with the specified name. This method is overridden such
     8704 * that adding to a panel's anchor adds to the panel, rather than to the panel's
     8705 * parent.
     8706 *
     8707 * @param {string} name the anchor name; either a string or a property function.
     8708 * @returns {pv.Anchor} the new anchor.
     8709 */
     8710pv.Panel.prototype.anchor = function(name) {
     8711  var anchor = pv.Bar.prototype.anchor.call(this, name);
     8712  anchor.parent = this;
     8713  return anchor;
     8714};
     8715
     8716/**
     8717 * Adds a new mark of the specified type to this panel. Unlike the normal
     8718 * {@link Mark#add} behavior, adding a mark to a panel does not cause the mark
     8719 * to inherit from the panel. Since the contained marks are offset by the panel
     8720 * margins already, inheriting properties is generally undesirable; of course,
     8721 * it is always possible to change this behavior by calling {@link Mark#extend}
     8722 * explicitly.
     8723 *
     8724 * @param {function} type the type of the new mark to add.
     8725 * @returns {pv.Mark} the new mark.
     8726 */
     8727pv.Panel.prototype.add = function(type) {
     8728  var child = new type();
     8729  child.parent = this;
     8730  child.root = this.root;
     8731  child.childIndex = this.children.length;
     8732  this.children.push(child);
     8733  return child;
     8734};
     8735
     8736/** @private Bind this panel, then any child marks recursively. */
     8737pv.Panel.prototype.bind = function() {
     8738  pv.Mark.prototype.bind.call(this);
     8739  for (var i = 0; i < this.children.length; i++) {
     8740    this.children[i].bind();
     8741  }
     8742};
     8743
     8744/**
     8745 * @private Evaluates all of the properties for this panel for the specified
     8746 * instance <tt>s</tt> in the scene graph, including recursively building the
     8747 * scene graph for child marks.
     8748 *
     8749 * @param s a node in the scene graph; the instance of the panel to build.
     8750 * @see Mark#scene
     8751 */
     8752pv.Panel.prototype.buildInstance = function(s) {
     8753  pv.Bar.prototype.buildInstance.call(this, s);
     8754  if (!s.visible) return;
     8755  if (!s.children) s.children = [];
     8756
     8757  /*
     8758   * Multiply the current scale factor by this panel's transform. Also clear the
     8759   * default index as we recurse into child marks; it will be reset to the
     8760   * current index when the next panel instance is built.
     8761   */
     8762  var scale = this.scale * s.transform.k, child, n = this.children.length;
     8763  pv.Mark.prototype.index = -1;
     8764
     8765  /*
     8766   * Build each child, passing in the parent (this panel) scene graph node. The
     8767   * child mark's scene is initialized from the corresponding entry in the
     8768   * existing scene graph, such that properties from the previous build can be
     8769   * reused; this is largely to facilitate the recycling of SVG elements.
     8770   */
     8771  for (var i = 0; i < n; i++) {
     8772    child = this.children[i];
     8773    child.scene = s.children[i]; // possibly undefined
     8774    child.scale = scale;
     8775    child.build();
     8776  }
     8777
     8778  /*
     8779   * Once the child marks have been built, the new scene graph nodes are removed
     8780   * from the child marks and placed into the scene graph. The nodes cannot
     8781   * remain on the child nodes because this panel (or a parent panel) may be
     8782   * instantiated multiple times!
     8783   */
     8784  for (var i = 0; i < n; i++) {
     8785    child = this.children[i];
     8786    s.children[i] = child.scene;
     8787    delete child.scene;
     8788    delete child.scale;
     8789  }
     8790
     8791  /* Delete any expired child scenes. */
     8792  s.children.length = n;
     8793};
     8794
     8795/**
     8796 * @private Computes the implied properties for this panel for the specified
     8797 * instance <tt>s</tt> in the scene graph. Panels have two implied
     8798 * properties:<ul>
     8799 *
     8800 * <li>The <tt>canvas</tt> property references the DOM element, typically a DIV,
     8801 * that contains the SVG element that is used to display the visualization. This
     8802 * property may be specified as a string, referring to the unique ID of the
     8803 * element in the DOM. The string is converted to a reference to the DOM
     8804 * element. The width and height of the SVG element is inferred from this DOM
     8805 * element. If no canvas property is specified, a new SVG element is created and
     8806 * inserted into the document, using the panel dimensions; see
     8807 * {@link #createCanvas}.
     8808 *
     8809 * <li>The <tt>children</tt> array, while not a property per se, contains the
     8810 * scene graph for each child mark. This array is initialized to be empty, and
     8811 * is populated above in {@link #buildInstance}.
     8812 *
     8813 * </ul>The current implementation creates the SVG element, if necessary, during
     8814 * the build phase; in the future, it may be preferrable to move this to the
     8815 * update phase, although then the canvas property would be undefined. In
     8816 * addition, DOM inspection is necessary to define the implied width and height
     8817 * properties that may be inferred from the DOM.
     8818 *
     8819 * @param s a node in the scene graph; the instance of the panel to build.
     8820 */
     8821pv.Panel.prototype.buildImplied = function(s) {
     8822  if (!this.parent) {
     8823    var c = s.canvas;
     8824    if (c) {
     8825      /* Clear the container if it's not associated with this panel. */
     8826      if (c.$panel != this) {
     8827        c.$panel = this;
     8828        while (c.lastChild) c.removeChild(c.lastChild);
     8829      }
     8830
     8831      /* If width and height weren't specified, inspect the container. */
     8832      var w, h;
     8833      if (s.width == null) {
     8834        w = parseFloat(pv.css(c, "width"));
     8835        s.width = w - s.left - s.right;
     8836      }
     8837      if (s.height == null) {
     8838        h = parseFloat(pv.css(c, "height"));
     8839        s.height = h - s.top - s.bottom;
     8840      }
     8841    } else {
     8842      var cache = this.$canvas || (this.$canvas = []);
     8843      if (!(c = cache[this.index])) {
     8844        c = cache[this.index] = document.createElement("span");
     8845        if (this.$dom) { // script element for text/javascript+protovis
     8846          this.$dom.parentNode.insertBefore(c, this.$dom);
     8847        } else { // find the last element in the body
     8848          var n = document.body;
     8849          while (n.lastChild && n.lastChild.tagName) n = n.lastChild;
     8850          if (n != document.body) n = n.parentNode;
     8851          n.appendChild(c);
     8852        }
     8853      }
     8854    }
     8855    s.canvas = c;
     8856  }
     8857  if (!s.transform) s.transform = pv.Transform.identity;
     8858  pv.Mark.prototype.buildImplied.call(this, s);
     8859};
     8860/**
     8861 * Constructs a new image with default properties. Images are not typically
     8862 * constructed directly, but by adding to a panel or an existing mark via
     8863 * {@link pv.Mark#add}.
     8864 *
     8865 * @class Represents an image, either a static resource or a dynamically-
     8866 * generated pixel buffer. Images share the same layout and style properties as
     8867 * bars. The external image resource is specified via the {@link #url}
     8868 * property. The optional fill, if specified, appears beneath the image, while
     8869 * the optional stroke appears above the image.
     8870 *
     8871 * <p>Dynamic images such as heatmaps are supported using the {@link #image}
     8872 * psuedo-property. This function is passed the <i>x</i> and <i>y</i> index, in
     8873 * addition to the current data stack. The return value is a {@link pv.Color},
     8874 * or null for transparent. A string can also be returned, which will be parsed
     8875 * into a color; however, it is typically much faster to return an object with
     8876 * <tt>r</tt>, <tt>g</tt>, <tt>b</tt> and <tt>a</tt> attributes, to avoid the
     8877 * cost of parsing and object instantiation.
     8878 *
     8879 * <p>See {@link pv.Bar} for details on positioning properties.
     8880 *
     8881 * @extends pv.Bar
     8882 */
     8883pv.Image = function() {
     8884  pv.Bar.call(this);
     8885};
     8886
     8887pv.Image.prototype = pv.extend(pv.Bar)
     8888    .property("url", String)
     8889    .property("imageWidth", Number)
     8890    .property("imageHeight", Number);
     8891
     8892pv.Image.prototype.type = "image";
     8893
     8894/**
     8895 * The URL of the image to display. The set of supported image types is
     8896 * browser-dependent; PNG and JPEG are recommended.
     8897 *
     8898 * @type string
     8899 * @name pv.Image.prototype.url
     8900 */
     8901
     8902/**
     8903 * The width of the image in pixels. For static images, this property is
     8904 * computed implicitly from the loaded image resources. For dynamic images, this
     8905 * property can be used to specify the width of the pixel buffer; otherwise, the
     8906 * value is derived from the <tt>width</tt> property.
     8907 *
     8908 * @type number
     8909 * @name pv.Image.prototype.imageWidth
     8910 */
     8911
     8912/**
     8913 * The height of the image in pixels. For static images, this property is
     8914 * computed implicitly from the loaded image resources. For dynamic images, this
     8915 * property can be used to specify the height of the pixel buffer; otherwise, the
     8916 * value is derived from the <tt>height</tt> property.
     8917 *
     8918 * @type number
     8919 * @name pv.Image.prototype.imageHeight
     8920 */
     8921
     8922/**
     8923 * Default properties for images. By default, there is no stroke or fill style.
     8924 *
     8925 * @type pv.Image
     8926 */
     8927pv.Image.prototype.defaults = new pv.Image()
     8928    .extend(pv.Bar.prototype.defaults)
     8929    .fillStyle(null);
     8930
     8931/**
     8932 * Specifies the dynamic image function. By default, no image function is
     8933 * specified and the <tt>url</tt> property is used to load a static image
     8934 * resource. If an image function is specified, it will be invoked for each
     8935 * pixel in the image, based on the related <tt>imageWidth</tt> and
     8936 * <tt>imageHeight</tt> properties.
     8937 *
     8938 * <p>For example, given a two-dimensional array <tt>heatmap</tt>, containing
     8939 * numbers in the range [0, 1] in row-major order, a simple monochrome heatmap
     8940 * image can be specified as:
     8941 *
     8942 * <pre>vis.add(pv.Image)
     8943 *     .imageWidth(heatmap[0].length)
     8944 *     .imageHeight(heatmap.length)
     8945 *     .image(pv.ramp("white", "black").by(function(x, y) heatmap[y][x]));</pre>
     8946 *
     8947 * For fastest performance, use an ordinal scale which caches the fixed color
     8948 * palette, or return an object literal with <tt>r</tt>, <tt>g</tt>, <tt>b</tt>
     8949 * and <tt>a</tt> attributes. A {@link pv.Color} or string can also be returned,
     8950 * though this typically results in slower performance.
     8951 *
     8952 * @param {function} f the new sizing function.
     8953 * @returns {pv.Layout.Pack} this.
     8954 */
     8955pv.Image.prototype.image = function(f) {
     8956  /** @private */
     8957  this.$image = function() {
     8958      var c = f.apply(this, arguments);
     8959      return c == null ? pv.Color.transparent
     8960          : typeof c == "string" ? pv.color(c)
     8961          : c;
     8962    };
     8963  return this;
     8964};
     8965
     8966/** @private Scan the proto chain for an image function. */
     8967pv.Image.prototype.bind = function() {
     8968  pv.Bar.prototype.bind.call(this);
     8969  var binds = this.binds, mark = this;
     8970  do {
     8971    binds.image = mark.$image;
     8972  } while (!binds.image && (mark = mark.proto));
     8973};
     8974
     8975/** @private */
     8976pv.Image.prototype.buildImplied = function(s) {
     8977  pv.Bar.prototype.buildImplied.call(this, s);
     8978  if (!s.visible) return;
     8979
     8980  /* Compute the implied image dimensions. */
     8981  if (s.imageWidth == null) s.imageWidth = s.width;
     8982  if (s.imageHeight == null) s.imageHeight = s.height;
     8983
     8984  /* Compute the pixel values. */
     8985  if ((s.url == null) && this.binds.image) {
     8986
     8987    /* Cache the canvas element to reuse across renders. */
     8988    var canvas = this.$canvas || (this.$canvas = document.createElement("canvas")),
     8989        context = canvas.getContext("2d"),
     8990        w = s.imageWidth,
     8991        h = s.imageHeight,
     8992        stack = pv.Mark.stack,
     8993        data;
     8994
     8995    /* Evaluate the image function, storing into a CanvasPixelArray. */
     8996    canvas.width = w;
     8997    canvas.height = h;
     8998    data = (s.image = context.createImageData(w, h)).data;
     8999    stack.unshift(null, null);
     9000    for (var y = 0, p = 0; y < h; y++) {
     9001      stack[1] = y;
     9002      for (var x = 0; x < w; x++) {
     9003        stack[0] = x;
     9004        var color = this.binds.image.apply(this, stack);
     9005        data[p++] = color.r;
     9006        data[p++] = color.g;
     9007        data[p++] = color.b;
     9008        data[p++] = 255 * color.a;
     9009      }
     9010    }
     9011    stack.splice(0, 2);
     9012  }
     9013};
     9014/**
     9015 * Constructs a new wedge with default properties. Wedges are not typically
     9016 * constructed directly, but by adding to a panel or an existing mark via
     9017 * {@link pv.Mark#add}.
     9018 *
     9019 * @class Represents a wedge, or pie slice. Specified in terms of start and end
     9020 * angle, inner and outer radius, wedges can be used to construct donut charts
     9021 * and polar bar charts as well. If the {@link #angle} property is used, the end
     9022 * angle is implied by adding this value to start angle. By default, the start
     9023 * angle is the previously-generated wedge's end angle. This design allows
     9024 * explicit control over the wedge placement if desired, while offering
     9025 * convenient defaults for the construction of radial graphs.
     9026 *
     9027 * <p>The center point of the circle is positioned using the standard box model.
     9028 * The wedge can be stroked and filled, similar to {@link pv.Bar}.
     9029 *
     9030 * <p>See also the <a href="../../api/Wedge.html">Wedge guide</a>.
     9031 *
     9032 * @extends pv.Mark
     9033 */
     9034pv.Wedge = function() {
     9035  pv.Mark.call(this);
     9036};
     9037
     9038pv.Wedge.prototype = pv.extend(pv.Mark)
     9039    .property("startAngle", Number)
     9040    .property("endAngle", Number)
     9041    .property("angle", Number)
     9042    .property("innerRadius", Number)
     9043    .property("outerRadius", Number)
     9044    .property("lineWidth", Number)
     9045    .property("strokeStyle", pv.color)
     9046    .property("fillStyle", pv.color);
     9047
     9048pv.Wedge.prototype.type = "wedge";
     9049
     9050/**
     9051 * The start angle of the wedge, in radians. The start angle is measured
     9052 * clockwise from the 3 o'clock position. The default value of this property is
     9053 * the end angle of the previous instance (the {@link Mark#sibling}), or -PI / 2
     9054 * for the first wedge; for pie and donut charts, typically only the
     9055 * {@link #angle} property needs to be specified.
     9056 *
     9057 * @type number
     9058 * @name pv.Wedge.prototype.startAngle
     9059 */
     9060
     9061/**
     9062 * The end angle of the wedge, in radians. If not specified, the end angle is
     9063 * implied as the start angle plus the {@link #angle}.
     9064 *
     9065 * @type number
     9066 * @name pv.Wedge.prototype.endAngle
     9067 */
     9068
     9069/**
     9070 * The angular span of the wedge, in radians. This property is used if end angle
     9071 * is not specified.
     9072 *
     9073 * @type number
     9074 * @name pv.Wedge.prototype.angle
     9075 */
     9076
     9077/**
     9078 * The inner radius of the wedge, in pixels. The default value of this property
     9079 * is zero; a positive value will produce a donut slice rather than a pie slice.
     9080 * The inner radius can vary per-wedge.
     9081 *
     9082 * @type number
     9083 * @name pv.Wedge.prototype.innerRadius
     9084 */
     9085
     9086/**
     9087 * The outer radius of the wedge, in pixels. This property is required. For
     9088 * pies, only this radius is required; for donuts, the inner radius must be
     9089 * specified as well. The outer radius can vary per-wedge.
     9090 *
     9091 * @type number
     9092 * @name pv.Wedge.prototype.outerRadius
     9093 */
     9094
     9095/**
     9096 * The width of stroked lines, in pixels; used in conjunction with
     9097 * <tt>strokeStyle</tt> to stroke the wedge's border.
     9098 *
     9099 * @type number
     9100 * @name pv.Wedge.prototype.lineWidth
     9101 */
     9102
     9103/**
     9104 * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
     9105 * stroke the wedge's border. The default value of this property is null,
     9106 * meaning wedges are not stroked by default.
     9107 *
     9108 * @type string
     9109 * @name pv.Wedge.prototype.strokeStyle
     9110 * @see pv.color
     9111 */
     9112
     9113/**
     9114 * The wedge fill style; if non-null, the interior of the wedge is filled with
     9115 * the specified color. The default value of this property is a categorical
     9116 * color.
     9117 *
     9118 * @type string
     9119 * @name pv.Wedge.prototype.fillStyle
     9120 * @see pv.color
     9121 */
     9122
     9123/**
     9124 * Default properties for wedges. By default, there is no stroke and the fill
     9125 * style is a categorical color.
     9126 *
     9127 * @type pv.Wedge
     9128 */
     9129pv.Wedge.prototype.defaults = new pv.Wedge()
     9130    .extend(pv.Mark.prototype.defaults)
     9131    .startAngle(function() {
     9132        var s = this.sibling();
     9133        return s ? s.endAngle : -Math.PI / 2;
     9134      })
     9135    .innerRadius(0)
     9136    .lineWidth(1.5)
     9137    .strokeStyle(null)
     9138    .fillStyle(pv.Colors.category20().by(pv.index));
     9139
     9140/**
     9141 * Returns the mid-radius of the wedge, which is defined as half-way between the
     9142 * inner and outer radii.
     9143 *
     9144 * @see #innerRadius
     9145 * @see #outerRadius
     9146 * @returns {number} the mid-radius, in pixels.
     9147 */
     9148pv.Wedge.prototype.midRadius = function() {
     9149  return (this.innerRadius() + this.outerRadius()) / 2;
     9150};
     9151
     9152/**
     9153 * Returns the mid-angle of the wedge, which is defined as half-way between the
     9154 * start and end angles.
     9155 *
     9156 * @see #startAngle
     9157 * @see #endAngle
     9158 * @returns {number} the mid-angle, in radians.
     9159 */
     9160pv.Wedge.prototype.midAngle = function() {
     9161  return (this.startAngle() + this.endAngle()) / 2;
     9162};
     9163
     9164/**
     9165 * Constructs a new wedge anchor with default properties. Wedges support five
     9166 * different anchors:<ul>
     9167 *
     9168 * <li>outer
     9169 * <li>inner
     9170 * <li>center
     9171 * <li>start
     9172 * <li>end
     9173 *
     9174 * </ul>In addition to positioning properties (left, right, top bottom), the
     9175 * anchors support text rendering properties (text-align, text-baseline,
     9176 * textAngle). Text is rendered to appear inside the wedge.
     9177 *
     9178 * @param {string} name the anchor name; either a string or a property function.
     9179 * @returns {pv.Anchor}
     9180 */
     9181pv.Wedge.prototype.anchor = function(name) {
     9182  function partial(s) { return s.innerRadius || s.angle < 2 * Math.PI; }
     9183  function midRadius(s) { return (s.innerRadius + s.outerRadius) / 2; }
     9184  function midAngle(s) { return (s.startAngle + s.endAngle) / 2; }
     9185  var scene;
     9186  return pv.Mark.prototype.anchor.call(this, name)
     9187    .def("$wedge.anchor", function() {
     9188        scene = this.scene.target;
     9189      })
     9190    .left(function() {
     9191        var s = scene[this.index];
     9192        if (partial(s)) switch (this.name()) {
     9193          case "outer": return s.left + s.outerRadius * Math.cos(midAngle(s));
     9194          case "inner": return s.left + s.innerRadius * Math.cos(midAngle(s));
     9195          case "start": return s.left + midRadius(s) * Math.cos(s.startAngle);
     9196          case "center": return s.left + midRadius(s) * Math.cos(midAngle(s));
     9197          case "end": return s.left + midRadius(s) * Math.cos(s.endAngle);
     9198        }
     9199        return s.left;
     9200      })
     9201    .top(function() {
     9202        var s = scene[this.index];
     9203        if (partial(s)) switch (this.name()) {
     9204          case "outer": return s.top + s.outerRadius * Math.sin(midAngle(s));
     9205          case "inner": return s.top + s.innerRadius * Math.sin(midAngle(s));
     9206          case "start": return s.top + midRadius(s) * Math.sin(s.startAngle);
     9207          case "center": return s.top + midRadius(s) * Math.sin(midAngle(s));
     9208          case "end": return s.top + midRadius(s) * Math.sin(s.endAngle);
     9209        }
     9210        return s.top;
     9211      })
     9212    .textAlign(function() {
     9213        var s = scene[this.index];
     9214        if (partial(s)) switch (this.name()) {
     9215          case "outer": return pv.Wedge.upright(midAngle(s)) ? "right" : "left";
     9216          case "inner": return pv.Wedge.upright(midAngle(s)) ? "left" : "right";
     9217        }
     9218        return "center";
     9219      })
     9220    .textBaseline(function() {
     9221        var s = scene[this.index];
     9222        if (partial(s)) switch (this.name()) {
     9223          case "start": return pv.Wedge.upright(s.startAngle) ? "top" : "bottom";
     9224          case "end": return pv.Wedge.upright(s.endAngle) ? "bottom" : "top";
     9225        }
     9226        return "middle";
     9227      })
     9228    .textAngle(function() {
     9229        var s = scene[this.index], a = 0;
     9230        if (partial(s)) switch (this.name()) {
     9231          case "center":
     9232          case "inner":
     9233          case "outer": a = midAngle(s); break;
     9234          case "start": a = s.startAngle; break;
     9235          case "end": a = s.endAngle; break;
     9236        }
     9237        return pv.Wedge.upright(a) ? a : (a + Math.PI);
     9238      });
     9239};
     9240
     9241/**
     9242 * Returns true if the specified angle is considered "upright", as in, text
     9243 * rendered at that angle would appear upright. If the angle is not upright,
     9244 * text is rotated 180 degrees to be upright, and the text alignment properties
     9245 * are correspondingly changed.
     9246 *
     9247 * @param {number} angle an angle, in radius.
     9248 * @returns {boolean} true if the specified angle is upright.
     9249 */
     9250pv.Wedge.upright = function(angle) {
     9251  angle = angle % (2 * Math.PI);
     9252  angle = (angle < 0) ? (2 * Math.PI + angle) : angle;
     9253  return (angle < Math.PI / 2) || (angle >= 3 * Math.PI / 2);
     9254};
     9255
     9256/** @private Sets angle based on endAngle or vice versa. */
     9257pv.Wedge.prototype.buildImplied = function(s) {
     9258  if (s.angle == null) s.angle = s.endAngle - s.startAngle;
     9259  else if (s.endAngle == null) s.endAngle = s.startAngle + s.angle;
     9260  pv.Mark.prototype.buildImplied.call(this, s);
     9261};
     9262/**
     9263 * Abstract; not implemented. There is no explicit constructor; this class
     9264 * merely serves to document the attributes that are used on particles in
     9265 * physics simulations.
     9266 *
     9267 * @class A weighted particle that can participate in a force simulation.
     9268 *
     9269 * @name pv.Particle
     9270 */
     9271
     9272/**
     9273 * The next particle in the simulation. Particles form a singly-linked list.
     9274 *
     9275 * @field
     9276 * @type pv.Particle
     9277 * @name pv.Particle.prototype.next
     9278 */
     9279
     9280/**
     9281 * The <i>x</i>-position of the particle.
     9282 *
     9283 * @field
     9284 * @type number
     9285 * @name pv.Particle.prototype.x
     9286 */
     9287
     9288/**
     9289 * The <i>y</i>-position of the particle.
     9290 *
     9291 * @field
     9292 * @type number
     9293 * @name pv.Particle.prototype.y
     9294 */
     9295
     9296/**
     9297 * The <i>x</i>-velocity of the particle.
     9298 *
     9299 * @field
     9300 * @type number
     9301 * @name pv.Particle.prototype.vx
     9302 */
     9303
     9304/**
     9305 * The <i>y</i>-velocity of the particle.
     9306 *
     9307 * @field
     9308 * @type number
     9309 * @name pv.Particle.prototype.vy
     9310 */
     9311
     9312/**
     9313 * The <i>x</i>-position of the particle at -dt.
     9314 *
     9315 * @field
     9316 * @type number
     9317 * @name pv.Particle.prototype.px
     9318 */
     9319
     9320/**
     9321 * The <i>y</i>-position of the particle at -dt.
     9322 *
     9323 * @field
     9324 * @type number
     9325 * @name pv.Particle.prototype.py
     9326 */
     9327
     9328/**
     9329 * The <i>x</i>-force on the particle.
     9330 *
     9331 * @field
     9332 * @type number
     9333 * @name pv.Particle.prototype.fx
     9334 */
     9335
     9336/**
     9337 * The <i>y</i>-force on the particle.
     9338 *
     9339 * @field
     9340 * @type number
     9341 * @name pv.Particle.prototype.fy
     9342 */
     9343/**
     9344 * Constructs a new empty simulation.
     9345 *
     9346 * @param {array} particles
     9347 * @returns {pv.Simulation} a new simulation for the specified particles.
     9348 * @see pv.Simulation
     9349 */
     9350pv.simulation = function(particles) {
     9351  return new pv.Simulation(particles);
     9352};
     9353
     9354/**
     9355 * Constructs a new simulation for the specified particles.
     9356 *
     9357 * @class Represents a particle simulation. Particles are massive points in
     9358 * two-dimensional space. Forces can be applied to these particles, causing them
     9359 * to move. Constraints can also be applied to restrict particle movement, for
     9360 * example, constraining particles to a fixed position, or simulating collision
     9361 * between circular particles with area.
     9362 *
     9363 * <p>The simulation uses <a
     9364 * href="http://en.wikipedia.org/wiki/Verlet_integration">Position Verlet</a>
     9365 * integration, due to the ease with which <a
     9366 * href="http://www.teknikus.dk/tj/gdc2001.htm">geometric constraints</a> can be
     9367 * implemented. For each time step, Verlet integration is performed, new forces
     9368 * are accumulated, and then constraints are applied.
     9369 *
     9370 * <p>The simulation makes two simplifying assumptions: all particles are
     9371 * equal-mass, and the time step of the simulation is fixed. It would be easy to
     9372 * incorporate variable-mass particles as a future enhancement. Variable time
     9373 * steps are also possible, but are likely to introduce instability in the
     9374 * simulation.
     9375 *
     9376 * <p>This class can be used directly to simulate particle interaction.
     9377 * Alternatively, for network diagrams, see {@link pv.Layout.Force}.
     9378 *
     9379 * @param {array} particles an array of {@link pv.Particle}s to simulate.
     9380 * @see pv.Layout.Force
     9381 * @see pv.Force
     9382 * @see pv.Constraint
     9383 */
     9384pv.Simulation = function(particles) {
     9385  for (var i = 0; i < particles.length; i++) this.particle(particles[i]);
     9386};
     9387
     9388/**
     9389 * The particles in the simulation. Particles are stored as a linked list; this
     9390 * field represents the first particle in the simulation.
     9391 *
     9392 * @field
     9393 * @type pv.Particle
     9394 * @name pv.Simulation.prototype.particles
     9395 */
     9396
     9397/**
     9398 * The forces in the simulation. Forces are stored as a linked list; this field
     9399 * represents the first force in the simulation.
     9400 *
     9401 * @field
     9402 * @type pv.Force
     9403 * @name pv.Simulation.prototype.forces
     9404 */
     9405
     9406/**
     9407 * The constraints in the simulation. Constraints are stored as a linked list;
     9408 * this field represents the first constraint in the simulation.
     9409 *
     9410 * @field
     9411 * @type pv.Constraint
     9412 * @name pv.Simulation.prototype.constraints
     9413 */
     9414
     9415/**
     9416 * Adds the specified particle to the simulation.
     9417 *
     9418 * @param {pv.Particle} p the new particle.
     9419 * @returns {pv.Simulation} this.
     9420 */
     9421pv.Simulation.prototype.particle = function(p) {
     9422  p.next = this.particles;
     9423  /* Default velocities and forces to zero if unset. */
     9424  if (isNaN(p.px)) p.px = p.x;
     9425  if (isNaN(p.py)) p.py = p.y;
     9426  if (isNaN(p.fx)) p.fx = 0;
     9427  if (isNaN(p.fy)) p.fy = 0;
     9428  this.particles = p;
     9429  return this;
     9430};
     9431
     9432/**
     9433 * Adds the specified force to the simulation.
     9434 *
     9435 * @param {pv.Force} f the new force.
     9436 * @returns {pv.Simulation} this.
     9437 */
     9438pv.Simulation.prototype.force = function(f) {
     9439  f.next = this.forces;
     9440  this.forces = f;
     9441  return this;
     9442};
     9443
     9444/**
     9445 * Adds the specified constraint to the simulation.
     9446 *
     9447 * @param {pv.Constraint} c the new constraint.
     9448 * @returns {pv.Simulation} this.
     9449 */
     9450pv.Simulation.prototype.constraint = function(c) {
     9451  c.next = this.constraints;
     9452  this.constraints = c;
     9453  return this;
     9454};
     9455
     9456/**
     9457 * Apply constraints, and then set the velocities to zero.
     9458 *
     9459 * @returns {pv.Simulation} this.
     9460 */
     9461pv.Simulation.prototype.stabilize = function(n) {
     9462  var c;
     9463  if (!arguments.length) n = 3; // TODO use cooling schedule
     9464  for (var i = 0; i < n; i++) {
     9465    var q = new pv.Quadtree(this.particles);
     9466    for (c = this.constraints; c; c = c.next) c.apply(this.particles, q);
     9467  }
     9468  for (var p = this.particles; p; p = p.next) {
     9469    p.px = p.x;
     9470    p.py = p.y;
     9471  }
     9472  return this;
     9473};
     9474
     9475/**
     9476 * Advances the simulation one time-step.
     9477 */
     9478pv.Simulation.prototype.step = function() {
     9479  var p, f, c;
     9480
     9481  /*
     9482   * Assumptions:
     9483   * - The mass (m) of every particles is 1.
     9484   * - The time step (dt) is 1.
     9485   */
     9486
     9487  /* Position Verlet integration. */
     9488  for (p = this.particles; p; p = p.next) {
     9489    var px = p.px, py = p.py;
     9490    p.px = p.x;
     9491    p.py = p.y;
     9492    p.x += p.vx = ((p.x - px) + p.fx);
     9493    p.y += p.vy = ((p.y - py) + p.fy);
     9494  }
     9495
     9496  /* Apply constraints, then accumulate new forces. */
     9497  var q = new pv.Quadtree(this.particles);
     9498  for (c = this.constraints; c; c = c.next) c.apply(this.particles, q);
     9499  for (p = this.particles; p; p = p.next) p.fx = p.fy = 0;
     9500  for (f = this.forces; f; f = f.next) f.apply(this.particles, q);
     9501};
     9502/**
     9503 * Constructs a new quadtree for the specified array of particles.
     9504 *
     9505 * @class Represents a quadtree: a two-dimensional recursive spatial
     9506 * subdivision. This particular implementation uses square partitions, dividing
     9507 * each square into four equally-sized squares. Each particle exists in a unique
     9508 * node; if multiple particles are in the same position, some particles may be
     9509 * stored on internal nodes rather than leaf nodes.
     9510 *
     9511 * <p>This quadtree can be used to accelerate various spatial operations, such
     9512 * as the Barnes-Hut approximation for computing n-body forces, or collision
     9513 * detection.
     9514 *
     9515 * @see pv.Force.charge
     9516 * @see pv.Constraint.collision
     9517 * @param {pv.Particle} particles the linked list of particles.
     9518 */
     9519pv.Quadtree = function(particles) {
     9520  var p;
     9521
     9522  /* Compute bounds. */
     9523  var x1 = Number.POSITIVE_INFINITY, y1 = x1,
     9524      x2 = Number.NEGATIVE_INFINITY, y2 = x2;
     9525  for (p = particles; p; p = p.next) {
     9526    if (p.x < x1) x1 = p.x;
     9527    if (p.y < y1) y1 = p.y;
     9528    if (p.x > x2) x2 = p.x;
     9529    if (p.y > y2) y2 = p.y;
     9530  }
     9531
     9532  /* Squarify the bounds. */
     9533  var dx = x2 - x1, dy = y2 - y1;
     9534  if (dx > dy) y2 = y1 + dx;
     9535  else x2 = x1 + dy;
     9536  this.xMin = x1;
     9537  this.yMin = y1;
     9538  this.xMax = x2;
     9539  this.yMax = y2;
     9540
     9541  /**
     9542   * @ignore Recursively inserts the specified particle <i>p</i> at the node
     9543   * <i>n</i> or one of its descendants. The bounds are defined by [<i>x1</i>,
     9544   * <i>x2</i>] and [<i>y1</i>, <i>y2</i>].
     9545   */
     9546  function insert(n, p, x1, y1, x2, y2) {
     9547    if (isNaN(p.x) || isNaN(p.y)) return; // ignore invalid particles
     9548    if (n.leaf) {
     9549      if (n.p) {
     9550        /*
     9551         * If the particle at this leaf node is at the same position as the new
     9552         * particle we are adding, we leave the particle associated with the
     9553         * internal node while adding the new particle to a child node. This
     9554         * avoids infinite recursion.
     9555         */
     9556        if ((Math.abs(n.p.x - p.x) + Math.abs(n.p.y - p.y)) < .01) {
     9557          insertChild(n, p, x1, y1, x2, y2);
     9558        } else {
     9559          var v = n.p;
     9560          n.p = null;
     9561          insertChild(n, v, x1, y1, x2, y2);
     9562          insertChild(n, p, x1, y1, x2, y2);
     9563        }
     9564      } else {
     9565        n.p = p;
     9566      }
     9567    } else {
     9568      insertChild(n, p, x1, y1, x2, y2);
     9569    }
     9570  }
     9571
     9572  /**
     9573   * @ignore Recursively inserts the specified particle <i>p</i> into a
     9574   * descendant of node <i>n</i>. The bounds are defined by [<i>x1</i>,
     9575   * <i>x2</i>] and [<i>y1</i>, <i>y2</i>].
     9576   */
     9577  function insertChild(n, p, x1, y1, x2, y2) {
     9578    /* Compute the split point, and the quadrant in which to insert p. */
     9579    var sx = (x1 + x2) * .5,
     9580        sy = (y1 + y2) * .5,
     9581        right = p.x >= sx,
     9582        bottom = p.y >= sy;
     9583
     9584    /* Recursively insert into the child node. */
     9585    n.leaf = false;
     9586    switch ((bottom << 1) + right) {
     9587      case 0: n = n.c1 || (n.c1 = new pv.Quadtree.Node()); break;
     9588      case 1: n = n.c2 || (n.c2 = new pv.Quadtree.Node()); break;
     9589      case 2: n = n.c3 || (n.c3 = new pv.Quadtree.Node()); break;
     9590      case 3: n = n.c4 || (n.c4 = new pv.Quadtree.Node()); break;
     9591    }
     9592
     9593    /* Update the bounds as we recurse. */
     9594    if (right) x1 = sx; else x2 = sx;
     9595    if (bottom) y1 = sy; else y2 = sy;
     9596    insert(n, p, x1, y1, x2, y2);
     9597  }
     9598
     9599  /* Insert all particles. */
     9600  this.root = new pv.Quadtree.Node();
     9601  for (p = particles; p; p = p.next) insert(this.root, p, x1, y1, x2, y2);
     9602};
     9603
     9604/**
     9605 * The root node of the quadtree.
     9606 *
     9607 * @type pv.Quadtree.Node
     9608 * @name pv.Quadtree.prototype.root
     9609 */
     9610
     9611/**
     9612 * The minimum x-coordinate value of all contained particles.
     9613 *
     9614 * @type number
     9615 * @name pv.Quadtree.prototype.xMin
     9616 */
     9617
     9618/**
     9619 * The maximum x-coordinate value of all contained particles.
     9620 *
     9621 * @type number
     9622 * @name pv.Quadtree.prototype.xMax
     9623 */
     9624
     9625/**
     9626 * The minimum y-coordinate value of all contained particles.
     9627 *
     9628 * @type number
     9629 * @name pv.Quadtree.prototype.yMin
     9630 */
     9631
     9632/**
     9633 * The maximum y-coordinate value of all contained particles.
     9634 *
     9635 * @type number
     9636 * @name pv.Quadtree.prototype.yMax
     9637 */
     9638
     9639/**
     9640 * Constructs a new node.
     9641 *
     9642 * @class A node in a quadtree.
     9643 *
     9644 * @see pv.Quadtree
     9645 */
     9646pv.Quadtree.Node = function() {
     9647  /*
     9648   * Prepopulating all attributes significantly increases performance! Also,
     9649   * letting the language interpreter manage garbage collection was moderately
     9650   * faster than creating a cache pool.
     9651   */
     9652  this.leaf = true;
     9653  this.c1 = null;
     9654  this.c2 = null;
     9655  this.c3 = null;
     9656  this.c4 = null;
     9657  this.p = null;
     9658};
     9659
     9660/**
     9661 * True if this node is a leaf node; i.e., it has no children. Note that both
     9662 * leaf nodes and non-leaf (internal) nodes may have associated particles. If
     9663 * this is a non-leaf node, then at least one of {@link #c1}, {@link #c2},
     9664 * {@link #c3} or {@link #c4} is guaranteed to be non-null.
     9665 *
     9666 * @type boolean
     9667 * @name pv.Quadtree.Node.prototype.leaf
     9668 */
     9669
     9670/**
     9671 * The particle associated with this node, if any.
     9672 *
     9673 * @type pv.Particle
     9674 * @name pv.Quadtree.Node.prototype.p
     9675 */
     9676
     9677/**
     9678 * The child node for the second quadrant, if any.
     9679 *
     9680 * @type pv.Quadtree.Node
     9681 * @name pv.Quadtree.Node.prototype.c2
     9682 */
     9683
     9684/**
     9685 * The child node for the third quadrant, if any.
     9686 *
     9687 * @type pv.Quadtree.Node
     9688 * @name pv.Quadtree.Node.prototype.c3
     9689 */
     9690
     9691/**
     9692 * The child node for the fourth quadrant, if any.
     9693 *
     9694 * @type pv.Quadtree.Node
     9695 * @name pv.Quadtree.Node.prototype.c4
     9696 */
     9697/**
     9698 * Abstract; see an implementing class.
     9699 *
     9700 * @class Represents a force that acts on particles. Note that this interface
     9701 * does not specify how to bind a force to specific particles; in general,
     9702 * forces are applied globally to all particles. However, some forces may be
     9703 * applied to specific particles or between particles, such as spring forces,
     9704 * through additional specialization.
     9705 *
     9706 * @see pv.Simulation
     9707 * @see pv.Particle
     9708 * @see pv.Force.charge
     9709 * @see pv.Force.drag
     9710 * @see pv.Force.spring
     9711 */
     9712pv.Force = {};
     9713
     9714/**
     9715 * Applies this force to the specified particles.
     9716 *
     9717 * @function
     9718 * @name pv.Force.prototype.apply
     9719 * @param {pv.Particle} particles particles to which to apply this force.
     9720 * @param {pv.Quadtree} q a quadtree for spatial acceleration.
     9721 */
     9722/**
     9723 * Constructs a new charge force, with an optional charge constant. The charge
     9724 * constant can be negative for repulsion (e.g., particles with electrical
     9725 * charge of equal sign), or positive for attraction (e.g., massive particles
     9726 * with mutual gravity). The default charge constant is -40.
     9727 *
     9728 * @class An n-body force, as defined by Coulomb's law or Newton's law of
     9729 * gravitation, inversely proportional to the square of the distance between
     9730 * particles. Note that the force is independent of the <i>mass</i> of the
     9731 * associated particles, and that the particles do not have charges of varying
     9732 * magnitude; instead, the attraction or repulsion of all particles is globally
     9733 * specified as the charge {@link #constant}.
     9734 *
     9735 * <p>This particular implementation uses the Barnes-Hut algorithm. For details,
     9736 * see <a
     9737 * href="http://www.nature.com/nature/journal/v324/n6096/abs/324446a0.html">"A
     9738 * hierarchical O(N log N) force-calculation algorithm"</a>, J. Barnes &amp;
     9739 * P. Hut, <i>Nature</i> 1986.
     9740 *
     9741 * @name pv.Force.charge
     9742 * @param {number} [k] the charge constant.
     9743 */
     9744pv.Force.charge = function(k) {
     9745  var min = 2, // minimum distance at which to observe forces
     9746      min1 = 1 / min,
     9747      max = 500, // maximum distance at which to observe forces
     9748      max1 = 1 / max,
     9749      theta = .9, // Barnes-Hut theta approximation constant
     9750      force = {};
     9751
     9752  if (!arguments.length) k = -40; // default charge constant (repulsion)
     9753
     9754  /**
     9755   * Sets or gets the charge constant. If an argument is specified, it is the
     9756   * new charge constant. The charge constant can be negative for repulsion
     9757   * (e.g., particles with electrical charge of equal sign), or positive for
     9758   * attraction (e.g., massive particles with mutual gravity). The default
     9759   * charge constant is -40.
     9760   *
     9761   * @function
     9762   * @name pv.Force.charge.prototype.constant
     9763   * @param {number} x the charge constant.
     9764   * @returns {pv.Force.charge} this.
     9765   */
     9766  force.constant = function(x) {
     9767    if (arguments.length) {
     9768      k = Number(x);
     9769      return force;
     9770    }
     9771    return k;
     9772  };
     9773
     9774  /**
     9775   * Sets or gets the domain; specifies the minimum and maximum domain within
     9776   * which charge forces are applied. A minimum distance threshold avoids
     9777   * applying forces that are two strong (due to granularity of the simulation's
     9778   * numeric integration). A maximum distance threshold improves performance by
     9779   * skipping force calculations for particles that are far apart.
     9780   *
     9781   * <p>The default domain is [2, 500].
     9782   *
     9783   * @function
     9784   * @name pv.Force.charge.prototype.domain
     9785   * @param {number} a
     9786   * @param {number} b
     9787   * @returns {pv.Force.charge} this.
     9788   */
     9789  force.domain = function(a, b) {
     9790    if (arguments.length) {
     9791      min = Number(a);
     9792      min1 = 1 / min;
     9793      max = Number(b);
     9794      max1 = 1 / max;
     9795      return force;
     9796    }
     9797    return [min, max];
     9798  };
     9799
     9800  /**
     9801   * Sets or gets the Barnes-Hut approximation factor. The Barnes-Hut
     9802   * approximation criterion is the ratio of the size of the quadtree node to
     9803   * the distance from the point to the node's center of mass is beneath some
     9804   * threshold.
     9805   *
     9806   * @function
     9807   * @name pv.Force.charge.prototype.theta
     9808   * @param {number} x the new Barnes-Hut approximation factor.
     9809   * @returns {pv.Force.charge} this.
     9810   */
     9811  force.theta = function(x) {
     9812    if (arguments.length) {
     9813      theta = Number(x);
     9814      return force;
     9815    }
     9816    return theta;
     9817  };
     9818
     9819  /**
     9820   * @ignore Recursively computes the center of charge for each node in the
     9821   * quadtree. This is equivalent to the center of mass, assuming that all
     9822   * particles have unit weight.
     9823   */
     9824  function accumulate(n) {
     9825    var cx = 0, cy = 0;
     9826    n.cn = 0;
     9827    function accumulateChild(c) {
     9828      accumulate(c);
     9829      n.cn += c.cn;
     9830      cx += c.cn * c.cx;
     9831      cy += c.cn * c.cy;
     9832    }
     9833    if (!n.leaf) {
     9834      if (n.c1) accumulateChild(n.c1);
     9835      if (n.c2) accumulateChild(n.c2);
     9836      if (n.c3) accumulateChild(n.c3);
     9837      if (n.c4) accumulateChild(n.c4);
     9838    }
     9839    if (n.p) {
     9840      n.cn += k;
     9841      cx += k * n.p.x;
     9842      cy += k * n.p.y;
     9843    }
     9844    n.cx = cx / n.cn;
     9845    n.cy = cy / n.cn;
     9846  }
     9847
     9848  /**
     9849   * @ignore Recursively computes forces on the given particle using the given
     9850   * quadtree node. The Barnes-Hut approximation criterion is the ratio of the
     9851   * size of the quadtree node to the distance from the point to the node's
     9852   * center of mass is beneath some threshold.
     9853   */
     9854  function forces(n, p, x1, y1, x2, y2) {
     9855    var dx = n.cx - p.x,
     9856        dy = n.cy - p.y,
     9857        dn = 1 / Math.sqrt(dx * dx + dy * dy);
     9858
     9859    /* Barnes-Hut criterion. */
     9860    if ((n.leaf && (n.p != p)) || ((x2 - x1) * dn < theta)) {
     9861      if (dn < max1) return;
     9862      if (dn > min1) dn = min1;
     9863      var kc = n.cn * dn * dn * dn,
     9864          fx = dx * kc,
     9865          fy = dy * kc;
     9866      p.fx += fx;
     9867      p.fy += fy;
     9868    } else if (!n.leaf) {
     9869      var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5;
     9870      if (n.c1) forces(n.c1, p, x1, y1, sx, sy);
     9871      if (n.c2) forces(n.c2, p, sx, y1, x2, sy);
     9872      if (n.c3) forces(n.c3, p, x1, sy, sx, y2);
     9873      if (n.c4) forces(n.c4, p, sx, sy, x2, y2);
     9874      if (dn < max1) return;
     9875      if (dn > min1) dn = min1;
     9876      if (n.p && (n.p != p)) {
     9877        var kc = k * dn * dn * dn,
     9878            fx = dx * kc,
     9879            fy = dy * kc;
     9880        p.fx += fx;
     9881        p.fy += fy;
     9882      }
     9883    }
     9884  }
     9885
     9886  /**
     9887   * Applies this force to the specified particles. The force is applied between
     9888   * all pairs of particles within the domain, using the specified quadtree to
     9889   * accelerate n-body force calculation using the Barnes-Hut approximation
     9890   * criterion.
     9891   *
     9892   * @function
     9893   * @name pv.Force.charge.prototype.apply
     9894   * @param {pv.Particle} particles particles to which to apply this force.
     9895   * @param {pv.Quadtree} q a quadtree for spatial acceleration.
     9896   */
     9897  force.apply = function(particles, q) {
     9898    accumulate(q.root);
     9899    for (var p = particles; p; p = p.next) {
     9900      forces(q.root, p, q.xMin, q.yMin, q.xMax, q.yMax);
     9901    }
     9902  };
     9903
     9904  return force;
     9905};
     9906/**
     9907 * Constructs a new drag force with the specified constant.
     9908 *
     9909 * @class Implements a drag force, simulating friction. The drag force is
     9910 * applied in the opposite direction of the particle's velocity. Since Position
     9911 * Verlet integration does not track velocities explicitly, the error term with
     9912 * this estimate of velocity is fairly high, so the drag force may be
     9913 * inaccurate.
     9914 *
     9915 * @extends pv.Force
     9916 * @param {number} k the drag constant.
     9917 * @see #constant
     9918 */
     9919pv.Force.drag = function(k) {
     9920  var force = {};
     9921
     9922  if (!arguments.length) k = .1; // default drag constant
     9923
     9924  /**
     9925   * Sets or gets the drag constant, in the range [0,1]. The default drag
     9926   * constant is 0.1. The drag forces scales linearly with the particle's
     9927   * velocity based on the given drag constant.
     9928   *
     9929   * @function
     9930   * @name pv.Force.drag.prototype.constant
     9931   * @param {number} x the new drag constant.
     9932   * @returns {pv.Force.drag} this, or the current drag constant.
     9933   */
     9934  force.constant = function(x) {
     9935    if (arguments.length) { k = x; return force; }
     9936    return k;
     9937  };
     9938
     9939  /**
     9940   * Applies this force to the specified particles.
     9941   *
     9942   * @function
     9943   * @name pv.Force.drag.prototype.apply
     9944   * @param {pv.Particle} particles particles to which to apply this force.
     9945   */
     9946  force.apply = function(particles) {
     9947    if (k) for (var p = particles; p; p = p.next) {
     9948      p.fx -= k * p.vx;
     9949      p.fy -= k * p.vy;
     9950    }
     9951  };
     9952
     9953  return force;
     9954};
     9955/**
     9956 * Constructs a new spring force with the specified constant. The links
     9957 * associated with this spring force must be specified before the spring force
     9958 * can be applied.
     9959 *
     9960 * @class Implements a spring force, per Hooke's law. The spring force can be
     9961 * configured with a tension constant, rest length, and damping factor. The
     9962 * tension and damping will automatically be normalized using the inverse square
     9963 * root of the maximum link degree of attached nodes; this makes springs weaker
     9964 * between nodes of high link degree.
     9965 *
     9966 * <p>Unlike other forces (such as charge and drag forces) which may be applied
     9967 * globally, spring forces are only applied between linked particles. Therefore,
     9968 * an array of links must be specified before this force can be applied; the
     9969 * links should be an array of {@link pv.Layout.Network.Link}s. See also
     9970 * {@link pv.Layout.Force} for an example of using spring and charge forces for
     9971 * network layout.
     9972 *
     9973 * @extends pv.Force
     9974 * @param {number} k the spring constant.
     9975 * @see #constant
     9976 * @see #links
     9977 */
     9978pv.Force.spring = function(k) {
     9979  var d = .1, // default damping factor
     9980      l = 20, // default rest length
     9981      links, // links on which to apply spring forces
     9982      kl, // per-spring normalization
     9983      force = {};
     9984
     9985  if (!arguments.length) k = .1; // default spring constant (tension)
     9986
     9987  /**
     9988   * Sets or gets the links associated with this spring force. Unlike other
     9989   * forces (such as charge and drag forces) which may be applied globally,
     9990   * spring forces are only applied between linked particles. Therefore, an
     9991   * array of links must be specified before this force can be applied; the
     9992   * links should be an array of {@link pv.Layout.Network.Link}s.
     9993   *
     9994   * @function
     9995   * @name pv.Force.spring.prototype.links
     9996   * @param {array} x the new array of links.
     9997   * @returns {pv.Force.spring} this, or the current array of links.
     9998   */
     9999  force.links = function(x) {
     10000    if (arguments.length) {
     10001      links = x;
     10002      kl = x.map(function(l) {
     10003          return 1 / Math.sqrt(Math.max(
     10004              l.sourceNode.linkDegree,
     10005              l.targetNode.linkDegree));
     10006        });
     10007      return force;
     10008    }
     10009    return links;
     10010  };
     10011
     10012  /**
     10013   * Sets or gets the spring constant. The default value is 0.1; greater values
     10014   * will result in stronger tension. The spring tension is automatically
     10015   * normalized using the inverse square root of the maximum link degree of
     10016   * attached nodes.
     10017   *
     10018   * @function
     10019   * @name pv.Force.spring.prototype.constant
     10020   * @param {number} x the new spring constant.
     10021   * @returns {pv.Force.spring} this, or the current spring constant.
     10022   */
     10023  force.constant = function(x) {
     10024    if (arguments.length) {
     10025      k = Number(x);
     10026      return force;
     10027    }
     10028    return k;
     10029  };
     10030
     10031  /**
     10032   * The spring damping factor, in the range [0,1]. Damping functions
     10033   * identically to drag forces, damping spring bounciness by applying a force
     10034   * in the opposite direction of attached nodes' velocities. The default value
     10035   * is 0.1. The spring damping is automatically normalized using the inverse
     10036   * square root of the maximum link degree of attached nodes.
     10037   *
     10038   * @function
     10039   * @name pv.Force.spring.prototype.damping
     10040   * @param {number} x the new spring damping factor.
     10041   * @returns {pv.Force.spring} this, or the current spring damping factor.
     10042   */
     10043  force.damping = function(x) {
     10044    if (arguments.length) {
     10045      d = Number(x);
     10046      return force;
     10047    }
     10048    return d;
     10049  };
     10050
     10051  /**
     10052   * The spring rest length. The default value is 20 pixels.
     10053   *
     10054   * @function
     10055   * @name pv.Force.spring.prototype.length
     10056   * @param {number} x the new spring rest length.
     10057   * @returns {pv.Force.spring} this, or the current spring rest length.
     10058   */
     10059  force.length = function(x) {
     10060    if (arguments.length) {
     10061      l = Number(x);
     10062      return force;
     10063    }
     10064    return l;
     10065  };
     10066
     10067  /**
     10068   * Applies this force to the specified particles.
     10069   *
     10070   * @function
     10071   * @name pv.Force.spring.prototype.apply
     10072   * @param {pv.Particle} particles particles to which to apply this force.
     10073   */
     10074  force.apply = function(particles) {
     10075    for (var i = 0; i < links.length; i++) {
     10076      var a = links[i].sourceNode,
     10077          b = links[i].targetNode,
     10078          dx = a.x - b.x,
     10079          dy = a.y - b.y,
     10080          dn = Math.sqrt(dx * dx + dy * dy),
     10081          dd = dn ? (1 / dn) : 1,
     10082          ks = k * kl[i], // normalized tension
     10083          kd = d * kl[i], // normalized damping
     10084          kk = (ks * (dn - l) + kd * (dx * (a.vx - b.vx) + dy * (a.vy - b.vy)) * dd) * dd,
     10085          fx = -kk * (dn ? dx : (.01 * (.5 - Math.random()))),
     10086          fy = -kk * (dn ? dy : (.01 * (.5 - Math.random())));
     10087      a.fx += fx;
     10088      a.fy += fy;
     10089      b.fx -= fx;
     10090      b.fy -= fy;
     10091    }
     10092  };
     10093
     10094  return force;
     10095};
     10096/**
     10097 * Abstract; see an implementing class.
     10098 *
     10099 * @class Represents a constraint that acts on particles. Note that this
     10100 * interface does not specify how to bind a constraint to specific particles; in
     10101 * general, constraints are applied globally to all particles. However, some
     10102 * constraints may be applied to specific particles or between particles, such
     10103 * as position constraints, through additional specialization.
     10104 *
     10105 * @see pv.Simulation
     10106 * @see pv.Particle
     10107 * @see pv.Constraint.bound
     10108 * @see pv.Constraint.collision
     10109 * @see pv.Constraint.position
     10110 */
     10111pv.Constraint = {};
     10112
     10113/**
     10114 * Applies this constraint to the specified particles.
     10115 *
     10116 * @function
     10117 * @name pv.Constraint.prototype.apply
     10118 * @param {pv.Particle} particles particles to which to apply this constraint.
     10119 * @param {pv.Quadtree} q a quadtree for spatial acceleration.
     10120 * @returns {pv.Constraint} this.
     10121 */
     10122/**
     10123 * Constructs a new collision constraint. The default search radius is 10, and
     10124 * the default repeat count is 1. A radius function must be specified to compute
     10125 * the radius of particles.
     10126 *
     10127 * @class Constraints circles to avoid overlap. Each particle is treated as a
     10128 * circle, with the radius of the particle computed using a specified function.
     10129 * For example, if the particle has an <tt>r</tt> attribute storing the radius,
     10130 * the radius <tt>function(d) d.r</tt> specifies a collision constraint using
     10131 * this radius. The radius function is passed each {@link pv.Particle} as the
     10132 * first argument.
     10133 *
     10134 * <p>To accelerate collision detection, this implementation uses a quadtree and
     10135 * a search radius. The search radius is computed as the maximum radius of all
     10136 * particles in the simulation.
     10137 *
     10138 * @see pv.Constraint
     10139 * @param {function} radius the radius function.
     10140 */
     10141pv.Constraint.collision = function(radius) {
     10142  var n = 1, // number of times to repeat the constraint
     10143      r1,
     10144      px1,
     10145      py1,
     10146      px2,
     10147      py2,
     10148      constraint = {};
     10149
     10150  if (!arguments.length) r1 = 10; // default search radius
     10151
     10152  /**
     10153   * Sets or gets the repeat count. If the repeat count is greater than 1, the
     10154   * constraint will be applied repeatedly; this is a form of the Gauss-Seidel
     10155   * method for constraints relaxation. Repeating the collision constraint makes
     10156   * the constraint have more of an effect when there is a potential for many
     10157   * co-occurring collisions.
     10158   *
     10159   * @function
     10160   * @name pv.Constraint.collision.prototype.repeat
     10161   * @param {number} x the number of times to repeat this constraint.
     10162   * @returns {pv.Constraint.collision} this.
     10163   */
     10164  constraint.repeat = function(x) {
     10165    if (arguments.length) {
     10166      n = Number(x);
     10167      return constraint;
     10168    }
     10169    return n;
     10170  };
     10171
     10172  /** @private */
     10173  function constrain(n, p, x1, y1, x2, y2) {
     10174    if (!n.leaf) {
     10175      var sx = (x1 + x2) * .5,
     10176          sy = (y1 + y2) * .5,
     10177          top = sy > py1,
     10178          bottom = sy < py2,
     10179          left = sx > px1,
     10180          right = sx < px2;
     10181      if (top) {
     10182        if (n.c1 && left) constrain(n.c1, p, x1, y1, sx, sy);
     10183        if (n.c2 && right) constrain(n.c2, p, sx, y1, x2, sy);
     10184      }
     10185      if (bottom) {
     10186        if (n.c3 && left) constrain(n.c3, p, x1, sy, sx, y2);
     10187        if (n.c4 && right) constrain(n.c4, p, sx, sy, x2, y2);
     10188      }
     10189    }
     10190    if (n.p && (n.p != p)) {
     10191      var dx = p.x - n.p.x,
     10192          dy = p.y - n.p.y,
     10193          l = Math.sqrt(dx * dx + dy * dy),
     10194          d = r1 + radius(n.p);
     10195      if (l < d) {
     10196        var k = (l - d) / l * .5;
     10197        dx *= k;
     10198        dy *= k;
     10199        p.x -= dx;
     10200        p.y -= dy;
     10201        n.p.x += dx;
     10202        n.p.y += dy;
     10203      }
     10204    }
     10205  }
     10206
     10207  /**
     10208   * Applies this constraint to the specified particles.
     10209   *
     10210   * @function
     10211   * @name pv.Constraint.collision.prototype.apply
     10212   * @param {pv.Particle} particles particles to which to apply this constraint.
     10213   * @param {pv.Quadtree} q a quadtree for spatial acceleration.
     10214   */
     10215  constraint.apply = function(particles, q) {
     10216    var p, r, max = -Infinity;
     10217    for (p = particles; p; p = p.next) {
     10218      r = radius(p);
     10219      if (r > max) max = r;
     10220    }
     10221    for (var i = 0; i < n; i++) {
     10222      for (p = particles; p; p = p.next) {
     10223        r = (r1 = radius(p)) + max;
     10224        px1 = p.x - r;
     10225        px2 = p.x + r;
     10226        py1 = p.y - r;
     10227        py2 = p.y + r;
     10228        constrain(q.root, p, q.xMin, q.yMin, q.xMax, q.yMax);
     10229      }
     10230    }
     10231  };
     10232
     10233  return constraint;
     10234};
     10235/**
     10236 * Constructs a default position constraint using the <tt>fix</tt> attribute.
     10237 * An optional position function can be specified to determine how the fixed
     10238 * position per-particle is determined.
     10239 *
     10240 * @class Constraints particles to a fixed position. The fixed position per
     10241 * particle is determined using a given position function, which defaults to
     10242 * <tt>function(d) d.fix</tt>.
     10243 *
     10244 * <p>If the position function returns null, then no position constraint is
     10245 * applied to the given particle. Otherwise, the particle's position is set to
     10246 * the returned position, as expressed by a {@link pv.Vector}. (Note: the
     10247 * position does not need to be an instance of <tt>pv.Vector</tt>, but simply an
     10248 * object with <tt>x</tt> and <tt>y</tt> attributes.)
     10249 *
     10250 * <p>This constraint also supports a configurable alpha parameter, which
     10251 * defaults to 1. If the alpha parameter is in the range [0,1], then rather than
     10252 * setting the particle's new position directly to the position returned by the
     10253 * supplied position function, the particle's position is interpolated towards
     10254 * the fixed position. This results is a smooth (exponential) drift towards the
     10255 * fixed position, which can increase the stability of the physics simulation.
     10256 * In addition, the alpha parameter can be decayed over time, relaxing the
     10257 * position constraint, which helps to stabilize on an optimal solution.
     10258 *
     10259 * @param {function} [f] the position function.
     10260 */
     10261pv.Constraint.position = function(f) {
     10262  var a = 1, // default alpha
     10263      constraint = {};
     10264
     10265  if (!arguments.length) /** @ignore */ f = function(p) { return p.fix; };
     10266
     10267  /**
     10268   * Sets or gets the alpha parameter for position interpolation. If the alpha
     10269   * parameter is in the range [0,1], then rather than setting the particle's
     10270   * new position directly to the position returned by the supplied position
     10271   * function, the particle's position is interpolated towards the fixed
     10272   * position.
     10273   *
     10274   * @function
     10275   * @name pv.Constraint.position.prototype.alpha
     10276   * @param {number} x the new alpha parameter, in the range [0,1].
     10277   * @returns {pv.Constraint.position} this.
     10278   */
     10279  constraint.alpha = function(x) {
     10280    if (arguments.length) {
     10281      a = Number(x);
     10282      return constraint;
     10283    }
     10284    return a;
     10285  };
     10286
     10287  /**
     10288   * Applies this constraint to the specified particles.
     10289   *
     10290   * @function
     10291   * @name pv.Constraint.position.prototype.apply
     10292   * @param {pv.Particle} particles particles to which to apply this constraint.
     10293   */
     10294  constraint.apply = function(particles) {
     10295    for (var p = particles; p; p = p.next) {
     10296      var v = f(p);
     10297      if (v) {
     10298        p.x += (v.x - p.x) * a;
     10299        p.y += (v.y - p.y) * a;
     10300        p.fx = p.fy = p.vx = p.vy = 0;
     10301      }
     10302    }
     10303  };
     10304
     10305  return constraint;
     10306};
     10307/**
     10308 * Constructs a new bound constraint. Before the constraint can be used, the
     10309 * {@link #x} and {@link #y} methods must be call to specify the bounds.
     10310 *
     10311 * @class Constrains particles to within fixed rectangular bounds. For example,
     10312 * this constraint can be used to constrain particles in a physics simulation
     10313 * within the bounds of an enclosing panel.
     10314 *
     10315 * <p>Note that the current implementation treats particles as points, with no
     10316 * area. If the particles are rendered as dots, be sure to include some
     10317 * additional padding to inset the bounds such that the edges of the dots do not
     10318 * get clipped by the panel bounds. If the particles have different radii, this
     10319 * constraint would need to be extended using a radius function, similar to
     10320 * {@link pv.Constraint.collision}.
     10321 *
     10322 * @see pv.Layout.Force
     10323 * @extends pv.Constraint
     10324 */
     10325pv.Constraint.bound = function() {
     10326  var constraint = {},
     10327      x,
     10328      y;
     10329
     10330  /**
     10331   * Sets or gets the bounds on the x-coordinate.
     10332   *
     10333   * @function
     10334   * @name pv.Constraint.bound.prototype.x
     10335   * @param {number} min the minimum allowed x-coordinate.
     10336   * @param {number} max the maximum allowed x-coordinate.
     10337   * @returns {pv.Constraint.bound} this.
     10338   */
     10339  constraint.x = function(min, max) {
     10340    if (arguments.length) {
     10341      x = {min: Math.min(min, max), max: Math.max(min, max)};
     10342      return this;
     10343    }
     10344    return x;
     10345  };
     10346
     10347  /**
     10348   * Sets or gets the bounds on the y-coordinate.
     10349   *
     10350   * @function
     10351   * @name pv.Constraint.bound.prototype.y
     10352   * @param {number} min the minimum allowed y-coordinate.
     10353   * @param {number} max the maximum allowed y-coordinate.
     10354   * @returns {pv.Constraint.bound} this.
     10355   */
     10356  constraint.y = function(min, max) {
     10357    if (arguments.length) {
     10358      y = {min: Math.min(min, max), max: Math.max(min, max)};
     10359      return this;
     10360    }
     10361    return y;
     10362  };
     10363
     10364  /**
     10365   * Applies this constraint to the specified particles.
     10366   *
     10367   * @function
     10368   * @name pv.Constraint.bound.prototype.apply
     10369   * @param {pv.Particle} particles particles to which to apply this constraint.
     10370   */
     10371  constraint.apply = function(particles) {
     10372    if (x) for (var p = particles; p; p = p.next) {
     10373      p.x = p.x < x.min ? x.min : (p.x > x.max ? x.max : p.x);
     10374    }
     10375    if (y) for (var p = particles; p; p = p.next) {
     10376      p.y = p.y < y.min ? y.min : (p.y > y.max ? y.max : p.y);
     10377    }
     10378  };
     10379
     10380  return constraint;
     10381};
     10382/**
     10383 * Constructs a new, empty layout with default properties. Layouts are not
     10384 * typically constructed directly; instead, a concrete subclass is added to an
     10385 * existing panel via {@link pv.Mark#add}.
     10386 *
     10387 * @class Represents an abstract layout, encapsulating a visualization technique
     10388 * such as a streamgraph or treemap. Layouts are themselves containers,
     10389 * extending from {@link pv.Panel}, and defining a set of mark prototypes as
     10390 * children. These mark prototypes provide default properties that together
     10391 * implement the given visualization technique.
     10392 *
     10393 * <p>Layouts do not initially contain any marks; any exported marks (such as a
     10394 * network layout's <tt>link</tt> and <tt>node</tt>) are intended to be used as
     10395 * prototypes. By adding a concrete mark, such as a {@link pv.Bar}, to the
     10396 * appropriate mark prototype, the mark is added to the layout and inherits the
     10397 * given properties. This approach allows further customization of the layout,
     10398 * either by choosing a different mark type to add, or more simply by overriding
     10399 * some of the layout's defined properties.
     10400 *
     10401 * <p>Each concrete layout, such as treemap or circle-packing, has different
     10402 * behavior and may export different mark prototypes, depending on what marks
     10403 * are typically needed to render the desired visualization. Therefore it is
     10404 * important to understand how each layout is structured, such that the provided
     10405 * mark prototypes are used appropriately.
     10406 *
     10407 * <p>In addition to the mark prototypes, layouts may define custom properties
     10408 * that affect the overall behavior of the layout. For example, a treemap layout
     10409 * might use a property to specify which layout algorithm to use. These
     10410 * properties are just like other mark properties, and can be defined as
     10411 * constants or as functions. As with panels, the data property can be used to
     10412 * replicate layouts, and properties can be defined to in terms of layout data.
     10413 *
     10414 * @extends pv.Panel
     10415 */
     10416pv.Layout = function() {
     10417  pv.Panel.call(this);
     10418};
     10419
     10420pv.Layout.prototype = pv.extend(pv.Panel);
     10421
     10422/**
     10423 * @private Defines a local property with the specified name and cast. Note that
     10424 * although the property method is only defined locally, the cast function is
     10425 * global, which is necessary since properties are inherited!
     10426 *
     10427 * @param {string} name the property name.
     10428 * @param {function} [cast] the cast function for this property.
     10429 */
     10430pv.Layout.prototype.property = function(name, cast) {
     10431  if (!this.hasOwnProperty("properties")) {
     10432    this.properties = pv.extend(this.properties);
     10433  }
     10434  this.properties[name] = true;
     10435  this.propertyMethod(name, false, pv.Mark.cast[name] = cast);
     10436  return this;
     10437};
     10438/**
     10439 * Constructs a new, empty network layout. Layouts are not typically constructed
     10440 * directly; instead, they are added to an existing panel via
     10441 * {@link pv.Mark#add}.
     10442 *
     10443 * @class Represents an abstract layout for network diagrams. This class
     10444 * provides the basic structure for both node-link diagrams (such as
     10445 * force-directed graph layout) and space-filling network diagrams (such as
     10446 * sunbursts and treemaps). Note that "network" here is a general term that
     10447 * includes hierarchical structures; a tree is represented using links from
     10448 * child to parent.
     10449 *
     10450 * <p>Network layouts require the graph data structure to be defined using two
     10451 * properties:<ul>
     10452 *
     10453 * <li><tt>nodes</tt> - an array of objects representing nodes. Objects in this
     10454 * array must conform to the {@link pv.Layout.Network.Node} interface; which is
     10455 * to say, be careful to avoid naming collisions with automatic attributes such
     10456 * as <tt>index</tt> and <tt>linkDegree</tt>. If the nodes property is defined
     10457 * as an array of primitives, such as numbers or strings, these primitives are
     10458 * automatically wrapped in an object; the resulting object's <tt>nodeValue</tt>
     10459 * attribute points to the original primitive value.
     10460 *
     10461 * <p><li><tt>links</tt> - an array of objects representing links. Objects in
     10462 * this array must conform to the {@link pv.Layout.Network.Link} interface; at a
     10463 * minimum, either <tt>source</tt> and <tt>target</tt> indexes or
     10464 * <tt>sourceNode</tt> and <tt>targetNode</tt> references must be set. Note that
     10465 * if the links property is defined after the nodes property, the links can be
     10466 * defined in terms of <tt>this.nodes()</tt>.
     10467 *
     10468 * </ul>
     10469 *
     10470 * <p>Three standard mark prototypes are provided:<ul>
     10471 *
     10472 * <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Dot}. The node
     10473 * mark is added directly to the layout, with the data property defined via the
     10474 * layout's <tt>nodes</tt> property. Properties such as <tt>strokeStyle</tt> and
     10475 * <tt>fillStyle</tt> can be overridden to compute properties from node data
     10476 * dynamically.
     10477 *
     10478 * <p><li><tt>link</tt> - for rendering links; typically a {@link pv.Line}. The
     10479 * link mark is added to a child panel, whose data property is defined as
     10480 * layout's <tt>links</tt> property. The link's data property is then a
     10481 * two-element array of the source node and target node. Thus, poperties such as
     10482 * <tt>strokeStyle</tt> and <tt>fillStyle</tt> can be overridden to compute
     10483 * properties from either the node data (the first argument) or the link data
     10484 * (the second argument; the parent panel data) dynamically.
     10485 *
     10486 * <p><li><tt>label</tt> - for rendering node labels; typically a
     10487 * {@link pv.Label}. The label mark is added directly to the layout, with the
     10488 * data property defined via the layout's <tt>nodes</tt> property. Properties
     10489 * such as <tt>strokeStyle</tt> and <tt>fillStyle</tt> can be overridden to
     10490 * compute properties from node data dynamically.
     10491 *
     10492 * </ul>Note that some network implementations may not support all three
     10493 * standard mark prototypes; for example, space-filling hierarchical layouts
     10494 * typically do not use a <tt>link</tt> prototype, as the parent-child links are
     10495 * implied by the structure of the space-filling <tt>node</tt> marks.  Check the
     10496 * specific network layout for implementation details.
     10497 *
     10498 * <p>Network layout properties, including <tt>nodes</tt> and <tt>links</tt>,
     10499 * are typically cached rather than re-evaluated with every call to render. This
     10500 * is a performance optimization, as network layout algorithms can be
     10501 * expensive. If the network structure changes, call {@link #reset} to clear the
     10502 * cache before rendering. Note that although the network layout properties are
     10503 * cached, child mark properties, such as the marks used to render the nodes and
     10504 * links, <i>are not</i>. Therefore, non-structural changes to the network
     10505 * layout, such as changing the color of a mark on mouseover, do not need to
     10506 * reset the layout.
     10507 *
     10508 * @see pv.Layout.Hierarchy
     10509 * @see pv.Layout.Force
     10510 * @see pv.Layout.Matrix
     10511 * @see pv.Layout.Arc
     10512 * @see pv.Layout.Rollup
     10513 * @extends pv.Layout
     10514 */
     10515pv.Layout.Network = function() {
     10516  pv.Layout.call(this);
     10517  var that = this;
     10518
     10519  /* @private Version tracking to cache layout state, improving performance. */
     10520  this.$id = pv.id();
     10521
     10522  /**
     10523   * The node prototype. This prototype is intended to be used with a Dot mark
     10524   * in conjunction with the link prototype.
     10525   *
     10526   * @type pv.Mark
     10527   * @name pv.Layout.Network.prototype.node
     10528   */
     10529  (this.node = new pv.Mark()
     10530      .data(function() { return that.nodes(); })
     10531      .strokeStyle("#1f77b4")
     10532      .fillStyle("#fff")
     10533      .left(function(n) { return n.x; })
     10534      .top(function(n) { return n.y; })).parent = this;
     10535
     10536  /**
     10537   * The link prototype, which renders edges between source nodes and target
     10538   * nodes. This prototype is intended to be used with a Line mark in
     10539   * conjunction with the node prototype.
     10540   *
     10541   * @type pv.Mark
     10542   * @name pv.Layout.Network.prototype.link
     10543   */
     10544  this.link = new pv.Mark()
     10545      .extend(this.node)
     10546      .data(function(p) { return [p.sourceNode, p.targetNode]; })
     10547      .fillStyle(null)
     10548      .lineWidth(function(d, p) { return p.linkValue * 1.5; })
     10549      .strokeStyle("rgba(0,0,0,.2)");
     10550
     10551  this.link.add = function(type) {
     10552    return that.add(pv.Panel)
     10553        .data(function() { return that.links(); })
     10554      .add(type)
     10555        .extend(this);
     10556  };
     10557
     10558  /**
     10559   * The node label prototype, which renders the node name adjacent to the node.
     10560   * This prototype is provided as an alternative to using the anchor on the
     10561   * node mark; it is primarily intended to be used with radial node-link
     10562   * layouts, since it provides a convenient mechanism to set the text angle.
     10563   *
     10564   * @type pv.Mark
     10565   * @name pv.Layout.Network.prototype.label
     10566   */
     10567  (this.label = new pv.Mark()
     10568      .extend(this.node)
     10569      .textMargin(7)
     10570      .textBaseline("middle")
     10571      .text(function(n) { return n.nodeName || n.nodeValue; })
     10572      .textAngle(function(n) {
     10573          var a = n.midAngle;
     10574          return pv.Wedge.upright(a) ? a : (a + Math.PI);
     10575        })
     10576      .textAlign(function(n) {
     10577          return pv.Wedge.upright(n.midAngle) ? "left" : "right";
     10578        })).parent = this;
     10579};
     10580
     10581/**
     10582 * @class Represents a node in a network layout. There is no explicit
     10583 * constructor; this class merely serves to document the attributes that are
     10584 * used on nodes in network layouts. (Note that hierarchical nodes place
     10585 * additional requirements on node representation, vis {@link pv.Dom.Node}.)
     10586 *
     10587 * @see pv.Layout.Network
     10588 * @name pv.Layout.Network.Node
     10589 */
     10590
     10591/**
     10592 * The node index, zero-based. This attribute is populated automatically based
     10593 * on the index in the array returned by the <tt>nodes</tt> property.
     10594 *
     10595 * @type number
     10596 * @name pv.Layout.Network.Node.prototype.index
     10597 */
     10598
     10599/**
     10600 * The link degree; the sum of link values for all incoming and outgoing links.
     10601 * This attribute is populated automatically.
     10602 *
     10603 * @type number
     10604 * @name pv.Layout.Network.Node.prototype.linkDegree
     10605 */
     10606
     10607/**
     10608 * The node name; optional. If present, this attribute will be used to provide
     10609 * the text for node labels. If not present, the label text will fallback to the
     10610 * <tt>nodeValue</tt> attribute.
     10611 *
     10612 * @type string
     10613 * @name pv.Layout.Network.Node.prototype.nodeName
     10614 */
     10615
     10616/**
     10617 * The node value; optional. If present, and no <tt>nodeName</tt> attribute is
     10618 * present, the node value will be used as the label text. This attribute is
     10619 * also automatically populated if the nodes are specified as an array of
     10620 * primitives, such as strings or numbers.
     10621 *
     10622 * @type object
     10623 * @name pv.Layout.Network.Node.prototype.nodeValue
     10624 */
     10625
     10626/**
     10627 * @class Represents a link in a network layout. There is no explicit
     10628 * constructor; this class merely serves to document the attributes that are
     10629 * used on links in network layouts. For hierarchical layouts, this class is
     10630 * used to represent the parent-child links.
     10631 *
     10632 * @see pv.Layout.Network
     10633 * @name pv.Layout.Network.Link
     10634 */
     10635
     10636/**
     10637 * The link value, or weight; optional. If not specified (or not a number), the
     10638 * default value of 1 is used.
     10639 *
     10640 * @type number
     10641 * @name pv.Layout.Network.Link.prototype.linkValue
     10642 */
     10643
     10644/**
     10645 * The link's source node. If not set, this value will be derived from the
     10646 * <tt>source</tt> attribute index.
     10647 *
     10648 * @type pv.Layout.Network.Node
     10649 * @name pv.Layout.Network.Link.prototype.sourceNode
     10650 */
     10651
     10652/**
     10653 * The link's target node. If not set, this value will be derived from the
     10654 * <tt>target</tt> attribute index.
     10655 *
     10656 * @type pv.Layout.Network.Node
     10657 * @name pv.Layout.Network.Link.prototype.targetNode
     10658 */
     10659
     10660/**
     10661 * Alias for <tt>sourceNode</tt>, as expressed by the index of the source node.
     10662 * This attribute is not populated automatically, but may be used as a more
     10663 * convenient identification of the link's source, for example in a static JSON
     10664 * representation.
     10665 *
     10666 * @type number
     10667 * @name pv.Layout.Network.Link.prototype.source
     10668 */
     10669
     10670/**
     10671 * Alias for <tt>targetNode</tt>, as expressed by the index of the target node.
     10672 * This attribute is not populated automatically, but may be used as a more
     10673 * convenient identification of the link's target, for example in a static JSON
     10674 * representation.
     10675 *
     10676 * @type number
     10677 * @name pv.Layout.Network.Link.prototype.target
     10678 */
     10679
     10680/**
     10681 * Alias for <tt>linkValue</tt>. This attribute is not populated automatically,
     10682 * but may be used instead of the <tt>linkValue</tt> attribute when specifying
     10683 * links.
     10684 *
     10685 * @type number
     10686 * @name pv.Layout.Network.Link.prototype.value
     10687 */
     10688
     10689/** @private Transform nodes and links on cast. */
     10690pv.Layout.Network.prototype = pv.extend(pv.Layout)
     10691    .property("nodes", function(v) {
     10692        return v.map(function(d, i) {
     10693            if (typeof d != "object") d = {nodeValue: d};
     10694            d.index = i;
     10695            d.linkDegree = 0;
     10696            return d;
     10697          });
     10698      })
     10699    .property("links", function(v) {
     10700        return v.map(function(d) {
     10701            if (isNaN(d.linkValue)) d.linkValue = isNaN(d.value) ? 1 : d.value;
     10702            return d;
     10703          });
     10704      });
     10705
     10706/**
     10707 * Resets the cache, such that changes to layout property definitions will be
     10708 * visible on subsequent render. Unlike normal marks (and normal layouts),
     10709 * properties associated with network layouts are not automatically re-evaluated
     10710 * on render; the properties are cached, and any expensive layout algorithms are
     10711 * only run after the layout is explicitly reset.
     10712 *
     10713 * @returns {pv.Layout.Network} this.
     10714 */
     10715pv.Layout.Network.prototype.reset = function() {
     10716  this.$id = pv.id();
     10717  return this;
     10718};
     10719
     10720/** @private Skip evaluating properties if cached. */
     10721pv.Layout.Network.prototype.buildProperties = function(s, properties) {
     10722  if ((s.$id || 0) < this.$id) {
     10723    pv.Layout.prototype.buildProperties.call(this, s, properties);
     10724  }
     10725};
     10726
     10727/** @private Compute link degrees; map source and target indexes to nodes. */
     10728pv.Layout.Network.prototype.buildImplied = function(s) {
     10729  pv.Layout.prototype.buildImplied.call(this, s);
     10730  if (s.$id >= this.$id) return true;
     10731  s.$id = this.$id;
     10732  s.links.forEach(function(d) {
     10733      var v = d.linkValue;
     10734      (d.sourceNode || (d.sourceNode = s.nodes[d.source])).linkDegree += v;
     10735      (d.targetNode || (d.targetNode = s.nodes[d.target])).linkDegree += v;
     10736    });
     10737};
     10738/**
     10739 * Constructs a new, empty hierarchy layout. Layouts are not typically
     10740 * constructed directly; instead, they are added to an existing panel via
     10741 * {@link pv.Mark#add}.
     10742 *
     10743 * @class Represents an abstract layout for hierarchy diagrams. This class is a
     10744 * specialization of {@link pv.Layout.Network}, providing the basic structure
     10745 * for both hierarchical node-link diagrams (such as Reingold-Tilford trees) and
     10746 * space-filling hierarchy diagrams (such as sunbursts and treemaps).
     10747 *
     10748 * <p>Unlike general network layouts, the <tt>links</tt> property need not be
     10749 * defined explicitly. Instead, the links are computed implicitly from the
     10750 * <tt>parentNode</tt> attribute of the node objects, as defined by the
     10751 * <tt>nodes</tt> property. This implementation is also available as
     10752 * {@link #links}, for reuse with non-hierarchical layouts; for example, to
     10753 * render a tree using force-directed layout.
     10754 *
     10755 * <p>Correspondingly, the <tt>nodes</tt> property is represented as a union of
     10756 * {@link pv.Layout.Network.Node} and {@link pv.Dom.Node}. To construct a node
     10757 * hierarchy from a simple JSON map, use the {@link pv.Dom} operator; this
     10758 * operator also provides an easy way to sort nodes before passing them to the
     10759 * layout.
     10760 *
     10761 * <p>For more details on how to use this layout, see
     10762 * {@link pv.Layout.Network}.
     10763 *
     10764 * @see pv.Layout.Cluster
     10765 * @see pv.Layout.Partition
     10766 * @see pv.Layout.Tree
     10767 * @see pv.Layout.Treemap
     10768 * @see pv.Layout.Indent
     10769 * @see pv.Layout.Pack
     10770 * @extends pv.Layout.Network
     10771 */
     10772pv.Layout.Hierarchy = function() {
     10773  pv.Layout.Network.call(this);
     10774  this.link.strokeStyle("#ccc");
     10775};
     10776
     10777pv.Layout.Hierarchy.prototype = pv.extend(pv.Layout.Network);
     10778
     10779/** @private Compute the implied links. (Links are null by default.) */
     10780pv.Layout.Hierarchy.prototype.buildImplied = function(s) {
     10781  if (!s.links) s.links = pv.Layout.Hierarchy.links.call(this);
     10782  pv.Layout.Network.prototype.buildImplied.call(this, s);
     10783};
     10784
     10785/** The implied links; computes links using the <tt>parentNode</tt> attribute. */
     10786pv.Layout.Hierarchy.links = function() {
     10787  return this.nodes()
     10788      .filter(function(n) { return n.parentNode; })
     10789      .map(function(n) {
     10790          return {
     10791              sourceNode: n,
     10792              targetNode: n.parentNode,
     10793              linkValue: 1
     10794            };
     10795      });
     10796};
     10797
     10798/** @private Provides standard node-link layout based on breadth & depth. */
     10799pv.Layout.Hierarchy.NodeLink = {
     10800
     10801  /** @private */
     10802  buildImplied: function(s) {
     10803    var nodes = s.nodes,
     10804        orient = s.orient,
     10805        horizontal = /^(top|bottom)$/.test(orient),
     10806        w = s.width,
     10807        h = s.height;
     10808
     10809    /* Compute default inner and outer radius. */
     10810    if (orient == "radial") {
     10811      var ir = s.innerRadius, or = s.outerRadius;
     10812      if (ir == null) ir = 0;
     10813      if (or == null) or = Math.min(w, h) / 2;
     10814    }
     10815
     10816    /** @private Returns the radius of the given node. */
     10817    function radius(n) {
     10818      return n.parentNode ? (n.depth * (or - ir) + ir) : 0;
     10819    }
     10820
     10821    /** @private Returns the angle of the given node. */
     10822    function midAngle(n) {
     10823      return (n.parentNode ? (n.breadth - .25) * 2 * Math.PI : 0);
     10824    }
     10825
     10826    /** @private */
     10827    function x(n) {
     10828      switch (orient) {
     10829        case "left": return n.depth * w;
     10830        case "right": return w - n.depth * w;
     10831        case "top": return n.breadth * w;
     10832        case "bottom": return w - n.breadth * w;
     10833        case "radial": return w / 2 + radius(n) * Math.cos(n.midAngle);
     10834      }
     10835    }
     10836
     10837    /** @private */
     10838    function y(n) {
     10839      switch (orient) {
     10840        case "left": return n.breadth * h;
     10841        case "right": return h - n.breadth * h;
     10842        case "top": return n.depth * h;
     10843        case "bottom": return h - n.depth * h;
     10844        case "radial": return h / 2 + radius(n) * Math.sin(n.midAngle);
     10845      }
     10846    }
     10847
     10848    for (var i = 0; i < nodes.length; i++) {
     10849      var n = nodes[i];
     10850      n.midAngle = orient == "radial" ? midAngle(n)
     10851          : horizontal ? Math.PI / 2 : 0;
     10852      n.x = x(n);
     10853      n.y = y(n);
     10854      if (n.firstChild) n.midAngle += Math.PI;
     10855    }
     10856  }
     10857};
     10858
     10859/** @private Provides standard space-filling layout based on breadth & depth. */
     10860pv.Layout.Hierarchy.Fill = {
     10861
     10862  /** @private */
     10863  constructor: function() {
     10864    this.node
     10865        .strokeStyle("#fff")
     10866        .fillStyle("#ccc")
     10867        .width(function(n) { return n.dx; })
     10868        .height(function(n) { return n.dy; })
     10869        .innerRadius(function(n) { return n.innerRadius; })
     10870        .outerRadius(function(n) { return n.outerRadius; })
     10871        .startAngle(function(n) { return n.startAngle; })
     10872        .angle(function(n) { return n.angle; });
     10873
     10874    this.label
     10875        .textAlign("center")
     10876        .left(function(n) { return n.x + (n.dx / 2); })
     10877        .top(function(n) { return n.y + (n.dy / 2); });
     10878
     10879    /* Hide unsupported link. */
     10880    delete this.link;
     10881  },
     10882
     10883  /** @private */
     10884  buildImplied: function(s) {
     10885    var nodes = s.nodes,
     10886        orient = s.orient,
     10887        horizontal = /^(top|bottom)$/.test(orient),
     10888        w = s.width,
     10889        h = s.height,
     10890        depth = -nodes[0].minDepth;
     10891
     10892    /* Compute default inner and outer radius. */
     10893    if (orient == "radial") {
     10894      var ir = s.innerRadius, or = s.outerRadius;
     10895      if (ir == null) ir = 0;
     10896      if (ir) depth *= 2; // use full depth step for root
     10897      if (or == null) or = Math.min(w, h) / 2;
     10898    }
     10899
     10900    /** @private Scales the specified depth for a space-filling layout. */
     10901    function scale(d, depth) {
     10902      return (d + depth) / (1 + depth);
     10903    }
     10904
     10905    /** @private */
     10906    function x(n) {
     10907      switch (orient) {
     10908        case "left": return scale(n.minDepth, depth) * w;
     10909        case "right": return (1 - scale(n.maxDepth, depth)) * w;
     10910        case "top": return n.minBreadth * w;
     10911        case "bottom": return (1 - n.maxBreadth) * w;
     10912        case "radial": return w / 2;
     10913      }
     10914    }
     10915
     10916    /** @private */
     10917    function y(n) {
     10918      switch (orient) {
     10919        case "left": return n.minBreadth * h;
     10920        case "right": return (1 - n.maxBreadth) * h;
     10921        case "top": return scale(n.minDepth, depth) * h;
     10922        case "bottom": return (1 - scale(n.maxDepth, depth)) * h;
     10923        case "radial": return h / 2;
     10924      }
     10925    }
     10926
     10927    /** @private */
     10928    function dx(n) {
     10929      switch (orient) {
     10930        case "left":
     10931        case "right": return (n.maxDepth - n.minDepth) / (1 + depth) * w;
     10932        case "top":
     10933        case "bottom": return (n.maxBreadth - n.minBreadth) * w;
     10934        case "radial": return n.parentNode ? (n.innerRadius + n.outerRadius) * Math.cos(n.midAngle) : 0;
     10935      }
     10936    }
     10937
     10938    /** @private */
     10939    function dy(n) {
     10940      switch (orient) {
     10941        case "left":
     10942        case "right": return (n.maxBreadth - n.minBreadth) * h;
     10943        case "top":
     10944        case "bottom": return (n.maxDepth - n.minDepth) / (1 + depth) * h;
     10945        case "radial": return n.parentNode ? (n.innerRadius + n.outerRadius) * Math.sin(n.midAngle) : 0;
     10946      }
     10947    }
     10948
     10949    /** @private */
     10950    function innerRadius(n) {
     10951      return Math.max(0, scale(n.minDepth, depth / 2)) * (or - ir) + ir;
     10952    }
     10953
     10954    /** @private */
     10955    function outerRadius(n) {
     10956      return scale(n.maxDepth, depth / 2) * (or - ir) + ir;
     10957    }
     10958
     10959    /** @private */
     10960    function startAngle(n) {
     10961      return (n.parentNode ? n.minBreadth - .25 : 0) * 2 * Math.PI;
     10962    }
     10963
     10964    /** @private */
     10965    function angle(n) {
     10966      return (n.parentNode ? n.maxBreadth - n.minBreadth : 1) * 2 * Math.PI;
     10967    }
     10968
     10969    for (var i = 0; i < nodes.length; i++) {
     10970      var n = nodes[i];
     10971      n.x = x(n);
     10972      n.y = y(n);
     10973      if (orient == "radial") {
     10974        n.innerRadius = innerRadius(n);
     10975        n.outerRadius = outerRadius(n);
     10976        n.startAngle = startAngle(n);
     10977        n.angle = angle(n);
     10978        n.midAngle = n.startAngle + n.angle / 2;
     10979      } else {
     10980        n.midAngle = horizontal ? -Math.PI / 2 : 0;
     10981      }
     10982      n.dx = dx(n);
     10983      n.dy = dy(n);
     10984    }
     10985  }
     10986};
     10987/**
     10988 * Constructs a new, empty grid layout. Layouts are not typically constructed
     10989 * directly; instead, they are added to an existing panel via
     10990 * {@link pv.Mark#add}.
     10991 *
     10992 * @class Implements a grid layout with regularly-sized rows and columns. The
     10993 * number of rows and columns are determined from their respective
     10994 * properties. For example, the 2&times;3 array:
     10995 *
     10996 * <pre>1 2 3
     10997 * 4 5 6</pre>
     10998 *
     10999 * can be represented using the <tt>rows</tt> property as:
     11000 *
     11001 * <pre>[[1, 2, 3], [4, 5, 6]]</pre>
     11002 *
     11003 * If your data is in column-major order, you can equivalently use the
     11004 * <tt>columns</tt> property. If the <tt>rows</tt> property is an array, it
     11005 * takes priority over the <tt>columns</tt> property. The data is implicitly
     11006 * transposed, as if the {@link pv.transpose} operator were applied.
     11007 *
     11008 * <p>This layout exports a single <tt>cell</tt> mark prototype, which is
     11009 * intended to be used with a bar, panel, layout, or subclass thereof. The data
     11010 * property of the cell prototype is defined as the elements in the array. For
     11011 * example, if the array is a two-dimensional array of values in the range
     11012 * [0,1], a simple heatmap can be generated as:
     11013 *
     11014 * <pre>vis.add(pv.Layout.Grid)
     11015 *     .rows(arrays)
     11016 *   .cell.add(pv.Bar)
     11017 *     .fillStyle(pv.ramp("white", "black"))</pre>
     11018 *
     11019 * The grid subdivides the full width and height of the parent panel into equal
     11020 * rectangles. Note, however, that for large, interactive, or animated heatmaps,
     11021 * you may see significantly better performance through dynamic {@link pv.Image}
     11022 * generation.
     11023 *
     11024 * <p>For irregular grids using value-based spatial partitioning, see {@link
     11025 * pv.Layout.Treemap}.
     11026 *
     11027 * @extends pv.Layout
     11028 */
     11029pv.Layout.Grid = function() {
     11030  pv.Layout.call(this);
     11031  var that = this;
     11032
     11033  /**
     11034   * The cell prototype. This prototype is intended to be used with a bar,
     11035   * panel, or layout (or subclass thereof) to render the grid cells.
     11036   *
     11037   * @type pv.Mark
     11038   * @name pv.Layout.Grid.prototype.cell
     11039   */
     11040  (this.cell = new pv.Mark()
     11041      .data(function() {
     11042          return that.scene[that.index].$grid;
     11043        })
     11044      .width(function() {
     11045          return that.width() / that.cols();
     11046        })
     11047      .height(function() {
     11048          return that.height() / that.rows();
     11049        })
     11050      .left(function() {
     11051          return this.width() * (this.index % that.cols());
     11052        })
     11053      .top(function() {
     11054          return this.height() * Math.floor(this.index / that.cols());
     11055        })).parent = this;
     11056};
     11057
     11058pv.Layout.Grid.prototype = pv.extend(pv.Layout)
     11059    .property("rows")
     11060    .property("cols");
     11061
     11062/**
     11063 * Default properties for grid layouts. By default, there is one row and one
     11064 * column, and the data is the propagated to the child cell.
     11065 *
     11066 * @type pv.Layout.Grid
     11067 */
     11068pv.Layout.Grid.prototype.defaults = new pv.Layout.Grid()
     11069    .extend(pv.Layout.prototype.defaults)
     11070    .rows(1)
     11071    .cols(1);
     11072
     11073/** @private */
     11074pv.Layout.Grid.prototype.buildImplied = function(s) {
     11075  pv.Layout.prototype.buildImplied.call(this, s);
     11076  var r = s.rows, c = s.cols;
     11077  if (typeof c == "object") r = pv.transpose(c);
     11078  if (typeof r == "object") {
     11079    s.$grid = pv.blend(r);
     11080    s.rows = r.length;
     11081    s.cols = r[0] ? r[0].length : 0;
     11082  } else {
     11083    s.$grid = pv.repeat([s.data], r * c);
     11084  }
     11085};
     11086
     11087/**
     11088 * The number of rows. This property can also be specified as the data in
     11089 * row-major order; in this case, the rows property is implicitly set to the
     11090 * length of the array, and the cols property is set to the length of the first
     11091 * element in the array.
     11092 *
     11093 * @type number
     11094 * @name pv.Layout.Grid.prototype.rows
     11095 */
     11096
     11097/**
     11098 * The number of columns. This property can also be specified as the data in
     11099 * column-major order; in this case, the cols property is implicitly set to the
     11100 * length of the array, and the rows property is set to the length of the first
     11101 * element in the array.
     11102 *
     11103 * @type number
     11104 * @name pv.Layout.Grid.prototype.cols
     11105 */
     11106/**
     11107 * Constructs a new, empty stack layout. Layouts are not typically constructed
     11108 * directly; instead, they are added to an existing panel via
     11109 * {@link pv.Mark#add}.
     11110 *
     11111 * @class Implements a layout for stacked visualizations, ranging from simple
     11112 * stacked bar charts to more elaborate "streamgraphs" composed of stacked
     11113 * areas. Stack layouts uses length as a visual encoding, as opposed to
     11114 * position, as the layers do not share an aligned axis.
     11115 *
     11116 * <p>Marks can be stacked vertically or horizontally. For example,
     11117 *
     11118 * <pre>vis.add(pv.Layout.Stack)
     11119 *     .layers([[1, 1.2, 1.7, 1.5, 1.7],
     11120 *              [.5, 1, .8, 1.1, 1.3],
     11121 *              [.2, .5, .8, .9, 1]])
     11122 *     .x(function() this.index * 35)
     11123 *     .y(function(d) d * 40)
     11124 *   .layer.add(pv.Area);</pre>
     11125 *
     11126 * specifies a vertically-stacked area chart, using the default "bottom-left"
     11127 * orientation with "zero" offset. This visualization can be easily changed into
     11128 * a streamgraph using the "wiggle" offset, which attempts to minimize change in
     11129 * slope weighted by layer thickness. See the {@link #offset} property for more
     11130 * supported streamgraph algorithms.
     11131 *
     11132 * <p>In the simplest case, the layer data can be specified as a two-dimensional
     11133 * array of numbers. The <tt>x</tt> and <tt>y</tt> psuedo-properties are used to
     11134 * define the thickness of each layer at the given position, respectively; in
     11135 * the above example of the "bottom-left" orientation, the <tt>x</tt> and
     11136 * <tt>y</tt> psuedo-properties are equivalent to the <tt>left</tt> and
     11137 * <tt>height</tt> properties that you might use if you implemented a stacked
     11138 * area by hand.
     11139 *
     11140 * <p>The advantage of using the stack layout is that the baseline, i.e., the
     11141 * <tt>bottom</tt> property is computed automatically using the specified offset
     11142 * algorithm. In addition, the order of layers can be computed using a built-in
     11143 * algorithm via the <tt>order</tt> property.
     11144 *
     11145 * <p>With the exception of the "expand" <tt>offset</tt>, the stack layout does
     11146 * not perform any automatic scaling of data; the values returned from
     11147 * <tt>x</tt> and <tt>y</tt> specify pixel sizes. To simplify scaling math, use
     11148 * this layout in conjunction with {@link pv.Scale.linear} or similar.
     11149 *
     11150 * <p>In other cases, the <tt>values</tt> psuedo-property can be used to define
     11151 * the data more flexibly. As with a typical panel &amp; area, the
     11152 * <tt>layers</tt> property corresponds to the data in the enclosing panel,
     11153 * while the <tt>values</tt> psuedo-property corresponds to the data for the
     11154 * area within the panel. For example, given an array of data values:
     11155 *
     11156 * <pre>var crimea = [
     11157 *  { date: "4/1854", wounds: 0, other: 110, disease: 110 },
     11158 *  { date: "5/1854", wounds: 0, other: 95, disease: 105 },
     11159 *  { date: "6/1854", wounds: 0, other: 40, disease: 95 },
     11160 *  ...</pre>
     11161 *
     11162 * and a corresponding array of series names:
     11163 *
     11164 * <pre>var causes = ["wounds", "other", "disease"];</pre>
     11165 *
     11166 * Separate layers can be defined for each cause like so:
     11167 *
     11168 * <pre>vis.add(pv.Layout.Stack)
     11169 *     .layers(causes)
     11170 *     .values(crimea)
     11171 *     .x(function(d) x(d.date))
     11172 *     .y(function(d, p) y(d[p]))
     11173 *   .layer.add(pv.Area)
     11174 *     ...</pre>
     11175 *
     11176 * As with the panel &amp; area case, the datum that is passed to the
     11177 * psuedo-properties <tt>x</tt> and <tt>y</tt> are the values (an element in
     11178 * <tt>crimea</tt>); the second argument is the layer data (a string in
     11179 * <tt>causes</tt>). Additional arguments specify the data of enclosing panels,
     11180 * if any.
     11181 *
     11182 * @extends pv.Layout
     11183 */
     11184pv.Layout.Stack = function() {
     11185  pv.Layout.call(this);
     11186  var that = this,
     11187      /** @ignore */ none = function() { return null; },
     11188      prop = {t: none, l: none, r: none, b: none, w: none, h: none},
     11189      values,
     11190      buildImplied = that.buildImplied;
     11191
     11192  /** @private Proxy the given property on the layer. */
     11193  function proxy(name) {
     11194    return function() {
     11195        return prop[name](this.parent.index, this.index);
     11196      };
     11197  }
     11198
     11199  /** @private Compute the layout! */
     11200  this.buildImplied = function(s) {
     11201    buildImplied.call(this, s);
     11202
     11203    var data = s.layers,
     11204        n = data.length,
     11205        m,
     11206        orient = s.orient,
     11207        horizontal = /^(top|bottom)\b/.test(orient),
     11208        h = this.parent[horizontal ? "height" : "width"](),
     11209        x = [],
     11210        y = [],
     11211        dy = [];
     11212
     11213    /*
     11214     * Iterate over the data, evaluating the values, x and y functions. The
     11215     * context in which the x and y psuedo-properties are evaluated is a
     11216     * pseudo-mark that is a grandchild of this layout.
     11217     */
     11218    var stack = pv.Mark.stack, o = {parent: {parent: this}};
     11219    stack.unshift(null);
     11220    values = [];
     11221    for (var i = 0; i < n; i++) {
     11222      dy[i] = [];
     11223      y[i] = [];
     11224      o.parent.index = i;
     11225      stack[0] = data[i];
     11226      values[i] = this.$values.apply(o.parent, stack);
     11227      if (!i) m = values[i].length;
     11228      stack.unshift(null);
     11229      for (var j = 0; j < m; j++) {
     11230        stack[0] = values[i][j];
     11231        o.index = j;
     11232        if (!i) x[j] = this.$x.apply(o, stack);
     11233        dy[i][j] = this.$y.apply(o, stack);
     11234      }
     11235      stack.shift();
     11236    }
     11237    stack.shift();
     11238
     11239    /* order */
     11240    var index;
     11241    switch (s.order) {
     11242      case "inside-out": {
     11243        var max = dy.map(function(v) { return pv.max.index(v); }),
     11244            map = pv.range(n).sort(function(a, b) { return max[a] - max[b]; }),
     11245            sums = dy.map(function(v) { return pv.sum(v); }),
     11246            top = 0,
     11247            bottom = 0,
     11248            tops = [],
     11249            bottoms = [];
     11250        for (var i = 0; i < n; i++) {
     11251          var j = map[i];
     11252          if (top < bottom) {
     11253            top += sums[j];
     11254            tops.push(j);
     11255          } else {
     11256            bottom += sums[j];
     11257            bottoms.push(j);
     11258          }
     11259        }
     11260        index = bottoms.reverse().concat(tops);
     11261        break;
     11262      }
     11263      case "reverse": index = pv.range(n - 1, -1, -1); break;
     11264      default: index = pv.range(n); break;
     11265    }
     11266
     11267    /* offset */
     11268    switch (s.offset) {
     11269      case "silohouette": {
     11270        for (var j = 0; j < m; j++) {
     11271          var o = 0;
     11272          for (var i = 0; i < n; i++) o += dy[i][j];
     11273          y[index[0]][j] = (h - o) / 2;
     11274        }
     11275        break;
     11276      }
     11277      case "wiggle": {
     11278        var o = 0;
     11279        for (var i = 0; i < n; i++) o += dy[i][0];
     11280        y[index[0]][0] = o = (h - o) / 2;
     11281        for (var j = 1; j < m; j++) {
     11282          var s1 = 0, s2 = 0, dx = x[j] - x[j - 1];
     11283          for (var i = 0; i < n; i++) s1 += dy[i][j];
     11284          for (var i = 0; i < n; i++) {
     11285            var s3 = (dy[index[i]][j] - dy[index[i]][j - 1]) / (2 * dx);
     11286            for (var k = 0; k < i; k++) {
     11287              s3 += (dy[index[k]][j] - dy[index[k]][j - 1]) / dx;
     11288            }
     11289            s2 += s3 * dy[index[i]][j];
     11290          }
     11291          y[index[0]][j] = o -= s1 ? s2 / s1 * dx : 0;
     11292        }
     11293        break;
     11294      }
     11295      case "expand": {
     11296        for (var j = 0; j < m; j++) {
     11297          y[index[0]][j] = 0;
     11298          var k = 0;
     11299          for (var i = 0; i < n; i++) k += dy[i][j];
     11300          if (k) {
     11301            k = h / k;
     11302            for (var i = 0; i < n; i++) dy[i][j] *= k;
     11303          } else {
     11304            k = h / n;
     11305            for (var i = 0; i < n; i++) dy[i][j] = k;
     11306          }
     11307        }
     11308        break;
     11309      }
     11310      default: {
     11311        for (var j = 0; j < m; j++) y[index[0]][j] = 0;
     11312        break;
     11313      }
     11314    }
     11315
     11316    /* Propagate the offset to the other series. */
     11317    for (var j = 0; j < m; j++) {
     11318      var o = y[index[0]][j];
     11319      for (var i = 1; i < n; i++) {
     11320        o += dy[index[i - 1]][j];
     11321        y[index[i]][j] = o;
     11322      }
     11323    }
     11324
     11325    /* Find the property definitions for dynamic substitution. */
     11326    var i = orient.indexOf("-"),
     11327        pdy = horizontal ? "h" : "w",
     11328        px = i < 0 ? (horizontal ? "l" : "b") : orient.charAt(i + 1),
     11329        py = orient.charAt(0);
     11330    for (var p in prop) prop[p] = none;
     11331    prop[px] = function(i, j) { return x[j]; };
     11332    prop[py] = function(i, j) { return y[i][j]; };
     11333    prop[pdy] = function(i, j) { return dy[i][j]; };
     11334  };
     11335
     11336  /**
     11337   * The layer prototype. This prototype is intended to be used with an area,
     11338   * bar or panel mark (or subclass thereof). Other mark types may be possible,
     11339   * though note that the stack layout is not currently designed to support
     11340   * radial stacked visualizations using wedges.
     11341   *
     11342   * <p>The layer is not a direct child of the stack layout; a hidden panel is
     11343   * used to replicate layers.
     11344   *
     11345   * @type pv.Mark
     11346   * @name pv.Layout.Stack.prototype.layer
     11347   */
     11348  this.layer = new pv.Mark()
     11349      .data(function() { return values[this.parent.index]; })
     11350      .top(proxy("t"))
     11351      .left(proxy("l"))
     11352      .right(proxy("r"))
     11353      .bottom(proxy("b"))
     11354      .width(proxy("w"))
     11355      .height(proxy("h"));
     11356
     11357  this.layer.add = function(type) {
     11358    return that.add(pv.Panel)
     11359        .data(function() { return that.layers(); })
     11360      .add(type)
     11361        .extend(this);
     11362  };
     11363};
     11364
     11365pv.Layout.Stack.prototype = pv.extend(pv.Layout)
     11366    .property("orient", String)
     11367    .property("offset", String)
     11368    .property("order", String)
     11369    .property("layers");
     11370
     11371/**
     11372 * Default properties for stack layouts. The default orientation is
     11373 * "bottom-left", the default offset is "zero", and the default layers is
     11374 * <tt>[[]]</tt>.
     11375 *
     11376 * @type pv.Layout.Stack
     11377 */
     11378pv.Layout.Stack.prototype.defaults = new pv.Layout.Stack()
     11379    .extend(pv.Layout.prototype.defaults)
     11380    .orient("bottom-left")
     11381    .offset("zero")
     11382    .layers([[]]);
     11383
     11384/** @private */
     11385pv.Layout.Stack.prototype.$x
     11386    = /** @private */ pv.Layout.Stack.prototype.$y
     11387    = function() { return 0; };
     11388
     11389/**
     11390 * The x psuedo-property; determines the position of the value within the layer.
     11391 * This typically corresponds to the independent variable. For example, with the
     11392 * default "bottom-left" orientation, this function defines the "left" property.
     11393 *
     11394 * @param {function} f the x function.
     11395 * @returns {pv.Layout.Stack} this.
     11396 */
     11397pv.Layout.Stack.prototype.x = function(f) {
     11398  /** @private */ this.$x = pv.functor(f);
     11399  return this;
     11400};
     11401
     11402/**
     11403 * The y psuedo-property; determines the thickness of the layer at the given
     11404 * value.  This typically corresponds to the dependent variable. For example,
     11405 * with the default "bottom-left" orientation, this function defines the
     11406 * "height" property.
     11407 *
     11408 * @param {function} f the y function.
     11409 * @returns {pv.Layout.Stack} this.
     11410 */
     11411pv.Layout.Stack.prototype.y = function(f) {
     11412  /** @private */ this.$y = pv.functor(f);
     11413  return this;
     11414};
     11415
     11416/** @private The default value function; identity. */
     11417pv.Layout.Stack.prototype.$values = pv.identity;
     11418
     11419/**
     11420 * The values function; determines the values for a given layer. The default
     11421 * value is the identity function, which assumes that the layers property is
     11422 * specified as a two-dimensional (i.e., nested) array.
     11423 *
     11424 * @param {function} f the values function.
     11425 * @returns {pv.Layout.Stack} this.
     11426 */
     11427pv.Layout.Stack.prototype.values = function(f) {
     11428  this.$values = pv.functor(f);
     11429  return this;
     11430};
     11431
     11432/**
     11433 * The layer data in row-major order. The value of this property is typically a
     11434 * two-dimensional (i.e., nested) array, but any array can be used, provided the
     11435 * values psuedo-property is defined accordingly.
     11436 *
     11437 * @type array[]
     11438 * @name pv.Layout.Stack.prototype.layers
     11439 */
     11440
     11441/**
     11442 * The layer orientation. The following values are supported:<ul>
     11443 *
     11444 * <li>bottom-left == bottom
     11445 * <li>bottom-right
     11446 * <li>top-left == top
     11447 * <li>top-right
     11448 * <li>left-top
     11449 * <li>left-bottom == left
     11450 * <li>right-top
     11451 * <li>right-bottom == right
     11452 *
     11453 * </ul>. The default value is "bottom-left", which means that the layers will
     11454 * be built from the bottom-up, and the values within layers will be laid out
     11455 * from left-to-right.
     11456 *
     11457 * <p>Note that with non-zero baselines, some orientations may give similar
     11458 * results. For example, offset("silohouette") centers the layers, resulting in
     11459 * a streamgraph. Thus, the orientations "bottom-left" and "top-left" will
     11460 * produce similar results, differing only in the layer order.
     11461 *
     11462 * @type string
     11463 * @name pv.Layout.Stack.prototype.orient
     11464 */
     11465
     11466/**
     11467 * The layer order. The following values are supported:<ul>
     11468 *
     11469 * <li><i>null</i> - use given layer order.
     11470 * <li>inside-out - sort by maximum value, with balanced order.
     11471 * <li>reverse - use reverse of given layer order.
     11472 *
     11473 * </ul>For details on the inside-out order algorithm, refer to "Stacked Graphs
     11474 * -- Geometry &amp; Aesthetics" by L. Byron and M. Wattenberg, IEEE TVCG
     11475 * November/December 2008.
     11476 *
     11477 * @type string
     11478 * @name pv.Layout.Stack.prototype.order
     11479 */
     11480
     11481/**
     11482 * The layer offset; the y-position of the bottom of the lowest layer. The
     11483 * following values are supported:<ul>
     11484 *
     11485 * <li>zero - use a zero baseline, i.e., the y-axis.
     11486 * <li>silohouette - center the stream, i.e., ThemeRiver.
     11487 * <li>wiggle - minimize weighted change in slope.
     11488 * <li>expand - expand layers to fill the enclosing layout dimensions.
     11489 *
     11490 * </ul>For details on these offset algorithms, refer to "Stacked Graphs --
     11491 * Geometry &amp; Aesthetics" by L. Byron and M. Wattenberg, IEEE TVCG
     11492 * November/December 2008.
     11493 *
     11494 * @type string
     11495 * @name pv.Layout.Stack.prototype.offset
     11496 */
     11497/**
     11498 * Constructs a new, empty treemap layout. Layouts are not typically
     11499 * constructed directly; instead, they are added to an existing panel via
     11500 * {@link pv.Mark#add}.
     11501 *
     11502 * @class Implements a space-filling rectangular layout, with the hierarchy
     11503 * represented via containment. Treemaps represent nodes as boxes, with child
     11504 * nodes placed within parent boxes. The size of each box is proportional to the
     11505 * size of the node in the tree. This particular algorithm is taken from Bruls,
     11506 * D.M., C. Huizing, and J.J. van Wijk, <a
     11507 * href="http://www.win.tue.nl/~vanwijk/stm.pdf">"Squarified Treemaps"</a> in
     11508 * <i>Data Visualization 2000, Proceedings of the Joint Eurographics and IEEE
     11509 * TCVG Sumposium on Visualization</i>, 2000, pp. 33-42.
     11510 *
     11511 * <p>The meaning of the exported mark prototypes changes slightly in the
     11512 * space-filling implementation:<ul>
     11513 *
     11514 * <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Bar}. The node
     11515 * data is populated with <tt>dx</tt> and <tt>dy</tt> attributes, in addition to
     11516 * the standard <tt>x</tt> and <tt>y</tt> position attributes.
     11517 *
     11518 * <p><li><tt>leaf</tt> - for rendering leaf nodes only, with no fill or stroke
     11519 * style by default; typically a {@link pv.Panel} or another layout!
     11520 *
     11521 * <p><li><tt>link</tt> - unsupported; undefined. Links are encoded implicitly
     11522 * in the arrangement of the space-filling nodes.
     11523 *
     11524 * <p><li><tt>label</tt> - for rendering node labels; typically a
     11525 * {@link pv.Label}.
     11526 *
     11527 * </ul>For more details on how to use this layout, see
     11528 * {@link pv.Layout.Hierarchy}.
     11529 *
     11530 * @extends pv.Layout.Hierarchy
     11531 */
     11532pv.Layout.Treemap = function() {
     11533  pv.Layout.Hierarchy.call(this);
     11534
     11535  this.node
     11536      .strokeStyle("#fff")
     11537      .fillStyle("rgba(31, 119, 180, .25)")
     11538      .width(function(n) { return n.dx; })
     11539      .height(function(n) { return n.dy; });
     11540
     11541  this.label
     11542      .visible(function(n) { return !n.firstChild; })
     11543      .left(function(n) { return n.x + (n.dx / 2); })
     11544      .top(function(n) { return n.y + (n.dy / 2); })
     11545      .textAlign("center")
     11546      .textAngle(function(n) { return n.dx > n.dy ? 0 : -Math.PI / 2; });
     11547
     11548  (this.leaf = new pv.Mark()
     11549      .extend(this.node)
     11550      .fillStyle(null)
     11551      .strokeStyle(null)
     11552      .visible(function(n) { return !n.firstChild; })).parent = this;
     11553
     11554  /* Hide unsupported link. */
     11555  delete this.link;
     11556};
     11557
     11558pv.Layout.Treemap.prototype = pv.extend(pv.Layout.Hierarchy)
     11559    .property("round", Boolean)
     11560    .property("paddingLeft", Number)
     11561    .property("paddingRight", Number)
     11562    .property("paddingTop", Number)
     11563    .property("paddingBottom", Number)
     11564    .property("mode", String)
     11565    .property("order", String);
     11566
     11567/**
     11568 * Default propertiess for treemap layouts. The default mode is "squarify" and
     11569 * the default order is "ascending".
     11570 *
     11571 * @type pv.Layout.Treemap
     11572 */
     11573pv.Layout.Treemap.prototype.defaults = new pv.Layout.Treemap()
     11574    .extend(pv.Layout.Hierarchy.prototype.defaults)
     11575    .mode("squarify") // squarify, slice-and-dice, slice, dice
     11576    .order("ascending"); // ascending, descending, reverse, null
     11577
     11578/**
     11579 * Whether node sizes should be rounded to integer values. This has a similar
     11580 * effect to setting <tt>antialias(false)</tt> for node values, but allows the
     11581 * treemap algorithm to accumulate error related to pixel rounding.
     11582 *
     11583 * @type boolean
     11584 * @name pv.Layout.Treemap.prototype.round
     11585 */
     11586
     11587/**
     11588 * The left inset between parent add child in pixels. Defaults to 0.
     11589 *
     11590 * @type number
     11591 * @name pv.Layout.Treemap.prototype.paddingLeft
     11592 * @see #padding
     11593 */
     11594
     11595/**
     11596 * The right inset between parent add child in pixels. Defaults to 0.
     11597 *
     11598 * @type number
     11599 * @name pv.Layout.Treemap.prototype.paddingRight
     11600 * @see #padding
     11601 */
     11602
     11603/**
     11604 * The top inset between parent and child in pixels. Defaults to 0.
     11605 *
     11606 * @type number
     11607 * @name pv.Layout.Treemap.prototype.paddingTop
     11608 * @see #padding
     11609 */
     11610
     11611/**
     11612 * The bottom inset between parent and child in pixels. Defaults to 0.
     11613 *
     11614 * @type number
     11615 * @name pv.Layout.Treemap.prototype.paddingBottom
     11616 * @see #padding
     11617 */
     11618
     11619/**
     11620 * The treemap algorithm. The default value is "squarify". The "slice-and-dice"
     11621 * algorithm may also be used, which alternates between horizontal and vertical
     11622 * slices for different depths. In addition, the "slice" and "dice" algorithms
     11623 * may be specified explicitly to control whether horizontal or vertical slices
     11624 * are used, which may be useful for nested treemap layouts.
     11625 *
     11626 * @type string
     11627 * @name pv.Layout.Treemap.prototype.mode
     11628 * @see <a
     11629 * href="ftp://ftp.cs.umd.edu/pub/hcil/Reports-Abstracts-Bibliography/2001-06html/2001-06.pdf"
     11630 * >"Ordered Treemap Layouts"</a> by B. Shneiderman &amp; M. Wattenberg, IEEE
     11631 * InfoVis 2001.
     11632 */
     11633
     11634/**
     11635 * The sibling node order. A <tt>null</tt> value means to use the sibling order
     11636 * specified by the nodes property as-is; "reverse" will reverse the given
     11637 * order. The default value "ascending" will sort siblings in ascending order of
     11638 * size, while "descending" will do the reverse. For sorting based on data
     11639 * attributes other than size, use the default <tt>null</tt> for the order
     11640 * property, and sort the nodes beforehand using the {@link pv.Dom} operator.
     11641 *
     11642 * @type string
     11643 * @name pv.Layout.Treemap.prototype.order
     11644 */
     11645
     11646/**
     11647 * Alias for setting the left, right, top and bottom padding properties
     11648 * simultaneously.
     11649 *
     11650 * @see #paddingLeft
     11651 * @see #paddingRight
     11652 * @see #paddingTop
     11653 * @see #paddingBottom
     11654 * @returns {pv.Layout.Treemap} this.
     11655 */
     11656pv.Layout.Treemap.prototype.padding = function(n) {
     11657  return this.paddingLeft(n).paddingRight(n).paddingTop(n).paddingBottom(n);
     11658};
     11659
     11660/** @private The default size function. */
     11661pv.Layout.Treemap.prototype.$size = function(d) {
     11662  return Number(d.nodeValue);
     11663};
     11664
     11665/**
     11666 * Specifies the sizing function. By default, the size function uses the
     11667 * <tt>nodeValue</tt> attribute of nodes as a numeric value: <tt>function(d)
     11668 * Number(d.nodeValue)</tt>.
     11669 *
     11670 * <p>The sizing function is invoked for each leaf node in the tree, per the
     11671 * <tt>nodes</tt> property. For example, if the tree data structure represents a
     11672 * file system, with files as leaf nodes, and each file has a <tt>bytes</tt>
     11673 * attribute, you can specify a size function as:
     11674 *
     11675 * <pre>    .size(function(d) d.bytes)</pre>
     11676 *
     11677 * @param {function} f the new sizing function.
     11678 * @returns {pv.Layout.Treemap} this.
     11679 */
     11680pv.Layout.Treemap.prototype.size = function(f) {
     11681  this.$size = pv.functor(f);
     11682  return this;
     11683};
     11684
     11685/** @private */
     11686pv.Layout.Treemap.prototype.buildImplied = function(s) {
     11687  if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return;
     11688
     11689  var that = this,
     11690      nodes = s.nodes,
     11691      root = nodes[0],
     11692      stack = pv.Mark.stack,
     11693      left = s.paddingLeft,
     11694      right = s.paddingRight,
     11695      top = s.paddingTop,
     11696      bottom = s.paddingBottom,
     11697      /** @ignore */ size = function(n) { return n.size; },
     11698      round = s.round ? Math.round : Number,
     11699      mode = s.mode;
     11700
     11701  /** @private */
     11702  function slice(row, sum, horizontal, x, y, w, h) {
     11703    for (var i = 0, d = 0; i < row.length; i++) {
     11704      var n = row[i];
     11705      if (horizontal) {
     11706        n.x = x + d;
     11707        n.y = y;
     11708        d += n.dx = round(w * n.size / sum);
     11709        n.dy = h;
     11710      } else {
     11711        n.x = x;
     11712        n.y = y + d;
     11713        n.dx = w;
     11714        d += n.dy = round(h * n.size / sum);
     11715      }
     11716    }
     11717    if (n) { // correct on-axis rounding error
     11718      if (horizontal) {
     11719        n.dx += w - d;
     11720      } else {
     11721        n.dy += h - d;
     11722      }
     11723    }
     11724  }
     11725
     11726  /** @private */
     11727  function ratio(row, l) {
     11728    var rmax = -Infinity, rmin = Infinity, s = 0;
     11729    for (var i = 0; i < row.length; i++) {
     11730      var r = row[i].size;
     11731      if (r < rmin) rmin = r;
     11732      if (r > rmax) rmax = r;
     11733      s += r;
     11734    }
     11735    s = s * s;
     11736    l = l * l;
     11737    return Math.max(l * rmax / s, s / (l * rmin));
     11738  }
     11739
     11740  /** @private */
     11741  function layout(n, i) {
     11742    var x = n.x + left,
     11743        y = n.y + top,
     11744        w = n.dx - left - right,
     11745        h = n.dy - top - bottom;
     11746
     11747    /* Assume squarify by default. */
     11748    if (mode != "squarify") {
     11749      slice(n.childNodes, n.size,
     11750          mode == "slice" ? true
     11751          : mode == "dice" ? false
     11752          : i & 1, x, y, w, h);
     11753      return;
     11754    }
     11755
     11756    var row = [],
     11757        mink = Infinity,
     11758        l = Math.min(w, h),
     11759        k = w * h / n.size;
     11760
     11761    /* Abort if the size is nonpositive. */
     11762    if (n.size <= 0) return;
     11763
     11764    /* Scale the sizes to fill the current subregion. */
     11765    n.visitBefore(function(n) { n.size *= k; });
     11766
     11767    /** @private Position the specified nodes along one dimension. */
     11768    function position(row) {
     11769      var horizontal = w == l,
     11770          sum = pv.sum(row, size),
     11771          r = l ? round(sum / l) : 0;
     11772      slice(row, sum, horizontal, x, y, horizontal ? w : r, horizontal ? r : h);
     11773      if (horizontal) {
     11774        y += r;
     11775        h -= r;
     11776      } else {
     11777        x += r;
     11778        w -= r;
     11779      }
     11780      l = Math.min(w, h);
     11781      return horizontal;
     11782    }
     11783
     11784    var children = n.childNodes.slice(); // copy
     11785    while (children.length) {
     11786      var child = children[children.length - 1];
     11787      if (!child.size) {
     11788        children.pop();
     11789        continue;
     11790      }
     11791      row.push(child);
     11792
     11793      var k = ratio(row, l);
     11794      if (k <= mink) {
     11795        children.pop();
     11796        mink = k;
     11797      } else {
     11798        row.pop();
     11799        position(row);
     11800        row.length = 0;
     11801        mink = Infinity;
     11802      }
     11803    }
     11804
     11805    /* correct off-axis rounding error */
     11806    if (position(row)) for (var i = 0; i < row.length; i++) {
     11807      row[i].dy += h;
     11808    } else for (var i = 0; i < row.length; i++) {
     11809      row[i].dx += w;
     11810    }
     11811  }
     11812
     11813  /* Recursively compute the node depth and size. */
     11814  stack.unshift(null);
     11815  root.visitAfter(function(n, i) {
     11816      n.depth = i;
     11817      n.x = n.y = n.dx = n.dy = 0;
     11818      n.size = n.firstChild
     11819          ? pv.sum(n.childNodes, function(n) { return n.size; })
     11820          : that.$size.apply(that, (stack[0] = n, stack));
     11821    });
     11822  stack.shift();
     11823
     11824  /* Sort. */
     11825  switch (s.order) {
     11826    case "ascending": {
     11827      root.sort(function(a, b) { return a.size - b.size; });
     11828      break;
     11829    }
     11830    case "descending": {
     11831      root.sort(function(a, b) { return b.size - a.size; });
     11832      break;
     11833    }
     11834    case "reverse": root.reverse(); break;
     11835  }
     11836
     11837  /* Recursively compute the layout. */
     11838  root.x = 0;
     11839  root.y = 0;
     11840  root.dx = s.width;
     11841  root.dy = s.height;
     11842  root.visitBefore(layout);
     11843};
     11844/**
     11845 * Constructs a new, empty tree layout. Layouts are not typically constructed
     11846 * directly; instead, they are added to an existing panel via
     11847 * {@link pv.Mark#add}.
     11848 *
     11849 * @class Implements a node-link tree diagram using the Reingold-Tilford "tidy"
     11850 * tree layout algorithm. The specific algorithm used by this layout is based on
     11851 * <a href="http://citeseer.ist.psu.edu/buchheim02improving.html">"Improving
     11852 * Walker's Algorithm to Run in Linear Time"</A> by C. Buchheim, M. J&uuml;nger
     11853 * &amp; S. Leipert, Graph Drawing 2002. This layout supports both cartesian and
     11854 * radial orientations orientations for node-link diagrams.
     11855 *
     11856 * <p>The tree layout supports a "group" property, which if true causes siblings
     11857 * to be positioned closer together than unrelated nodes at the same depth. The
     11858 * layout can be configured using the <tt>depth</tt> and <tt>breadth</tt>
     11859 * properties, which control the increments in pixel space between nodes in both
     11860 * dimensions, similar to the indent layout.
     11861 *
     11862 * <p>For more details on how to use this layout, see
     11863 * {@link pv.Layout.Hierarchy}.
     11864 *
     11865 * @extends pv.Layout.Hierarchy
     11866 */
     11867pv.Layout.Tree = function() {
     11868  pv.Layout.Hierarchy.call(this);
     11869};
     11870
     11871pv.Layout.Tree.prototype = pv.extend(pv.Layout.Hierarchy)
     11872    .property("group", Number)
     11873    .property("breadth", Number)
     11874    .property("depth", Number)
     11875    .property("orient", String);
     11876
     11877/**
     11878 * Default properties for tree layouts. The default orientation is "top", the
     11879 * default group parameter is 1, and the default breadth and depth offsets are
     11880 * 15 and 60 respectively.
     11881 *
     11882 * @type pv.Layout.Tree
     11883 */
     11884pv.Layout.Tree.prototype.defaults = new pv.Layout.Tree()
     11885    .extend(pv.Layout.Hierarchy.prototype.defaults)
     11886    .group(1)
     11887    .breadth(15)
     11888    .depth(60)
     11889    .orient("top");
     11890
     11891/** @private */
     11892pv.Layout.Tree.prototype.buildImplied = function(s) {
     11893  if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return;
     11894
     11895  var nodes = s.nodes,
     11896      orient = s.orient,
     11897      depth = s.depth,
     11898      breadth = s.breadth,
     11899      group = s.group,
     11900      w = s.width,
     11901      h = s.height;
     11902
     11903  /** @private */
     11904  function firstWalk(v) {
     11905    var l, r, a;
     11906    if (!v.firstChild) {
     11907      if (l = v.previousSibling) {
     11908        v.prelim = l.prelim + distance(v.depth, true);
     11909      }
     11910    } else {
     11911      l = v.firstChild;
     11912      r = v.lastChild;
     11913      a = l; // default ancestor
     11914      for (var c = l; c; c = c.nextSibling) {
     11915        firstWalk(c);
     11916        a = apportion(c, a);
     11917      }
     11918      executeShifts(v);
     11919      var midpoint = .5 * (l.prelim + r.prelim);
     11920      if (l = v.previousSibling) {
     11921        v.prelim = l.prelim + distance(v.depth, true);
     11922        v.mod = v.prelim - midpoint;
     11923      } else {
     11924        v.prelim = midpoint;
     11925      }
     11926    }
     11927  }
     11928
     11929  /** @private */
     11930  function secondWalk(v, m, depth) {
     11931    v.breadth = v.prelim + m;
     11932    m += v.mod;
     11933    for (var c = v.firstChild; c; c = c.nextSibling) {
     11934      secondWalk(c, m, depth);
     11935    }
     11936  }
     11937
     11938  /** @private */
     11939  function apportion(v, a) {
     11940    var w = v.previousSibling;
     11941    if (w) {
     11942      var vip = v,
     11943          vop = v,
     11944          vim = w,
     11945          vom = v.parentNode.firstChild,
     11946          sip = vip.mod,
     11947          sop = vop.mod,
     11948          sim = vim.mod,
     11949          som = vom.mod,
     11950          nr = nextRight(vim),
     11951          nl = nextLeft(vip);
     11952      while (nr && nl) {
     11953        vim = nr;
     11954        vip = nl;
     11955        vom = nextLeft(vom);
     11956        vop = nextRight(vop);
     11957        vop.ancestor = v;
     11958        var shift = (vim.prelim + sim) - (vip.prelim + sip) + distance(vim.depth, false);
     11959        if (shift > 0) {
     11960          moveSubtree(ancestor(vim, v, a), v, shift);
     11961          sip += shift;
     11962          sop += shift;
     11963        }
     11964        sim += vim.mod;
     11965        sip += vip.mod;
     11966        som += vom.mod;
     11967        sop += vop.mod;
     11968        nr = nextRight(vim);
     11969        nl = nextLeft(vip);
     11970      }
     11971      if (nr && !nextRight(vop)) {
     11972        vop.thread = nr;
     11973        vop.mod += sim - sop;
     11974      }
     11975      if (nl && !nextLeft(vom)) {
     11976        vom.thread = nl;
     11977        vom.mod += sip - som;
     11978        a = v;
     11979      }
     11980    }
     11981    return a;
     11982  }
     11983
     11984  /** @private */
     11985  function nextLeft(v) {
     11986    return v.firstChild || v.thread;
     11987  }
     11988
     11989  /** @private */
     11990  function nextRight(v) {
     11991    return v.lastChild || v.thread;
     11992  }
     11993
     11994  /** @private */
     11995  function moveSubtree(wm, wp, shift) {
     11996    var subtrees = wp.number - wm.number;
     11997    wp.change -= shift / subtrees;
     11998    wp.shift += shift;
     11999    wm.change += shift / subtrees;
     12000    wp.prelim += shift;
     12001    wp.mod += shift;
     12002  }
     12003
     12004  /** @private */
     12005  function executeShifts(v) {
     12006    var shift = 0, change = 0;
     12007    for (var c = v.lastChild; c; c = c.previousSibling) {
     12008      c.prelim += shift;
     12009      c.mod += shift;
     12010      change += c.change;
     12011      shift += c.shift + change;
     12012    }
     12013  }
     12014
     12015  /** @private */
     12016  function ancestor(vim, v, a) {
     12017    return (vim.ancestor.parentNode == v.parentNode) ? vim.ancestor : a;
     12018  }
     12019
     12020  /** @private */
     12021  function distance(depth, siblings) {
     12022    return (siblings ? 1 : (group + 1)) / ((orient == "radial") ? depth : 1);
     12023  }
     12024
     12025  /* Initialize temporary layout variables. TODO: store separately. */
     12026  var root = nodes[0];
     12027  root.visitAfter(function(v, i) {
     12028      v.ancestor = v;
     12029      v.prelim = 0;
     12030      v.mod = 0;
     12031      v.change = 0;
     12032      v.shift = 0;
     12033      v.number = v.previousSibling ? (v.previousSibling.number + 1) : 0;
     12034      v.depth = i;
     12035    });
     12036
     12037  /* Compute the layout using Buchheim et al.'s algorithm. */
     12038  firstWalk(root);
     12039  secondWalk(root, -root.prelim, 0);
     12040
     12041  /** @private Returns the angle of the given node. */
     12042  function midAngle(n) {
     12043    return (orient == "radial") ? n.breadth / depth : 0;
     12044  }
     12045
     12046  /** @private */
     12047  function x(n) {
     12048    switch (orient) {
     12049      case "left": return n.depth;
     12050      case "right": return w - n.depth;
     12051      case "top":
     12052      case "bottom": return n.breadth + w / 2;
     12053      case "radial": return w / 2 + n.depth * Math.cos(midAngle(n));
     12054    }
     12055  }
     12056
     12057  /** @private */
     12058  function y(n) {
     12059    switch (orient) {
     12060      case "left":
     12061      case "right": return n.breadth + h / 2;
     12062      case "top": return n.depth;
     12063      case "bottom": return h - n.depth;
     12064      case "radial": return h / 2 + n.depth * Math.sin(midAngle(n));
     12065    }
     12066  }
     12067
     12068  /* Clear temporary layout variables; transform depth and breadth. */
     12069  root.visitAfter(function(v) {
     12070      v.breadth *= breadth;
     12071      v.depth *= depth;
     12072      v.midAngle = midAngle(v);
     12073      v.x = x(v);
     12074      v.y = y(v);
     12075      if (v.firstChild) v.midAngle += Math.PI;
     12076      delete v.breadth;
     12077      delete v.depth;
     12078      delete v.ancestor;
     12079      delete v.prelim;
     12080      delete v.mod;
     12081      delete v.change;
     12082      delete v.shift;
     12083      delete v.number;
     12084      delete v.thread;
     12085    });
     12086};
     12087
     12088/**
     12089 * The offset between siblings nodes; defaults to 15.
     12090 *
     12091 * @type number
     12092 * @name pv.Layout.Tree.prototype.breadth
     12093 */
     12094
     12095/**
     12096 * The offset between parent and child nodes; defaults to 60.
     12097 *
     12098 * @type number
     12099 * @name pv.Layout.Tree.prototype.depth
     12100 */
     12101
     12102/**
     12103 * The orientation. The default orientation is "top", which means that the root
     12104 * node is placed on the top edge, leaf nodes appear at the bottom, and internal
     12105 * nodes are in-between. The following orientations are supported:<ul>
     12106 *
     12107 * <li>left - left-to-right.
     12108 * <li>right - right-to-left.
     12109 * <li>top - top-to-bottom.
     12110 * <li>bottom - bottom-to-top.
     12111 * <li>radial - radially, with the root at the center.</ul>
     12112 *
     12113 * @type string
     12114 * @name pv.Layout.Tree.prototype.orient
     12115 */
     12116
     12117/**
     12118 * The sibling grouping, i.e., whether differentiating space is placed between
     12119 * sibling groups. The default is 1 (or true), causing sibling leaves to be
     12120 * separated by one breadth offset. Setting this to false (or 0) causes
     12121 * non-siblings to be adjacent.
     12122 *
     12123 * @type number
     12124 * @name pv.Layout.Tree.prototype.group
     12125 */
     12126/**
     12127 * Constructs a new, empty indent layout. Layouts are not typically constructed
     12128 * directly; instead, they are added to an existing panel via
     12129 * {@link pv.Mark#add}.
     12130 *
     12131 * @class Implements a hierarchical layout using the indent algorithm. This
     12132 * layout implements a node-link diagram where the nodes are presented in
     12133 * preorder traversal, and nodes are indented based on their depth from the
     12134 * root. This technique is used ubiquitously by operating systems to represent
     12135 * file directories; although it requires much vertical space, indented trees
     12136 * allow efficient <i>interactive</i> exploration of trees to find a specific
     12137 * node. In addition they allow rapid scanning of node labels, and multivariate
     12138 * data such as file sizes can be displayed adjacent to the hierarchy.
     12139 *
     12140 * <p>The indent layout can be configured using the <tt>depth</tt> and
     12141 * <tt>breadth</tt> properties, which control the increments in pixel space for
     12142 * each indent and row in the layout. This layout does not support multiple
     12143 * orientations; the root node is rendered in the top-left, while
     12144 * <tt>breadth</tt> is a vertical offset from the top, and <tt>depth</tt> is a
     12145 * horizontal offset from the left.
     12146 *
     12147 * <p>For more details on how to use this layout, see
     12148 * {@link pv.Layout.Hierarchy}.
     12149 *
     12150 * @extends pv.Layout.Hierarchy
     12151 */
     12152pv.Layout.Indent = function() {
     12153  pv.Layout.Hierarchy.call(this);
     12154  this.link.interpolate("step-after");
     12155};
     12156
     12157pv.Layout.Indent.prototype = pv.extend(pv.Layout.Hierarchy)
     12158    .property("depth", Number)
     12159    .property("breadth", Number);
     12160
     12161/**
     12162 * The horizontal offset between different levels of the tree; defaults to 15.
     12163 *
     12164 * @type number
     12165 * @name pv.Layout.Indent.prototype.depth
     12166 */
     12167
     12168/**
     12169 * The vertical offset between nodes; defaults to 15.
     12170 *
     12171 * @type number
     12172 * @name pv.Layout.Indent.prototype.breadth
     12173 */
     12174
     12175/**
     12176 * Default properties for indent layouts. By default the depth and breadth
     12177 * offsets are 15 pixels.
     12178 *
     12179 * @type pv.Layout.Indent
     12180 */
     12181pv.Layout.Indent.prototype.defaults = new pv.Layout.Indent()
     12182    .extend(pv.Layout.Hierarchy.prototype.defaults)
     12183    .depth(15)
     12184    .breadth(15);
     12185
     12186/** @private */
     12187pv.Layout.Indent.prototype.buildImplied = function(s) {
     12188  if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return;
     12189
     12190  var nodes = s.nodes,
     12191      bspace = s.breadth,
     12192      dspace = s.depth,
     12193      ax = 0,
     12194      ay = 0;
     12195
     12196  /** @private */
     12197  function position(n, breadth, depth) {
     12198    n.x = ax + depth++ * dspace;
     12199    n.y = ay + breadth++ * bspace;
     12200    n.midAngle = 0;
     12201    for (var c = n.firstChild; c; c = c.nextSibling) {
     12202      breadth = position(c, breadth, depth);
     12203    }
     12204    return breadth;
     12205  }
     12206
     12207  position(nodes[0], 1, 1);
     12208};
     12209/**
     12210 * Constructs a new, empty circle-packing layout. Layouts are not typically
     12211 * constructed directly; instead, they are added to an existing panel via
     12212 * {@link pv.Mark#add}.
     12213 *
     12214 * @class Implements a hierarchical layout using circle-packing. The meaning of
     12215 * the exported mark prototypes changes slightly in the space-filling
     12216 * implementation:<ul>
     12217 *
     12218 * <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Dot}.
     12219 *
     12220 * <p><li><tt>link</tt> - unsupported; undefined. Links are encoded implicitly
     12221 * in the arrangement of the space-filling nodes.
     12222 *
     12223 * <p><li><tt>label</tt> - for rendering node labels; typically a
     12224 * {@link pv.Label}.
     12225 *
     12226 * </ul>The pack layout support dynamic sizing for leaf nodes, if a
     12227 * {@link #size} psuedo-property is specified. The default size function returns
     12228 * 1, causing all leaf nodes to be sized equally, and all internal nodes to be
     12229 * sized by the number of leaf nodes they have as descendants.
     12230 *
     12231 * <p>The size function can be used in conjunction with the order property,
     12232 * which allows the nodes to the sorted by the computed size. Note: for sorting
     12233 * based on other data attributes, simply use the default <tt>null</tt> for the
     12234 * order property, and sort the nodes beforehand using the {@link pv.Dom}
     12235 * operator.
     12236 *
     12237 * <p>For more details on how to use this layout, see
     12238 * {@link pv.Layout.Hierarchy}.
     12239 *
     12240 * @extends pv.Layout.Hierarchy
     12241 * @see <a href="http://portal.acm.org/citation.cfm?id=1124772.1124851"
     12242 * >"Visualization of large hierarchical data by circle packing"</a> by W. Wang,
     12243 * H. Wang, G. Dai, and H. Wang, ACM CHI 2006.
     12244 */
     12245pv.Layout.Pack = function() {
     12246  pv.Layout.Hierarchy.call(this);
     12247
     12248  this.node
     12249      .radius(function(n) { return n.radius; })
     12250      .strokeStyle("rgb(31, 119, 180)")
     12251      .fillStyle("rgba(31, 119, 180, .25)");
     12252
     12253  this.label
     12254      .textAlign("center");
     12255
     12256  /* Hide unsupported link. */
     12257  delete this.link;
     12258};
     12259
     12260pv.Layout.Pack.prototype = pv.extend(pv.Layout.Hierarchy)
     12261    .property("spacing", Number)
     12262    .property("order", String); // ascending, descending, reverse, null
     12263
     12264/**
     12265 * Default properties for circle-packing layouts. The default spacing parameter
     12266 * is 1 and the default order is "ascending".
     12267 *
     12268 * @type pv.Layout.Pack
     12269 */
     12270pv.Layout.Pack.prototype.defaults = new pv.Layout.Pack()
     12271    .extend(pv.Layout.Hierarchy.prototype.defaults)
     12272    .spacing(1)
     12273    .order("ascending");
     12274
     12275/**
     12276 * The spacing parameter; defaults to 1, which provides a little bit of padding
     12277 * between sibling nodes and the enclosing circle. Larger values increase the
     12278 * spacing, by making the sibling nodes smaller; a value of zero makes the leaf
     12279 * nodes as large as possible, with no padding on enclosing circles.
     12280 *
     12281 * @type number
     12282 * @name pv.Layout.Pack.prototype.spacing
     12283 */
     12284
     12285/**
     12286 * The sibling node order. The default order is <tt>null</tt>, which means to
     12287 * use the sibling order specified by the nodes property as-is. A value of
     12288 * "ascending" will sort siblings in ascending order of size, while "descending"
     12289 * will do the reverse. For sorting based on data attributes other than size,
     12290 * use the default <tt>null</tt> for the order property, and sort the nodes
     12291 * beforehand using the {@link pv.Dom} operator.
     12292 *
     12293 * @see pv.Dom.Node#sort
     12294 * @type string
     12295 * @name pv.Layout.Pack.prototype.order
     12296 */
     12297
     12298/** @private The default size function. */
     12299pv.Layout.Pack.prototype.$radius = function() { return 1; };
     12300
     12301// TODO is it possible for spacing to operate in pixel space?
     12302// Right now it appears to be multiples of the smallest radius.
     12303
     12304/**
     12305 * Specifies the sizing function. By default, a sizing function is disabled and
     12306 * all nodes are given constant size. The sizing function is invoked for each
     12307 * leaf node in the tree (passed to the constructor).
     12308 *
     12309 * <p>For example, if the tree data structure represents a file system, with
     12310 * files as leaf nodes, and each file has a <tt>bytes</tt> attribute, you can
     12311 * specify a size function as:
     12312 *
     12313 * <pre>    .size(function(d) d.bytes)</pre>
     12314 *
     12315 * As with other properties, a size function may specify additional arguments to
     12316 * access the data associated with the layout and any enclosing panels.
     12317 *
     12318 * @param {function} f the new sizing function.
     12319 * @returns {pv.Layout.Pack} this.
     12320 */
     12321pv.Layout.Pack.prototype.size = function(f) {
     12322  this.$radius = typeof f == "function"
     12323      ? function() { return Math.sqrt(f.apply(this, arguments)); }
     12324      : (f = Math.sqrt(f), function() { return f; });
     12325  return this;
     12326};
     12327
     12328/** @private */
     12329pv.Layout.Pack.prototype.buildImplied = function(s) {
     12330  if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return;
     12331
     12332  var that = this,
     12333      nodes = s.nodes,
     12334      root = nodes[0];
     12335
     12336  /** @private Compute the radii of the leaf nodes. */
     12337  function radii(nodes) {
     12338    var stack = pv.Mark.stack;
     12339    stack.unshift(null);
     12340    for (var i = 0, n = nodes.length; i < n; i++) {
     12341      var c = nodes[i];
     12342      if (!c.firstChild) {
     12343        c.radius = that.$radius.apply(that, (stack[0] = c, stack));
     12344      }
     12345    }
     12346    stack.shift();
     12347  }
     12348
     12349  /** @private */
     12350  function packTree(n) {
     12351    var nodes = [];
     12352    for (var c = n.firstChild; c; c = c.nextSibling) {
     12353      if (c.firstChild) c.radius = packTree(c);
     12354      c.n = c.p = c;
     12355      nodes.push(c);
     12356    }
     12357
     12358    /* Sort. */
     12359    switch (s.order) {
     12360      case "ascending": {
     12361        nodes.sort(function(a, b) { return a.radius - b.radius; });
     12362        break;
     12363      }
     12364      case "descending": {
     12365        nodes.sort(function(a, b) { return b.radius - a.radius; });
     12366        break;
     12367      }
     12368      case "reverse": nodes.reverse(); break;
     12369    }
     12370
     12371    return packCircle(nodes);
     12372  }
     12373
     12374  /** @private */
     12375  function packCircle(nodes) {
     12376    var xMin = Infinity,
     12377        xMax = -Infinity,
     12378        yMin = Infinity,
     12379        yMax = -Infinity,
     12380        a, b, c, j, k;
     12381
     12382    /** @private */
     12383    function bound(n) {
     12384      xMin = Math.min(n.x - n.radius, xMin);
     12385      xMax = Math.max(n.x + n.radius, xMax);
     12386      yMin = Math.min(n.y - n.radius, yMin);
     12387      yMax = Math.max(n.y + n.radius, yMax);
     12388    }
     12389
     12390    /** @private */
     12391    function insert(a, b) {
     12392      var c = a.n;
     12393      a.n = b;
     12394      b.p = a;
     12395      b.n = c;
     12396      c.p = b;
     12397    }
     12398
     12399    /** @private */
     12400    function splice(a, b) {
     12401      a.n = b;
     12402      b.p = a;
     12403    }
     12404
     12405    /** @private */
     12406    function intersects(a, b) {
     12407      var dx = b.x - a.x,
     12408          dy = b.y - a.y,
     12409          dr = a.radius + b.radius;
     12410      return (dr * dr - dx * dx - dy * dy) > .001; // within epsilon
     12411    }
     12412
     12413    /* Create first node. */
     12414    a = nodes[0];
     12415    a.x = -a.radius;
     12416    a.y = 0;
     12417    bound(a);
     12418
     12419    /* Create second node. */
     12420    if (nodes.length > 1) {
     12421      b = nodes[1];
     12422      b.x = b.radius;
     12423      b.y = 0;
     12424      bound(b);
     12425
     12426      /* Create third node and build chain. */
     12427      if (nodes.length > 2) {
     12428        c = nodes[2];
     12429        place(a, b, c);
     12430        bound(c);
     12431        insert(a, c);
     12432        a.p = c;
     12433        insert(c, b);
     12434        b = a.n;
     12435
     12436        /* Now iterate through the rest. */
     12437        for (var i = 3; i < nodes.length; i++) {
     12438          place(a, b, c = nodes[i]);
     12439
     12440          /* Search for the closest intersection. */
     12441          var isect = 0, s1 = 1, s2 = 1;
     12442          for (j = b.n; j != b; j = j.n, s1++) {
     12443            if (intersects(j, c)) {
     12444              isect = 1;
     12445              break;
     12446            }
     12447          }
     12448          if (isect == 1) {
     12449            for (k = a.p; k != j.p; k = k.p, s2++) {
     12450              if (intersects(k, c)) {
     12451                if (s2 < s1) {
     12452                  isect = -1;
     12453                  j = k;
     12454                }
     12455                break;
     12456              }
     12457            }
     12458          }
     12459
     12460          /* Update node chain. */
     12461          if (isect == 0) {
     12462            insert(a, c);
     12463            b = c;
     12464            bound(c);
     12465          } else if (isect > 0) {
     12466            splice(a, j);
     12467            b = j;
     12468            i--;
     12469          } else if (isect < 0) {
     12470            splice(j, b);
     12471            a = j;
     12472            i--;
     12473          }
     12474        }
     12475      }
     12476    }
     12477
     12478    /* Re-center the circles and return the encompassing radius. */
     12479    var cx = (xMin + xMax) / 2,
     12480        cy = (yMin + yMax) / 2,
     12481        cr = 0;
     12482    for (var i = 0; i < nodes.length; i++) {
     12483      var n = nodes[i];
     12484      n.x -= cx;
     12485      n.y -= cy;
     12486      cr = Math.max(cr, n.radius + Math.sqrt(n.x * n.x + n.y * n.y));
     12487    }
     12488    return cr + s.spacing;
     12489  }
     12490
     12491  /** @private */
     12492  function place(a, b, c) {
     12493    var da = b.radius + c.radius,
     12494        db = a.radius + c.radius,
     12495        dx = b.x - a.x,
     12496        dy = b.y - a.y,
     12497        dc = Math.sqrt(dx * dx + dy * dy),
     12498        cos = (db * db + dc * dc - da * da) / (2 * db * dc),
     12499        theta = Math.acos(cos),
     12500        x = cos * db,
     12501        h = Math.sin(theta) * db;
     12502    dx /= dc;
     12503    dy /= dc;
     12504    c.x = a.x + x * dx + h * dy;
     12505    c.y = a.y + x * dy - h * dx;
     12506  }
     12507
     12508  /** @private */
     12509  function transform(n, x, y, k) {
     12510    for (var c = n.firstChild; c; c = c.nextSibling) {
     12511      c.x += n.x;
     12512      c.y += n.y;
     12513      transform(c, x, y, k);
     12514    }
     12515    n.x = x + k * n.x;
     12516    n.y = y + k * n.y;
     12517    n.radius *= k;
     12518  }
     12519
     12520  radii(nodes);
     12521
     12522  /* Recursively compute the layout. */
     12523  root.x = 0;
     12524  root.y = 0;
     12525  root.radius = packTree(root);
     12526
     12527  var w = this.width(),
     12528      h = this.height(),
     12529      k = 1 / Math.max(2 * root.radius / w, 2 * root.radius / h);
     12530  transform(root, w / 2, h / 2, k);
     12531};
     12532/**
     12533 * Constructs a new, empty force-directed layout. Layouts are not typically
     12534 * constructed directly; instead, they are added to an existing panel via
     12535 * {@link pv.Mark#add}.
     12536 *
     12537 * @class Implements force-directed network layout as a node-link diagram. This
     12538 * layout uses the Fruchterman-Reingold algorithm, which applies an attractive
     12539 * spring force between neighboring nodes, and a repulsive electrical charge
     12540 * force between all nodes. An additional drag force improves stability of the
     12541 * simulation. See {@link pv.Force.spring}, {@link pv.Force.drag} and {@link
     12542 * pv.Force.charge} for more details; note that the n-body charge force is
     12543 * approximated using the Barnes-Hut algorithm.
     12544 *
     12545 * <p>This layout is implemented on top of {@link pv.Simulation}, which can be
     12546 * used directly for more control over simulation parameters. The simulation
     12547 * uses Position Verlet integration, which does not compute velocities
     12548 * explicitly, but allows for easy geometric constraints, such as bounding the
     12549 * nodes within the layout panel. Many of the configuration properties supported
     12550 * by this layout are simply passed through to the underlying forces and
     12551 * constraints of the simulation.
     12552 *
     12553 * <p>Force layouts are typically interactive. The gradual movement of the nodes
     12554 * as they stabilize to a local stress minimum can help reveal the structure of
     12555 * the network, as can {@link pv.Behavior.drag}, which allows the user to pick
     12556 * up nodes and reposition them while the physics simulation continues. This
     12557 * layout can also be used with pan &amp; zoom behaviors for interaction.
     12558 *
     12559 * <p>To facilitate interaction, this layout by default automatically re-renders
     12560 * using a <tt>setInterval</tt> every 42 milliseconds. This can be disabled via
     12561 * the <tt>iterations</tt> property, which if non-null specifies the number of
     12562 * simulation iterations to run before the force-directed layout is finalized.
     12563 * Be careful not to use too high an iteration count, as this can lead to an
     12564 * annoying delay on page load.
     12565 *
     12566 * <p>As with other network layouts, the network data can be updated
     12567 * dynamically, provided the property cache is reset. See
     12568 * {@link pv.Layout.Network} for details. New nodes are initialized with random
     12569 * positions near the center. Alternatively, positions can be specified manually
     12570 * by setting the <tt>x</tt> and <tt>y</tt> attributes on nodes.
     12571 *
     12572 * @extends pv.Layout.Network
     12573 * @see <a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.13.8444&rep=rep1&type=pdf"
     12574 * >"Graph Drawing by Force-directed Placement"</a> by T. Fruchterman &amp;
     12575 * E. Reingold, Software--Practice &amp; Experience, November 1991.
     12576 */
     12577pv.Layout.Force = function() {
     12578  pv.Layout.Network.call(this);
     12579
     12580  /* Force-directed graphs can be messy, so reduce the link width. */
     12581  this.link.lineWidth(function(d, p) { return Math.sqrt(p.linkValue) * 1.5; });
     12582  this.label.textAlign("center");
     12583};
     12584
     12585pv.Layout.Force.prototype = pv.extend(pv.Layout.Network)
     12586    .property("bound", Boolean)
     12587    .property("iterations", Number)
     12588    .property("dragConstant", Number)
     12589    .property("chargeConstant", Number)
     12590    .property("chargeMinDistance", Number)
     12591    .property("chargeMaxDistance", Number)
     12592    .property("chargeTheta", Number)
     12593    .property("springConstant", Number)
     12594    .property("springDamping", Number)
     12595    .property("springLength", Number);
     12596
     12597/**
     12598 * The bound parameter; true if nodes should be constrained within the layout
     12599 * panel. Bounding is disabled by default. Currently the layout does not observe
     12600 * the radius of the nodes; strictly speaking, only the center of the node is
     12601 * constrained to be within the panel, with an additional 6-pixel offset for
     12602 * padding. A future enhancement could extend the bound constraint to observe
     12603 * the node's radius, which would also support bounding for variable-size nodes.
     12604 *
     12605 * <p>Note that if this layout is used in conjunction with pan &amp; zoom
     12606 * behaviors, those behaviors should have their bound parameter set to the same
     12607 * value.
     12608 *
     12609 * @type boolean
     12610 * @name pv.Layout.Force.prototype.bound
     12611 */
     12612
     12613/**
     12614 * The number of simulation iterations to run, or null if this layout is
     12615 * interactive. Force-directed layouts are interactive by default, using a
     12616 * <tt>setInterval</tt> to advance the physics simulation and re-render
     12617 * automatically.
     12618 *
     12619 * @type number
     12620 * @name pv.Layout.Force.prototype.iterations
     12621 */
     12622
     12623/**
     12624 * The drag constant, in the range [0,1]. A value of 0 means no drag (a
     12625 * perfectly frictionless environment), while a value of 1 means friction
     12626 * immediately cancels all momentum. The default value is 0.1, which provides a
     12627 * minimum amount of drag that helps stabilize bouncy springs; lower values may
     12628 * result in excessive bounciness, while higher values cause the simulation to
     12629 * take longer to converge.
     12630 *
     12631 * @type number
     12632 * @name pv.Layout.Force.prototype.dragConstant
     12633 * @see pv.Force.drag#constant
     12634 */
     12635
     12636/**
     12637 * The charge constant, which should be a negative number. The default value is
     12638 * -40; more negative values will result in a stronger repulsive force, which
     12639 * may lead to faster convergence at the risk of instability. Too strong
     12640 * repulsive charge forces can cause comparatively weak springs to be stretched
     12641 * well beyond their rest length, emphasizing global structure over local
     12642 * structure. A nonnegative value will break the Fruchterman-Reingold algorithm,
     12643 * and is for entertainment purposes only.
     12644 *
     12645 * @type number
     12646 * @name pv.Layout.Force.prototype.chargeConstant
     12647 * @see pv.Force.charge#constant
     12648 */
     12649
     12650/**
     12651 * The minimum distance at which charge forces are applied. The default minimum
     12652 * distance of 2 avoids applying forces that are two strong; because the physics
     12653 * simulation is run at discrete time intervals, it is possible for two same-
     12654 * charged particles to become very close or even a singularity! Since the
     12655 * charge force is inversely proportional to the square of the distance, very
     12656 * small distances can break the simulation.
     12657 *
     12658 * <p>In rare cases, two particles can become stuck on top of each other, as a
     12659 * minimum distance threshold will prevent the charge force from repelling them.
     12660 * However, this occurs very rarely because other forces and momentum typically
     12661 * cause the particles to become separated again, at which point the repulsive
     12662 * charge force kicks in.
     12663 *
     12664 * @type number
     12665 * @name pv.Layout.Force.prototype.chargeMinDistance
     12666 * @see pv.Force.charge#domain
     12667 */
     12668
     12669/**
     12670 * The maximum distance at which charge forces are applied. This improves
     12671 * performance by ignoring weak charge forces at great distances. Note that this
     12672 * parameter is partly redundant, as the Barnes-Hut algorithm for n-body forces
     12673 * already improves performance for far-away particles through approximation.
     12674 *
     12675 * @type number
     12676 * @name pv.Layout.Force.prototype.chargeMaxDistance
     12677 * @see pv.Force.charge#domain
     12678 */
     12679
     12680/**
     12681 * The Barnes-Hut approximation factor. The Barnes-Hut approximation criterion
     12682 * is the ratio of the size of the quadtree node to the distance from the point
     12683 * to the node's center of mass is beneath some threshold. The default value is
     12684 * 0.9.
     12685 *
     12686 * @type number
     12687 * @name pv.Layout.Force.prototype.chargeTheta
     12688 * @see pv.Force.charge#theta
     12689 */
     12690
     12691/**
     12692 * The spring constant, which should be a positive number. The default value is
     12693 * 0.1; greater values will result in a stronger attractive force, which may
     12694 * lead to faster convergence at the risk of instability. Too strong spring
     12695 * forces can cause comparatively weak charge forces to be ignored, emphasizing
     12696 * local structure over global structure. A nonpositive value will break the
     12697 * Fruchterman-Reingold algorithm, and is for entertainment purposes only.
     12698 *
     12699 * <p>The spring tension is automatically normalized using the inverse square
     12700 * root of the maximum link degree of attached nodes.
     12701 *
     12702 * @type number
     12703 * @name pv.Layout.Force.prototype.springConstant
     12704 * @see pv.Force.spring#constant
     12705 */
     12706
     12707/**
     12708 * The spring damping factor, in the range [0,1]. Damping functions identically
     12709 * to drag forces, damping spring bounciness by applying a force in the opposite
     12710 * direction of attached nodes' velocities. The default value is 0.3.
     12711 *
     12712 * <p>The spring damping is automatically normalized using the inverse square
     12713 * root of the maximum link degree of attached nodes.
     12714 *
     12715 * @type number
     12716 * @name pv.Layout.Force.prototype.springDamping
     12717 * @see pv.Force.spring#damping
     12718 */
     12719
     12720/**
     12721 * The spring rest length. The default value is 20 pixels. Larger values may be
     12722 * appropriate if the layout panel is larger, or if the nodes are rendered
     12723 * larger than the default dot size of 20.
     12724 *
     12725 * @type number
     12726 * @name pv.Layout.Force.prototype.springLength
     12727 * @see pv.Force.spring#length
     12728 */
     12729
     12730/**
     12731 * Default properties for force-directed layouts. The default drag constant is
     12732 * 0.1, the default charge constant is -40 (with a domain of [2, 500] and theta
     12733 * of 0.9), and the default spring constant is 0.1 (with a damping of 0.3 and a
     12734 * rest length of 20).
     12735 *
     12736 * @type pv.Layout.Force
     12737 */
     12738pv.Layout.Force.prototype.defaults = new pv.Layout.Force()
     12739    .extend(pv.Layout.Network.prototype.defaults)
     12740    .dragConstant(.1)
     12741    .chargeConstant(-40)
     12742    .chargeMinDistance(2)
     12743    .chargeMaxDistance(500)
     12744    .chargeTheta(.9)
     12745    .springConstant(.1)
     12746    .springDamping(.3)
     12747    .springLength(20);
     12748
     12749/** @private Initialize the physics simulation. */
     12750pv.Layout.Force.prototype.buildImplied = function(s) {
     12751
     12752  /* Any cached interactive layouts need to be rebound for the timer. */
     12753  if (pv.Layout.Network.prototype.buildImplied.call(this, s)) {
     12754    var f = s.$force;
     12755    if (f) {
     12756      f.next = this.binds.$force;
     12757      this.binds.$force = f;
     12758    }
     12759    return;
     12760  }
     12761
     12762  var that = this,
     12763      nodes = s.nodes,
     12764      links = s.links,
     12765      k = s.iterations,
     12766      w = s.width,
     12767      h = s.height;
     12768
     12769  /* Initialize positions randomly near the center. */
     12770  for (var i = 0, n; i < nodes.length; i++) {
     12771    n = nodes[i];
     12772    if (isNaN(n.x)) n.x = w / 2 + 40 * Math.random() - 20;
     12773    if (isNaN(n.y)) n.y = h / 2 + 40 * Math.random() - 20;
     12774  }
     12775
     12776  /* Initialize the simulation. */
     12777  var sim = pv.simulation(nodes);
     12778
     12779  /* Drag force. */
     12780  sim.force(pv.Force.drag(s.dragConstant));
     12781
     12782  /* Charge (repelling) force. */
     12783  sim.force(pv.Force.charge(s.chargeConstant)
     12784      .domain(s.chargeMinDistance, s.chargeMaxDistance)
     12785      .theta(s.chargeTheta));
     12786
     12787  /* Spring (attracting) force. */
     12788  sim.force(pv.Force.spring(s.springConstant)
     12789      .damping(s.springDamping)
     12790      .length(s.springLength)
     12791      .links(links));
     12792
     12793  /* Position constraint (for interactive dragging). */
     12794  sim.constraint(pv.Constraint.position());
     12795
     12796  /* Optionally add bound constraint. TODO: better padding. */
     12797  if (s.bound) {
     12798    sim.constraint(pv.Constraint.bound().x(6, w - 6).y(6, h - 6));
     12799  }
     12800
     12801  /** @private Returns the speed of the given node, to determine cooling. */
     12802  function speed(n) {
     12803    return n.fix ? 1 : n.vx * n.vx + n.vy * n.vy;
     12804  }
     12805
     12806  /*
     12807   * If the iterations property is null (the default), the layout is
     12808   * interactive. The simulation is run until the fastest particle drops below
     12809   * an arbitrary minimum speed. Although the timer keeps firing, this speed
     12810   * calculation is fast so there is minimal CPU overhead. Note: if a particle
     12811   * is fixed for interactivity, treat this as a high speed and resume
     12812   * simulation.
     12813   */
     12814  if (k == null) {
     12815    sim.step(); // compute initial previous velocities
     12816    sim.step(); // compute initial velocities
     12817
     12818    /* Add the simulation state to the bound list. */
     12819    var force = s.$force = this.binds.$force = {
     12820      next: this.binds.$force,
     12821      nodes: nodes,
     12822      min: 1e-4 * (links.length + 1),
     12823      sim: sim
     12824    };
     12825
     12826    /* Start the timer, if not already started. */
     12827    if (!this.$timer) this.$timer = setInterval(function() {
     12828      var render = false;
     12829      for (var f = that.binds.$force; f; f = f.next) {
     12830        if (pv.max(f.nodes, speed) > f.min) {
     12831          f.sim.step();
     12832          render = true;
     12833        }
     12834      }
     12835      if (render) that.render();
     12836    }, 42);
     12837  } else for (var i = 0; i < k; i++) {
     12838    sim.step();
     12839  }
     12840};
     12841/**
     12842 * Constructs a new, empty cluster layout. Layouts are not typically
     12843 * constructed directly; instead, they are added to an existing panel via
     12844 * {@link pv.Mark#add}.
     12845 *
     12846 * @class Implements a hierarchical layout using the cluster (or dendrogram)
     12847 * algorithm. This layout provides both node-link and space-filling
     12848 * implementations of cluster diagrams. In many ways it is similar to
     12849 * {@link pv.Layout.Partition}, except that leaf nodes are positioned at maximum
     12850 * depth, and the depth of internal nodes is based on their distance from their
     12851 * deepest descendant, rather than their distance from the root.
     12852 *
     12853 * <p>The cluster layout supports a "group" property, which if true causes
     12854 * siblings to be positioned closer together than unrelated nodes at the same
     12855 * depth. Unlike the partition layout, this layout does not support dynamic
     12856 * sizing for leaf nodes; all leaf nodes are the same size.
     12857 *
     12858 * <p>For more details on how to use this layout, see
     12859 * {@link pv.Layout.Hierarchy}.
     12860 *
     12861 * @see pv.Layout.Cluster.Fill
     12862 * @extends pv.Layout.Hierarchy
     12863 */
     12864pv.Layout.Cluster = function() {
     12865  pv.Layout.Hierarchy.call(this);
     12866  var interpolate, // cached interpolate
     12867      buildImplied = this.buildImplied;
     12868
     12869  /** @private Cache layout state to optimize properties. */
     12870  this.buildImplied = function(s) {
     12871    buildImplied.call(this, s);
     12872    interpolate
     12873        = /^(top|bottom)$/.test(s.orient) ? "step-before"
     12874        : /^(left|right)$/.test(s.orient) ? "step-after"
     12875        : "linear";
     12876  };
     12877
     12878  this.link.interpolate(function() { return interpolate; });
     12879};
     12880
     12881pv.Layout.Cluster.prototype = pv.extend(pv.Layout.Hierarchy)
     12882    .property("group", Number)
     12883    .property("orient", String)
     12884    .property("innerRadius", Number)
     12885    .property("outerRadius", Number);
     12886
     12887/**
     12888 * The group parameter; defaults to 0, disabling grouping of siblings. If this
     12889 * parameter is set to a positive number (or true, which is equivalent to 1),
     12890 * then additional space will be allotted between sibling groups. In other
     12891 * words, siblings (nodes that share the same parent) will be positioned more
     12892 * closely than nodes at the same depth that do not share a parent.
     12893 *
     12894 * @type number
     12895 * @name pv.Layout.Cluster.prototype.group
     12896 */
     12897
     12898/**
     12899 * The orientation. The default orientation is "top", which means that the root
     12900 * node is placed on the top edge, leaf nodes appear on the bottom edge, and
     12901 * internal nodes are in-between. The following orientations are supported:<ul>
     12902 *
     12903 * <li>left - left-to-right.
     12904 * <li>right - right-to-left.
     12905 * <li>top - top-to-bottom.
     12906 * <li>bottom - bottom-to-top.
     12907 * <li>radial - radially, with the root at the center.</ul>
     12908 *
     12909 * @type string
     12910 * @name pv.Layout.Cluster.prototype.orient
     12911 */
     12912
     12913/**
     12914 * The inner radius; defaults to 0. This property applies only to radial
     12915 * orientations, and can be used to compress the layout radially. Note that for
     12916 * the node-link implementation, the root node is always at the center,
     12917 * regardless of the value of this property; this property only affects internal
     12918 * and leaf nodes. For the space-filling implementation, a non-zero value of
     12919 * this property will result in the root node represented as a ring rather than
     12920 * a circle.
     12921 *
     12922 * @type number
     12923 * @name pv.Layout.Cluster.prototype.innerRadius
     12924 */
     12925
     12926/**
     12927 * The outer radius; defaults to fill the containing panel, based on the height
     12928 * and width of the layout. If the layout has no height and width specified, it
     12929 * will extend to fill the enclosing panel.
     12930 *
     12931 * @type number
     12932 * @name pv.Layout.Cluster.prototype.outerRadius
     12933 */
     12934
     12935/**
     12936 * Defaults for cluster layouts. The default group parameter is 0 and the
     12937 * default orientation is "top".
     12938 *
     12939 * @type pv.Layout.Cluster
     12940 */
     12941pv.Layout.Cluster.prototype.defaults = new pv.Layout.Cluster()
     12942    .extend(pv.Layout.Hierarchy.prototype.defaults)
     12943    .group(0)
     12944    .orient("top");
     12945
     12946/** @private */
     12947pv.Layout.Cluster.prototype.buildImplied = function(s) {
     12948  if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return;
     12949
     12950  var root = s.nodes[0],
     12951      group = s.group,
     12952      breadth,
     12953      depth,
     12954      leafCount = 0,
     12955      leafIndex = .5 - group / 2;
     12956
     12957  /* Count the leaf nodes and compute the depth of descendants. */
     12958  var p = undefined;
     12959  root.visitAfter(function(n) {
     12960      if (n.firstChild) {
     12961        n.depth = 1 + pv.max(n.childNodes, function(n) { return n.depth; });
     12962      } else {
     12963        if (group && (p != n.parentNode)) {
     12964          p = n.parentNode;
     12965          leafCount += group;
     12966        }
     12967        leafCount++;
     12968        n.depth = 0;
     12969      }
     12970    });
     12971  breadth = 1 / leafCount;
     12972  depth = 1 / root.depth;
     12973
     12974  /* Compute the unit breadth and depth of each node. */
     12975  var p = undefined;
     12976  root.visitAfter(function(n) {
     12977      if (n.firstChild) {
     12978        n.breadth = pv.mean(n.childNodes, function(n) { return n.breadth; });
     12979      } else {
     12980        if (group && (p != n.parentNode)) {
     12981          p = n.parentNode;
     12982          leafIndex += group;
     12983        }
     12984        n.breadth = breadth * leafIndex++;
     12985      }
     12986      n.depth = 1 - n.depth * depth;
     12987    });
     12988
     12989  /* Compute breadth and depth ranges for space-filling layouts. */
     12990  root.visitAfter(function(n) {
     12991      n.minBreadth = n.firstChild
     12992          ? n.firstChild.minBreadth
     12993          : (n.breadth - breadth / 2);
     12994      n.maxBreadth = n.firstChild
     12995          ? n.lastChild.maxBreadth
     12996          : (n.breadth + breadth / 2);
     12997    });
     12998  root.visitBefore(function(n) {
     12999      n.minDepth = n.parentNode
     13000          ? n.parentNode.maxDepth
     13001          : 0;
     13002      n.maxDepth = n.parentNode
     13003          ? (n.depth + root.depth)
     13004          : (n.minDepth + 2 * root.depth);
     13005    });
     13006  root.minDepth = -depth;
     13007
     13008  pv.Layout.Hierarchy.NodeLink.buildImplied.call(this, s);
     13009};
     13010
     13011/**
     13012 * Constructs a new, empty space-filling cluster layout. Layouts are not
     13013 * typically constructed directly; instead, they are added to an existing panel
     13014 * via {@link pv.Mark#add}.
     13015 *
     13016 * @class A variant of cluster layout that is space-filling. The meaning of the
     13017 * exported mark prototypes changes slightly in the space-filling
     13018 * implementation:<ul>
     13019 *
     13020 * <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Bar} for
     13021 * non-radial orientations, and a {@link pv.Wedge} for radial orientations.
     13022 *
     13023 * <p><li><tt>link</tt> - unsupported; undefined. Links are encoded implicitly
     13024 * in the arrangement of the space-filling nodes.
     13025 *
     13026 * <p><li><tt>label</tt> - for rendering node labels; typically a
     13027 * {@link pv.Label}.
     13028 *
     13029 * </ul>For more details on how to use this layout, see
     13030 * {@link pv.Layout.Cluster}.
     13031 *
     13032 * @extends pv.Layout.Cluster
     13033 */
     13034pv.Layout.Cluster.Fill = function() {
     13035  pv.Layout.Cluster.call(this);
     13036  pv.Layout.Hierarchy.Fill.constructor.call(this);
     13037};
     13038
     13039pv.Layout.Cluster.Fill.prototype = pv.extend(pv.Layout.Cluster);
     13040
     13041/** @private */
     13042pv.Layout.Cluster.Fill.prototype.buildImplied = function(s) {
     13043  if (pv.Layout.Cluster.prototype.buildImplied.call(this, s)) return;
     13044  pv.Layout.Hierarchy.Fill.buildImplied.call(this, s);
     13045};
     13046/**
     13047 * Constructs a new, empty partition layout. Layouts are not typically
     13048 * constructed directly; instead, they are added to an existing panel via
     13049 * {@link pv.Mark#add}.
     13050 *
     13051 * @class Implemeents a hierarchical layout using the partition (or sunburst,
     13052 * icicle) algorithm. This layout provides both node-link and space-filling
     13053 * implementations of partition diagrams. In many ways it is similar to
     13054 * {@link pv.Layout.Cluster}, except that leaf nodes are positioned based on
     13055 * their distance from the root.
     13056 *
     13057 * <p>The partition layout support dynamic sizing for leaf nodes, if a
     13058 * {@link #size} psuedo-property is specified. The default size function returns
     13059 * 1, causing all leaf nodes to be sized equally, and all internal nodes to be
     13060 * sized by the number of leaf nodes they have as descendants.
     13061 *
     13062 * <p>The size function can be used in conjunction with the order property,
     13063 * which allows the nodes to the sorted by the computed size. Note: for sorting
     13064 * based on other data attributes, simply use the default <tt>null</tt> for the
     13065 * order property, and sort the nodes beforehand using the {@link pv.Dom}
     13066 * operator.
     13067 *
     13068 * <p>For more details on how to use this layout, see
     13069 * {@link pv.Layout.Hierarchy}.
     13070 *
     13071 * @see pv.Layout.Partition.Fill
     13072 * @extends pv.Layout.Hierarchy
     13073 */
     13074pv.Layout.Partition = function() {
     13075  pv.Layout.Hierarchy.call(this);
     13076};
     13077
     13078pv.Layout.Partition.prototype = pv.extend(pv.Layout.Hierarchy)
     13079    .property("order", String) // null, ascending, descending?
     13080    .property("orient", String) // top, left, right, bottom, radial
     13081    .property("innerRadius", Number)
     13082    .property("outerRadius", Number);
     13083
     13084/**
     13085 * The sibling node order. The default order is <tt>null</tt>, which means to
     13086 * use the sibling order specified by the nodes property as-is. A value of
     13087 * "ascending" will sort siblings in ascending order of size, while "descending"
     13088 * will do the reverse. For sorting based on data attributes other than size,
     13089 * use the default <tt>null</tt> for the order property, and sort the nodes
     13090 * beforehand using the {@link pv.Dom} operator.
     13091 *
     13092 * @see pv.Dom.Node#sort
     13093 * @type string
     13094 * @name pv.Layout.Partition.prototype.order
     13095 */
     13096
     13097/**
     13098 * The orientation. The default orientation is "top", which means that the root
     13099 * node is placed on the top edge, leaf nodes appear at the bottom, and internal
     13100 * nodes are in-between. The following orientations are supported:<ul>
     13101 *
     13102 * <li>left - left-to-right.
     13103 * <li>right - right-to-left.
     13104 * <li>top - top-to-bottom.
     13105 * <li>bottom - bottom-to-top.
     13106 * <li>radial - radially, with the root at the center.</ul>
     13107 *
     13108 * @type string
     13109 * @name pv.Layout.Partition.prototype.orient
     13110 */
     13111
     13112/**
     13113 * The inner radius; defaults to 0. This property applies only to radial
     13114 * orientations, and can be used to compress the layout radially. Note that for
     13115 * the node-link implementation, the root node is always at the center,
     13116 * regardless of the value of this property; this property only affects internal
     13117 * and leaf nodes. For the space-filling implementation, a non-zero value of
     13118 * this property will result in the root node represented as a ring rather than
     13119 * a circle.
     13120 *
     13121 * @type number
     13122 * @name pv.Layout.Partition.prototype.innerRadius
     13123 */
     13124
     13125/**
     13126 * The outer radius; defaults to fill the containing panel, based on the height
     13127 * and width of the layout. If the layout has no height and width specified, it
     13128 * will extend to fill the enclosing panel.
     13129 *
     13130 * @type number
     13131 * @name pv.Layout.Partition.prototype.outerRadius
     13132 */
     13133
     13134/**
     13135 * Default properties for partition layouts. The default orientation is "top".
     13136 *
     13137 * @type pv.Layout.Partition
     13138 */
     13139pv.Layout.Partition.prototype.defaults = new pv.Layout.Partition()
     13140    .extend(pv.Layout.Hierarchy.prototype.defaults)
     13141    .orient("top");
     13142
     13143/** @private */
     13144pv.Layout.Partition.prototype.$size = function() { return 1; };
     13145
     13146/**
     13147 * Specifies the sizing function. By default, a sizing function is disabled and
     13148 * all nodes are given constant size. The sizing function is invoked for each
     13149 * leaf node in the tree (passed to the constructor).
     13150 *
     13151 * <p>For example, if the tree data structure represents a file system, with
     13152 * files as leaf nodes, and each file has a <tt>bytes</tt> attribute, you can
     13153 * specify a size function as:
     13154 *
     13155 * <pre>    .size(function(d) d.bytes)</pre>
     13156 *
     13157 * As with other properties, a size function may specify additional arguments to
     13158 * access the data associated with the layout and any enclosing panels.
     13159 *
     13160 * @param {function} f the new sizing function.
     13161 * @returns {pv.Layout.Partition} this.
     13162 */
     13163pv.Layout.Partition.prototype.size = function(f) {
     13164  this.$size = f;
     13165  return this;
     13166};
     13167
     13168/** @private */
     13169pv.Layout.Partition.prototype.buildImplied = function(s) {
     13170  if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return;
     13171
     13172  var that = this,
     13173      root = s.nodes[0],
     13174      stack = pv.Mark.stack,
     13175      maxDepth = 0;
     13176
     13177  /* Recursively compute the tree depth and node size. */
     13178  stack.unshift(null);
     13179  root.visitAfter(function(n, i) {
     13180      if (i > maxDepth) maxDepth = i;
     13181      n.size = n.firstChild
     13182          ? pv.sum(n.childNodes, function(n) { return n.size; })
     13183          : that.$size.apply(that, (stack[0] = n, stack));
     13184    });
     13185  stack.shift();
     13186
     13187  /* Order */
     13188  switch (s.order) {
     13189    case "ascending": root.sort(function(a, b) { return a.size - b.size; }); break;
     13190    case "descending": root.sort(function(b, a) { return a.size - b.size; }); break;
     13191  }
     13192
     13193  /* Compute the unit breadth and depth of each node. */
     13194  var ds = 1 / maxDepth;
     13195  root.minBreadth = 0;
     13196  root.breadth = .5;
     13197  root.maxBreadth = 1;
     13198  root.visitBefore(function(n) {
     13199    var b = n.minBreadth, s = n.maxBreadth - b;
     13200      for (var c = n.firstChild; c; c = c.nextSibling) {
     13201        c.minBreadth = b;
     13202        c.maxBreadth = b += (c.size / n.size) * s;
     13203        c.breadth = (b + c.minBreadth) / 2;
     13204      }
     13205    });
     13206  root.visitAfter(function(n, i) {
     13207      n.minDepth = (i - 1) * ds;
     13208      n.maxDepth = n.depth = i * ds;
     13209    });
     13210
     13211  pv.Layout.Hierarchy.NodeLink.buildImplied.call(this, s);
     13212};
     13213
     13214/**
     13215 * Constructs a new, empty space-filling partition layout. Layouts are not
     13216 * typically constructed directly; instead, they are added to an existing panel
     13217 * via {@link pv.Mark#add}.
     13218 *
     13219 * @class A variant of partition layout that is space-filling. The meaning of
     13220 * the exported mark prototypes changes slightly in the space-filling
     13221 * implementation:<ul>
     13222 *
     13223 * <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Bar} for
     13224 * non-radial orientations, and a {@link pv.Wedge} for radial orientations.
     13225 *
     13226 * <p><li><tt>link</tt> - unsupported; undefined. Links are encoded implicitly
     13227 * in the arrangement of the space-filling nodes.
     13228 *
     13229 * <p><li><tt>label</tt> - for rendering node labels; typically a
     13230 * {@link pv.Label}.
     13231 *
     13232 * </ul>For more details on how to use this layout, see
     13233 * {@link pv.Layout.Partition}.
     13234 *
     13235 * @extends pv.Layout.Partition
     13236 */
     13237pv.Layout.Partition.Fill = function() {
     13238  pv.Layout.Partition.call(this);
     13239  pv.Layout.Hierarchy.Fill.constructor.call(this);
     13240};
     13241
     13242pv.Layout.Partition.Fill.prototype = pv.extend(pv.Layout.Partition);
     13243
     13244/** @private */
     13245pv.Layout.Partition.Fill.prototype.buildImplied = function(s) {
     13246  if (pv.Layout.Partition.prototype.buildImplied.call(this, s)) return;
     13247  pv.Layout.Hierarchy.Fill.buildImplied.call(this, s);
     13248};
     13249/**
     13250 * Constructs a new, empty arc layout. Layouts are not typically constructed
     13251 * directly; instead, they are added to an existing panel via
     13252 * {@link pv.Mark#add}.
     13253 *
     13254 * @class Implements a layout for arc diagrams. An arc diagram is a network
     13255 * visualization with a one-dimensional layout of nodes, using circular arcs to
     13256 * render links between nodes. For undirected networks, arcs are rendering on a
     13257 * single side; this makes arc diagrams useful as annotations to other
     13258 * two-dimensional network layouts, such as rollup, matrix or table layouts. For
     13259 * directed networks, links in opposite directions can be rendered on opposite
     13260 * sides using <tt>directed(true)</tt>.
     13261 *
     13262 * <p>Arc layouts are particularly sensitive to node ordering; for best results,
     13263 * order the nodes such that related nodes are close to each other. A poor
     13264 * (e.g., random) order may result in large arcs with crossovers that impede
     13265 * visual processing. A future improvement to this layout may include automatic
     13266 * reordering using, e.g., spectral graph layout or simulated annealing.
     13267 *
     13268 * <p>This visualization technique is related to that developed by
     13269 * M. Wattenberg, <a
     13270 * href="http://www.research.ibm.com/visual/papers/arc-diagrams.pdf">"Arc
     13271 * Diagrams: Visualizing Structure in Strings"</a> in <i>IEEE InfoVis</i>, 2002.
     13272 * However, this implementation is limited to simple node-link networks, as
     13273 * opposed to structures with hierarchical self-similarity (such as strings).
     13274 *
     13275 * <p>As with other network layouts, three mark prototypes are provided:<ul>
     13276 *
     13277 * <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Dot}.
     13278 * <li><tt>link</tt> - for rendering links; typically a {@link pv.Line}.
     13279 * <li><tt>label</tt> - for rendering node labels; typically a {@link pv.Label}.
     13280 *
     13281 * </ul>For more details on how this layout is structured and can be customized,
     13282 * see {@link pv.Layout.Network}.
     13283 *
     13284 * @extends pv.Layout.Network
     13285 **/
     13286pv.Layout.Arc = function() {
     13287  pv.Layout.Network.call(this);
     13288  var interpolate, // cached interpolate
     13289      directed, // cached directed
     13290      reverse, // cached reverse
     13291      buildImplied = this.buildImplied;
     13292
     13293  /** @private Cache layout state to optimize properties. */
     13294  this.buildImplied = function(s) {
     13295    buildImplied.call(this, s);
     13296    directed = s.directed;
     13297    interpolate = s.orient == "radial" ? "linear" : "polar";
     13298    reverse = s.orient == "right" || s.orient == "top";
     13299  };
     13300
     13301  /* Override link properties to handle directedness and orientation. */
     13302  this.link
     13303      .data(function(p) {
     13304          var s = p.sourceNode, t = p.targetNode;
     13305          return reverse != (directed || (s.breadth < t.breadth)) ? [s, t] : [t, s];
     13306        })
     13307      .interpolate(function() { return interpolate; });
     13308};
     13309
     13310pv.Layout.Arc.prototype = pv.extend(pv.Layout.Network)
     13311    .property("orient", String)
     13312    .property("directed", Boolean);
     13313
     13314/**
     13315 * Default properties for arc layouts. By default, the orientation is "bottom".
     13316 *
     13317 * @type pv.Layout.Arc
     13318 */
     13319pv.Layout.Arc.prototype.defaults = new pv.Layout.Arc()
     13320    .extend(pv.Layout.Network.prototype.defaults)
     13321    .orient("bottom");
     13322
     13323/**
     13324 * Specifies an optional sort function. The sort function follows the same
     13325 * comparator contract required by {@link pv.Dom.Node#sort}. Specifying a sort
     13326 * function provides an alternative to sort the nodes as they are specified by
     13327 * the <tt>nodes</tt> property; the main advantage of doing this is that the
     13328 * comparator function can access implicit fields populated by the network
     13329 * layout, such as the <tt>linkDegree</tt>.
     13330 *
     13331 * <p>Note that arc diagrams are particularly sensitive to order. This is
     13332 * referred to as the seriation problem, and many different techniques exist to
     13333 * find good node orders that emphasize clusters, such as spectral layout and
     13334 * simulated annealing.
     13335 *
     13336 * @param {function} f comparator function for nodes.
     13337 * @returns {pv.Layout.Arc} this.
     13338 */
     13339pv.Layout.Arc.prototype.sort = function(f) {
     13340  this.$sort = f;
     13341  return this;
     13342};
     13343
     13344/** @private Populates the x, y and angle attributes on the nodes. */
     13345pv.Layout.Arc.prototype.buildImplied = function(s) {
     13346  if (pv.Layout.Network.prototype.buildImplied.call(this, s)) return;
     13347
     13348  var nodes = s.nodes,
     13349      orient = s.orient,
     13350      sort = this.$sort,
     13351      index = pv.range(nodes.length),
     13352      w = s.width,
     13353      h = s.height,
     13354      r = Math.min(w, h) / 2;
     13355
     13356  /* Sort the nodes. */
     13357  if (sort) index.sort(function(a, b) { return sort(nodes[a], nodes[b]); });
     13358
     13359  /** @private Returns the mid-angle, given the breadth. */
     13360  function midAngle(b) {
     13361    switch (orient) {
     13362      case "top": return -Math.PI / 2;
     13363      case "bottom": return Math.PI / 2;
     13364      case "left": return Math.PI;
     13365      case "right": return 0;
     13366      case "radial": return (b - .25) * 2 * Math.PI;
     13367    }
     13368  }
     13369
     13370  /** @private Returns the x-position, given the breadth. */
     13371  function x(b) {
     13372    switch (orient) {
     13373      case "top":
     13374      case "bottom": return b * w;
     13375      case "left": return 0;
     13376      case "right": return w;
     13377      case "radial": return w / 2 + r * Math.cos(midAngle(b));
     13378    }
     13379  }
     13380
     13381  /** @private Returns the y-position, given the breadth. */
     13382  function y(b) {
     13383    switch (orient) {
     13384      case "top": return 0;
     13385      case "bottom": return h;
     13386      case "left":
     13387      case "right": return b * h;
     13388      case "radial": return h / 2 + r * Math.sin(midAngle(b));
     13389    }
     13390  }
     13391
     13392  /* Populate the x, y and mid-angle attributes. */
     13393  for (var i = 0; i < nodes.length; i++) {
     13394    var n = nodes[index[i]], b = n.breadth = (i + .5) / nodes.length;
     13395    n.x = x(b);
     13396    n.y = y(b);
     13397    n.midAngle = midAngle(b);
     13398  }
     13399};
     13400
     13401/**
     13402 * The orientation. The default orientation is "left", which means that nodes
     13403 * will be positioned from left-to-right in the order they are specified in the
     13404 * <tt>nodes</tt> property. The following orientations are supported:<ul>
     13405 *
     13406 * <li>left - left-to-right.
     13407 * <li>right - right-to-left.
     13408 * <li>top - top-to-bottom.
     13409 * <li>bottom - bottom-to-top.
     13410 * <li>radial - radially, starting at 12 o'clock and proceeding clockwise.</ul>
     13411 *
     13412 * @type string
     13413 * @name pv.Layout.Arc.prototype.orient
     13414 */
     13415
     13416/**
     13417 * Whether this arc digram is directed (bidirectional); only applies to
     13418 * non-radial orientations. By default, arc digrams are undirected, such that
     13419 * all arcs appear on one side. If the arc digram is directed, then forward
     13420 * links are drawn on the conventional side (the same as as undirected
     13421 * links--right, left, bottom and top for left, right, top and bottom,
     13422 * respectively), while reverse links are drawn on the opposite side.
     13423 *
     13424 * @type boolean
     13425 * @name pv.Layout.Arc.prototype.directed
     13426 */
     13427/**
     13428 * Constructs a new, empty horizon layout. Layouts are not typically constructed
     13429 * directly; instead, they are added to an existing panel via
     13430 * {@link pv.Mark#add}.
     13431 *
     13432 * @class Implements a horizon layout, which is a variation of a single-series
     13433 * area chart where the area is folded into multiple bands. Color is used to
     13434 * encode band, allowing the size of the chart to be reduced significantly
     13435 * without impeding readability. This layout algorithm is based on the work of
     13436 * J. Heer, N. Kong and M. Agrawala in <a
     13437 * href="http://hci.stanford.edu/publications/2009/heer-horizon-chi09.pdf">"Sizing
     13438 * the Horizon: The Effects of Chart Size and Layering on the Graphical
     13439 * Perception of Time Series Visualizations"</a>, CHI 2009.
     13440 *
     13441 * <p>This layout exports a single <tt>band</tt> mark prototype, which is
     13442 * intended to be used with an area mark. The band mark is contained in a panel
     13443 * which is replicated per band (and for negative/positive bands). For example,
     13444 * to create a simple horizon graph given an array of numbers:
     13445 *
     13446 * <pre>vis.add(pv.Layout.Horizon)
     13447 *     .bands(n)
     13448 *   .band.add(pv.Area)
     13449 *     .data(data)
     13450 *     .left(function() this.index * 35)
     13451 *     .height(function(d) d * 40);</pre>
     13452 *
     13453 * The layout can be further customized by changing the number of bands, and
     13454 * toggling whether the negative bands are mirrored or offset. (See the
     13455 * above-referenced paper for guidance.)
     13456 *
     13457 * <p>The <tt>fillStyle</tt> of the area can be overridden, though typically it
     13458 * is easier to customize the layout's behavior through the custom
     13459 * <tt>backgroundStyle</tt>, <tt>positiveStyle</tt> and <tt>negativeStyle</tt>
     13460 * properties. By default, the background is white, positive bands are blue, and
     13461 * negative bands are red. For the most accurate presentation, use fully-opaque
     13462 * colors of equal intensity for the negative and positive bands.
     13463 *
     13464 * @extends pv.Layout
     13465 */
     13466pv.Layout.Horizon = function() {
     13467  pv.Layout.call(this);
     13468  var that = this,
     13469      bands, // cached bands
     13470      mode, // cached mode
     13471      size, // cached height
     13472      fill, // cached background style
     13473      red, // cached negative color (ramp)
     13474      blue, // cached positive color (ramp)
     13475      buildImplied = this.buildImplied;
     13476
     13477  /** @private Cache the layout state to optimize properties. */
     13478  this.buildImplied = function(s) {
     13479    buildImplied.call(this, s);
     13480    bands = s.bands;
     13481    mode = s.mode;
     13482    size = Math.round((mode == "color" ? .5 : 1) * s.height);
     13483    fill = s.backgroundStyle;
     13484    red = pv.ramp(fill, s.negativeStyle).domain(0, bands);
     13485    blue = pv.ramp(fill, s.positiveStyle).domain(0, bands);
     13486  };
     13487
     13488  var bands = new pv.Panel()
     13489      .data(function() { return pv.range(bands * 2); })
     13490      .overflow("hidden")
     13491      .height(function() { return size; })
     13492      .top(function(i) { return mode == "color" ? (i & 1) * size : 0; })
     13493      .fillStyle(function(i) { return i ? null : fill; });
     13494
     13495  /**
     13496   * The band prototype. This prototype is intended to be used with an Area
     13497   * mark to render the horizon bands.
     13498   *
     13499   * @type pv.Mark
     13500   * @name pv.Layout.Horizon.prototype.band
     13501   */
     13502  this.band = new pv.Mark()
     13503      .top(function(d, i) {
     13504          return mode == "mirror" && i & 1
     13505              ? (i + 1 >> 1) * size
     13506              : null;
     13507        })
     13508      .bottom(function(d, i) {
     13509          return mode == "mirror"
     13510              ? (i & 1 ? null : (i + 1 >> 1) * -size)
     13511              : ((i & 1 || -1) * (i + 1 >> 1) * size);
     13512        })
     13513      .fillStyle(function(d, i) {
     13514          return (i & 1 ? red : blue)((i >> 1) + 1);
     13515        });
     13516
     13517  this.band.add = function(type) {
     13518    return that.add(pv.Panel).extend(bands).add(type).extend(this);
     13519  };
     13520};
     13521
     13522pv.Layout.Horizon.prototype = pv.extend(pv.Layout)
     13523    .property("bands", Number)
     13524    .property("mode", String)
     13525    .property("backgroundStyle", pv.color)
     13526    .property("positiveStyle", pv.color)
     13527    .property("negativeStyle", pv.color);
     13528
     13529/**
     13530 * Default properties for horizon layouts. By default, there are two bands, the
     13531 * mode is "offset", the background style is "white", the positive style is
     13532 * blue, negative style is red.
     13533 *
     13534 * @type pv.Layout.Horizon
     13535 */
     13536pv.Layout.Horizon.prototype.defaults = new pv.Layout.Horizon()
     13537    .extend(pv.Layout.prototype.defaults)
     13538    .bands(2)
     13539    .mode("offset")
     13540    .backgroundStyle("white")
     13541    .positiveStyle("#1f77b4")
     13542    .negativeStyle("#d62728");
     13543
     13544/**
     13545 * The horizon mode: offset, mirror, or color. The default is "offset".
     13546 *
     13547 * @type string
     13548 * @name pv.Layout.Horizon.prototype.mode
     13549 */
     13550
     13551/**
     13552 * The number of bands. Must be at least one. The default value is two.
     13553 *
     13554 * @type number
     13555 * @name pv.Layout.Horizon.prototype.bands
     13556 */
     13557
     13558/**
     13559 * The positive band color; if non-null, the interior of positive bands are
     13560 * filled with the specified color. The default value of this property is blue.
     13561 * For accurate blending, this color should be fully opaque.
     13562 *
     13563 * @type pv.Color
     13564 * @name pv.Layout.Horizon.prototype.positiveStyle
     13565 */
     13566
     13567/**
     13568 * The negative band color; if non-null, the interior of negative bands are
     13569 * filled with the specified color. The default value of this property is red.
     13570 * For accurate blending, this color should be fully opaque.
     13571 *
     13572 * @type pv.Color
     13573 * @name pv.Layout.Horizon.prototype.negativeStyle
     13574 */
     13575
     13576/**
     13577 * The background color. The panel background is filled with the specified
     13578 * color, and the negative and positive bands are filled with an interpolated
     13579 * color between this color and the respective band color. The default value of
     13580 * this property is white. For accurate blending, this color should be fully
     13581 * opaque.
     13582 *
     13583 * @type pv.Color
     13584 * @name pv.Layout.Horizon.prototype.backgroundStyle
     13585 */
     13586/**
     13587 * Constructs a new, empty rollup network layout. Layouts are not typically
     13588 * constructed directly; instead, they are added to an existing panel via
     13589 * {@link pv.Mark#add}.
     13590 *
     13591 * @class Implements a network visualization using a node-link diagram where
     13592 * nodes are rolled up along two dimensions. This implementation is based on the
     13593 * "PivotGraph" designed by Martin Wattenberg:
     13594 *
     13595 * <blockquote>The method is designed for graphs that are "multivariate", i.e.,
     13596 * where each node is associated with several attributes. Unlike visualizations
     13597 * which emphasize global graph topology, PivotGraph uses a simple grid-based
     13598 * approach to focus on the relationship between node attributes &amp;
     13599 * connections.</blockquote>
     13600 *
     13601 * This layout requires two psuedo-properties to be specified, which assign node
     13602 * positions along the two dimensions {@link #x} and {@link #y}, corresponding
     13603 * to the left and top properties, respectively. Typically, these functions are
     13604 * specified using an {@link pv.Scale.ordinal}. Nodes that share the same
     13605 * position in <i>x</i> and <i>y</i> are "rolled up" into a meta-node, and
     13606 * similarly links are aggregated between meta-nodes. For example, to construct
     13607 * a rollup to analyze links by gender and affiliation, first define two ordinal
     13608 * scales:
     13609 *
     13610 * <pre>var x = pv.Scale.ordinal(nodes, function(d) d.gender).split(0, w),
     13611 *     y = pv.Scale.ordinal(nodes, function(d) d.aff).split(0, h);</pre>
     13612 *
     13613 * Next, define the position psuedo-properties:
     13614 *
     13615 * <pre>    .x(function(d) x(d.gender))
     13616 *     .y(function(d) y(d.aff))</pre>
     13617 *
     13618 * Linear and other quantitative scales can alternatively be used to position
     13619 * the nodes along either dimension. Note, however, that the rollup requires
     13620 * that the positions match exactly, and thus ordinal scales are recommended to
     13621 * avoid precision errors.
     13622 *
     13623 * <p>Note that because this layout provides a visualization of the rolled up
     13624 * graph, the data properties for the mark prototypes (<tt>node</tt>,
     13625 * <tt>link</tt> and <tt>label</tt>) are different from most other network
     13626 * layouts: they reference the rolled-up nodes and links, rather than the nodes
     13627 * and links of the full network. The underlying nodes and links for each
     13628 * rolled-up node and link can be accessed via the <tt>nodes</tt> and
     13629 * <tt>links</tt> attributes, respectively. The aggregated link values for
     13630 * rolled-up links can similarly be accessed via the <tt>linkValue</tt>
     13631 * attribute.
     13632 *
     13633 * <p>For undirected networks, links are duplicated in both directions. For
     13634 * directed networks, use <tt>directed(true)</tt>. The graph is assumed to be
     13635 * undirected by default.
     13636 *
     13637 * @extends pv.Layout.Network
     13638 * @see <a href="http://www.research.ibm.com/visual/papers/pivotgraph.pdf"
     13639 * >"Visual Exploration of Multivariate Graphs"</a> by M. Wattenberg, CHI 2006.
     13640 */
     13641pv.Layout.Rollup = function() {
     13642  pv.Layout.Network.call(this);
     13643  var that = this,
     13644      nodes, // cached rollup nodes
     13645      links, // cached rollup links
     13646      buildImplied = that.buildImplied;
     13647
     13648  /** @private Cache layout state to optimize properties. */
     13649  this.buildImplied = function(s) {
     13650    buildImplied.call(this, s);
     13651    nodes = s.$rollup.nodes;
     13652    links = s.$rollup.links;
     13653  };
     13654
     13655  /* Render rollup nodes. */
     13656  this.node
     13657      .data(function() { return nodes; })
     13658      .size(function(d) { return d.nodes.length * 20; });
     13659
     13660  /* Render rollup links. */
     13661  this.link
     13662      .interpolate("polar")
     13663      .eccentricity(.8);
     13664
     13665  this.link.add = function(type) {
     13666    return that.add(pv.Panel)
     13667        .data(function() { return links; })
     13668      .add(type)
     13669        .extend(this);
     13670  };
     13671};
     13672
     13673pv.Layout.Rollup.prototype = pv.extend(pv.Layout.Network)
     13674    .property("directed", Boolean);
     13675
     13676/**
     13677 * Whether the underlying network is directed. By default, the graph is assumed
     13678 * to be undirected, and links are rendered in both directions. If the network
     13679 * is directed, then forward links are drawn above the diagonal, while reverse
     13680 * links are drawn below.
     13681 *
     13682 * @type boolean
     13683 * @name pv.Layout.Rollup.prototype.directed
     13684 */
     13685
     13686/**
     13687 * Specifies the <i>x</i>-position function used to rollup nodes. The rolled up
     13688 * nodes are positioned horizontally using the return values from the given
     13689 * function. Typically the function is specified as an ordinal scale. For
     13690 * single-dimension rollups, a constant value can be specified.
     13691 *
     13692 * @param {function} f the <i>x</i>-position function.
     13693 * @returns {pv.Layout.Rollup} this.
     13694 * @see pv.Scale.ordinal
     13695 */
     13696pv.Layout.Rollup.prototype.x = function(f) {
     13697  this.$x = pv.functor(f);
     13698  return this;
     13699};
     13700
     13701/**
     13702 * Specifies the <i>y</i>-position function used to rollup nodes. The rolled up
     13703 * nodes are positioned vertically using the return values from the given
     13704 * function. Typically the function is specified as an ordinal scale. For
     13705 * single-dimension rollups, a constant value can be specified.
     13706 *
     13707 * @param {function} f the <i>y</i>-position function.
     13708 * @returns {pv.Layout.Rollup} this.
     13709 * @see pv.Scale.ordinal
     13710 */
     13711pv.Layout.Rollup.prototype.y = function(f) {
     13712  this.$y = pv.functor(f);
     13713  return this;
     13714};
     13715
     13716/** @private */
     13717pv.Layout.Rollup.prototype.buildImplied = function(s) {
     13718  if (pv.Layout.Network.prototype.buildImplied.call(this, s)) return;
     13719
     13720  var nodes = s.nodes,
     13721      links = s.links,
     13722      directed = s.directed,
     13723      n = nodes.length,
     13724      x = [],
     13725      y = [],
     13726      rnindex = 0,
     13727      rnodes = {},
     13728      rlinks = {};
     13729
     13730  /** @private */
     13731  function id(i) {
     13732    return x[i] + "," + y[i];
     13733  }
     13734
     13735  /* Iterate over the data, evaluating the x and y functions. */
     13736  var stack = pv.Mark.stack, o = {parent: this};
     13737  stack.unshift(null);
     13738  for (var i = 0; i < n; i++) {
     13739    o.index = i;
     13740    stack[0] = nodes[i];
     13741    x[i] = this.$x.apply(o, stack);
     13742    y[i] = this.$y.apply(o, stack);
     13743  }
     13744  stack.shift();
     13745
     13746  /* Compute rollup nodes. */
     13747  for (var i = 0; i < nodes.length; i++) {
     13748    var nodeId = id(i),
     13749        rn = rnodes[nodeId];
     13750    if (!rn) {
     13751      rn = rnodes[nodeId] = pv.extend(nodes[i]);
     13752      rn.index = rnindex++;
     13753      rn.x = x[i];
     13754      rn.y = y[i];
     13755      rn.nodes = [];
     13756    }
     13757    rn.nodes.push(nodes[i]);
     13758  }
     13759
     13760  /* Compute rollup links. */
     13761  for (var i = 0; i < links.length; i++) {
     13762    var source = links[i].sourceNode,
     13763        target = links[i].targetNode,
     13764        rsource = rnodes[id(source.index)],
     13765        rtarget = rnodes[id(target.index)],
     13766        reverse = !directed && rsource.index > rtarget.index,
     13767        linkId = reverse
     13768            ? rtarget.index + "," + rsource.index
     13769            : rsource.index + "," + rtarget.index,
     13770        rl = rlinks[linkId];
     13771    if (!rl) {
     13772      rl = rlinks[linkId] = {
     13773        sourceNode: rsource,
     13774        targetNode: rtarget,
     13775        linkValue: 0,
     13776        links: []
     13777      };
     13778    }
     13779    rl.links.push(links[i]);
     13780    rl.linkValue += links[i].linkValue;
     13781  }
     13782
     13783  /* Export the rolled up nodes and links to the scene. */
     13784  s.$rollup = {
     13785    nodes: pv.values(rnodes),
     13786    links: pv.values(rlinks)
     13787  };
     13788};
     13789/**
     13790 * Constructs a new, empty matrix network layout. Layouts are not typically
     13791 * constructed directly; instead, they are added to an existing panel via
     13792 * {@link pv.Mark#add}.
     13793 *
     13794 * @class Implements a network visualization using a matrix view. This is, in
     13795 * effect, a visualization of the graph's <i>adjacency matrix</i>: the cell at
     13796 * row <i>i</i>, column <i>j</i>, corresponds to the link from node <i>i</i> to
     13797 * node <i>j</i>. The fill color of each cell is binary by default, and
     13798 * corresponds to whether a link exists between the two nodes. If the underlying
     13799 * graph has links with variable values, the <tt>fillStyle</tt> property can be
     13800 * substited to use an appropriate color function, such as {@link pv.ramp}.
     13801 *
     13802 * <p>For undirected networks, the matrix is symmetric around the diagonal. For
     13803 * directed networks, links in opposite directions can be rendered on opposite
     13804 * sides of the diagonal using <tt>directed(true)</tt>. The graph is assumed to
     13805 * be undirected by default.
     13806 *
     13807 * <p>The mark prototypes for this network layout are slightly different than
     13808 * other implementations:<ul>
     13809 *
     13810 * <li><tt>node</tt> - unsupported; undefined. No mark is needed to visualize
     13811 * nodes directly, as the nodes are implicit in the location (rows and columns)
     13812 * of the links.
     13813 *
     13814 * <p><li><tt>link</tt> - for rendering links; typically a {@link pv.Bar}. The
     13815 * link mark is added directly to the layout, with the data property defined as
     13816 * all possible pairs of nodes. Each pair is represented as a
     13817 * {@link pv.Network.Layout.Link}, though the <tt>linkValue</tt> attribute may
     13818 * be 0 if no link exists in the graph.
     13819 *
     13820 * <p><li><tt>label</tt> - for rendering node labels; typically a
     13821 * {@link pv.Label}. The label mark is added directly to the layout, with the
     13822 * data property defined via the layout's <tt>nodes</tt> property; note,
     13823 * however, that the nodes are duplicated so as to provide a label across the
     13824 * top and down the side. Properties such as <tt>strokeStyle</tt> and
     13825 * <tt>fillStyle</tt> can be overridden to compute properties from node data
     13826 * dynamically.
     13827 *
     13828 * </ul>For more details on how to use this layout, see
     13829 * {@link pv.Layout.Network}.
     13830 *
     13831 * @extends pv.Layout.Network
     13832 */
     13833pv.Layout.Matrix = function() {
     13834  pv.Layout.Network.call(this);
     13835  var that = this,
     13836      n, // cached matrix size
     13837      dx, // cached cell width
     13838      dy, // cached cell height
     13839      labels, // cached labels (array of strings)
     13840      pairs, // cached pairs (array of links)
     13841      buildImplied = that.buildImplied;
     13842
     13843  /** @private Cache layout state to optimize properties. */
     13844  this.buildImplied = function(s) {
     13845    buildImplied.call(this, s);
     13846    n = s.nodes.length;
     13847    dx = s.width / n;
     13848    dy = s.height / n;
     13849    labels = s.$matrix.labels;
     13850    pairs = s.$matrix.pairs;
     13851  };
     13852
     13853  /* Links are all pairs of nodes. */
     13854  this.link
     13855      .data(function() { return pairs; })
     13856      .left(function() { return dx * (this.index % n); })
     13857      .top(function() { return dy * Math.floor(this.index / n); })
     13858      .width(function() { return dx; })
     13859      .height(function() { return dy; })
     13860      .lineWidth(1.5)
     13861      .strokeStyle("#fff")
     13862      .fillStyle(function(l) { return l.linkValue ? "#555" : "#eee"; })
     13863      .parent = this;
     13864
     13865  /* No special add for links! */
     13866  delete this.link.add;
     13867
     13868  /* Labels are duplicated for top & left. */
     13869  this.label
     13870      .data(function() { return labels; })
     13871      .left(function() { return this.index & 1 ? dx * ((this.index >> 1) + .5) : null; })
     13872      .top(function() { return this.index & 1 ? null : dy * ((this.index >> 1) + .5); })
     13873      .textMargin(4)
     13874      .textAlign(function() { return this.index & 1 ? "left" : "right"; })
     13875      .textAngle(function() { return this.index & 1 ? -Math.PI / 2 : 0; });
     13876
     13877  /* The node mark is unused. */
     13878  delete this.node;
     13879};
     13880
     13881pv.Layout.Matrix.prototype = pv.extend(pv.Layout.Network)
     13882    .property("directed", Boolean);
     13883
     13884/**
     13885 * Whether this matrix visualization is directed (bidirectional). By default,
     13886 * the graph is assumed to be undirected, such that the visualization is
     13887 * symmetric across the matrix diagonal. If the network is directed, then
     13888 * forward links are drawn above the diagonal, while reverse links are drawn
     13889 * below.
     13890 *
     13891 * @type boolean
     13892 * @name pv.Layout.Matrix.prototype.directed
     13893 */
     13894
     13895/**
     13896 * Specifies an optional sort function. The sort function follows the same
     13897 * comparator contract required by {@link pv.Dom.Node#sort}. Specifying a sort
     13898 * function provides an alternative to sort the nodes as they are specified by
     13899 * the <tt>nodes</tt> property; the main advantage of doing this is that the
     13900 * comparator function can access implicit fields populated by the network
     13901 * layout, such as the <tt>linkDegree</tt>.
     13902 *
     13903 * <p>Note that matrix visualizations are particularly sensitive to order. This
     13904 * is referred to as the seriation problem, and many different techniques exist
     13905 * to find good node orders that emphasize clusters, such as spectral layout and
     13906 * simulated annealing.
     13907 *
     13908 * @param {function} f comparator function for nodes.
     13909 * @returns {pv.Layout.Matrix} this.
     13910 */
     13911pv.Layout.Matrix.prototype.sort = function(f) {
     13912  this.$sort = f;
     13913  return this;
     13914};
     13915
     13916/** @private */
     13917pv.Layout.Matrix.prototype.buildImplied = function(s) {
     13918  if (pv.Layout.Network.prototype.buildImplied.call(this, s)) return;
     13919
     13920  var nodes = s.nodes,
     13921      links = s.links,
     13922      sort = this.$sort,
     13923      n = nodes.length,
     13924      index = pv.range(n),
     13925      labels = [],
     13926      pairs = [],
     13927      map = {};
     13928
     13929  s.$matrix = {labels: labels, pairs: pairs};
     13930
     13931  /* Sort the nodes. */
     13932  if (sort) index.sort(function(a, b) { return sort(nodes[a], nodes[b]); });
     13933
     13934  /* Create pairs. */
     13935  for (var i = 0; i < n; i++) {
     13936    for (var j = 0; j < n; j++) {
     13937      var a = index[i],
     13938          b = index[j],
     13939          p = {
     13940            row: i,
     13941            col: j,
     13942            sourceNode: nodes[a],
     13943            targetNode: nodes[b],
     13944            linkValue: 0
     13945          };
     13946      pairs.push(map[a + "." + b] = p);
     13947    }
     13948  }
     13949
     13950  /* Create labels. */
     13951  for (var i = 0; i < n; i++) {
     13952    var a = index[i];
     13953    labels.push(nodes[a], nodes[a]);
     13954  }
     13955
     13956  /* Accumulate link values. */
     13957  for (var i = 0; i < links.length; i++) {
     13958    var l = links[i],
     13959        source = l.sourceNode.index,
     13960        target = l.targetNode.index,
     13961        value = l.linkValue;
     13962    map[source + "." + target].linkValue += value;
     13963    if (!s.directed) map[target + "." + source].linkValue += value;
     13964  }
     13965};
     13966// ranges (bad, satisfactory, good)
     13967// measures (actual, forecast)
     13968// markers (previous, goal)
     13969
     13970/*
     13971 * Chart design based on the recommendations of Stephen Few. Implementation
     13972 * based on the work of Clint Ivy, Jamie Love, and Jason Davies.
     13973 * http://projects.instantcognition.com/protovis/bulletchart/
     13974 */
     13975
     13976/**
     13977 * Constructs a new, empty bullet layout. Layouts are not typically constructed
     13978 * directly; instead, they are added to an existing panel via
     13979 * {@link pv.Mark#add}.
     13980 *
     13981 * @class
     13982 * @extends pv.Layout
     13983 */
     13984pv.Layout.Bullet = function() {
     13985  pv.Layout.call(this);
     13986  var that = this,
     13987      buildImplied = that.buildImplied,
     13988      scale = that.x = pv.Scale.linear(),
     13989      orient,
     13990      horizontal,
     13991      rangeColor,
     13992      measureColor,
     13993      x;
     13994
     13995  /** @private Cache layout state to optimize properties. */
     13996  this.buildImplied = function(s) {
     13997    buildImplied.call(this, x = s);
     13998    orient = s.orient;
     13999    horizontal = /^left|right$/.test(orient);
     14000    rangeColor = pv.ramp("#bbb", "#eee")
     14001        .domain(0, Math.max(1, x.ranges.length - 1));
     14002    measureColor = pv.ramp("steelblue", "lightsteelblue")
     14003        .domain(0, Math.max(1, x.measures.length - 1));
     14004  };
     14005
     14006  /**
     14007   * The range prototype.
     14008   *
     14009   * @type pv.Mark
     14010   * @name pv.Layout.Bullet.prototype.range
     14011   */
     14012  (this.range = new pv.Mark())
     14013      .data(function() { return x.ranges; })
     14014      .reverse(true)
     14015      .left(function() { return orient == "left" ? 0 : null; })
     14016      .top(function() { return orient == "top" ? 0 : null; })
     14017      .right(function() { return orient == "right" ? 0 : null; })
     14018      .bottom(function() { return orient == "bottom" ? 0 : null; })
     14019      .width(function(d) { return horizontal ? scale(d) : null; })
     14020      .height(function(d) { return horizontal ? null : scale(d); })
     14021      .fillStyle(function() { return rangeColor(this.index); })
     14022      .antialias(false)
     14023      .parent = that;
     14024
     14025  /**
     14026   * The measure prototype.
     14027   *
     14028   * @type pv.Mark
     14029   * @name pv.Layout.Bullet.prototype.measure
     14030   */
     14031  (this.measure = new pv.Mark())
     14032      .extend(this.range)
     14033      .data(function() { return x.measures; })
     14034      .left(function() { return orient == "left" ? 0 : horizontal ? null : this.parent.width() / 3.25; })
     14035      .top(function() { return orient == "top" ? 0 : horizontal ? this.parent.height() / 3.25 : null; })
     14036      .right(function() { return orient == "right" ? 0 : horizontal ? null : this.parent.width() / 3.25; })
     14037      .bottom(function() { return orient == "bottom" ? 0 : horizontal ? this.parent.height() / 3.25 : null; })
     14038      .fillStyle(function() { return measureColor(this.index); })
     14039      .parent = that;
     14040
     14041  /**
     14042   * The marker prototype.
     14043   *
     14044   * @type pv.Mark
     14045   * @name pv.Layout.Bullet.prototype.marker
     14046   */
     14047  (this.marker = new pv.Mark())
     14048      .data(function() { return x.markers; })
     14049      .left(function(d) { return orient == "left" ? scale(d) : horizontal ? null : this.parent.width() / 2; })
     14050      .top(function(d) { return orient == "top" ? scale(d) : horizontal ? this.parent.height() / 2 : null; })
     14051      .right(function(d) { return orient == "right" ? scale(d) : null; })
     14052      .bottom(function(d) { return orient == "bottom" ? scale(d) : null; })
     14053      .strokeStyle("black")
     14054      .shape("bar")
     14055      .angle(function() { return horizontal ? 0 : Math.PI / 2; })
     14056      .parent = that;
     14057
     14058  (this.tick = new pv.Mark())
     14059      .data(function() { return scale.ticks(7); })
     14060      .left(function(d) { return orient == "left" ? scale(d) : null; })
     14061      .top(function(d) { return orient == "top" ? scale(d) : null; })
     14062      .right(function(d) { return orient == "right" ? scale(d) : horizontal ? null : -6; })
     14063      .bottom(function(d) { return orient == "bottom" ? scale(d) : horizontal ? -8 : null; })
     14064      .height(function() { return horizontal ? 6 : null; })
     14065      .width(function() { return horizontal ? null : 6; })
     14066      .parent = that;
     14067};
     14068
     14069pv.Layout.Bullet.prototype = pv.extend(pv.Layout)
     14070    .property("orient", String) // left, right, top, bottom
     14071    .property("ranges")
     14072    .property("markers")
     14073    .property("measures")
     14074    .property("maximum", Number);
     14075
     14076/**
     14077 * Default properties for bullet layouts.
     14078 *
     14079 * @type pv.Layout.Bullet
     14080 */
     14081pv.Layout.Bullet.prototype.defaults = new pv.Layout.Bullet()
     14082    .extend(pv.Layout.prototype.defaults)
     14083    .orient("left")
     14084    .ranges([])
     14085    .markers([])
     14086    .measures([]);
     14087
     14088/**
     14089 * The orientation.
     14090 *
     14091 * @type string
     14092 * @name pv.Layout.Bullet.prototype.orient
     14093 */
     14094
     14095/**
     14096 * The array of range values.
     14097 *
     14098 * @type array
     14099 * @name pv.Layout.Bullet.prototype.ranges
     14100 */
     14101
     14102/**
     14103 * The array of marker values.
     14104 *
     14105 * @type array
     14106 * @name pv.Layout.Bullet.prototype.markers
     14107 */
     14108
     14109/**
     14110 * The array of measure values.
     14111 *
     14112 * @type array
     14113 * @name pv.Layout.Bullet.prototype.measures
     14114 */
     14115
     14116/**
     14117 * Optional; the maximum range value.
     14118 *
     14119 * @type number
     14120 * @name pv.Layout.Bullet.prototype.maximum
     14121 */
     14122
     14123/** @private */
     14124pv.Layout.Bullet.prototype.buildImplied = function(s) {
     14125  pv.Layout.prototype.buildImplied.call(this, s);
     14126  var size = this.parent[/^left|right$/.test(s.orient) ? "width" : "height"]();
     14127  s.maximum = s.maximum || pv.max([].concat(s.ranges, s.markers, s.measures));
     14128  this.x.domain(0, s.maximum).range(0, size);
     14129};
     14130/**
     14131 * Abstract; see an implementing class for details.
     14132 *
     14133 * @class Represents a reusable interaction; applies an interactive behavior to
     14134 * a given mark. Behaviors are themselves functions designed to be used as event
     14135 * handlers. For example, to add pan and zoom support to any panel, say:
     14136 *
     14137 * <pre>    .event("mousedown", pv.Behavior.pan())
     14138 *     .event("mousewheel", pv.Behavior.zoom())</pre>
     14139 *
     14140 * The behavior should be registered on the event that triggers the start of the
     14141 * behavior. Typically, the behavior will take care of registering for any
     14142 * additional events that are necessary. For example, dragging starts on
     14143 * mousedown, while the drag behavior automatically listens for mousemove and
     14144 * mouseup events on the window. By listening to the window, the behavior can
     14145 * continue to receive mouse events even if the mouse briefly leaves the mark
     14146 * being dragged, or even the root panel.
     14147 *
     14148 * <p>Each behavior implementation has specific requirements as to which events
     14149 * it supports, and how it should be used. For example, the drag behavior
     14150 * requires that the data associated with the mark be an object with <tt>x</tt>
     14151 * and <tt>y</tt> attributes, such as a {@link pv.Vector}, storing the mark's
     14152 * position. See an implementing class for details.
     14153 *
     14154 * @see pv.Behavior.drag
     14155 * @see pv.Behavior.pan
     14156 * @see pv.Behavior.point
     14157 * @see pv.Behavior.select
     14158 * @see pv.Behavior.zoom
     14159 * @extends function
     14160 */
     14161pv.Behavior = {};
     14162/**
     14163 * Returns a new drag behavior to be registered on mousedown events.
     14164 *
     14165 * @class Implements interactive dragging starting with mousedown events.
     14166 * Register this behavior on marks that should be draggable by the user, such as
     14167 * the selected region for brushing and linking. This behavior can be used in
     14168 * tandom with {@link pv.Behavior.select} to allow the selected region to be
     14169 * dragged interactively.
     14170 *
     14171 * <p>After the initial mousedown event is triggered, this behavior listens for
     14172 * mousemove and mouseup events on the window. This allows dragging to continue
     14173 * even if the mouse temporarily leaves the mark that is being dragged, or even
     14174 * the root panel.
     14175 *
     14176 * <p>This behavior requires that the data associated with the mark being
     14177 * dragged have <tt>x</tt> and <tt>y</tt> attributes that correspond to the
     14178 * mark's location in pixels. The mark's positional properties are not set
     14179 * directly by this behavior; instead, the positional properties should be
     14180 * defined as:
     14181 *
     14182 * <pre>    .left(function(d) d.x)
     14183 *     .top(function(d) d.y)</pre>
     14184 *
     14185 * Thus, the behavior does not move the mark directly, but instead updates the
     14186 * mark position by updating the underlying data. Note that if the positional
     14187 * properties are defined with bottom and right (rather than top and left), the
     14188 * drag behavior will be inverted, which will confuse users!
     14189 *
     14190 * <p>The drag behavior is bounded by the parent panel; the <tt>x</tt> and
     14191 * <tt>y</tt> attributes are clamped such that the mark being dragged does not
     14192 * extend outside the enclosing panel's bounds. To facilitate this, the drag
     14193 * behavior also queries for <tt>dx</tt> and <tt>dy</tt> attributes on the
     14194 * underlying data, to determine the dimensions of the bar being dragged. For
     14195 * non-rectangular marks, the drag behavior simply treats the mark as a point,
     14196 * which means that only the mark's center is bounded.
     14197 *
     14198 * <p>The mark being dragged is automatically re-rendered for each mouse event
     14199 * as part of the drag operation. In addition, a <tt>fix</tt> attribute is
     14200 * populated on the mark, which allows visual feedback for dragging. For
     14201 * example, to change the mark fill color while dragging:
     14202 *
     14203 * <pre>    .fillStyle(function(d) d.fix ? "#ff7f0e" : "#aec7e8")</pre>
     14204 *
     14205 * In some cases, such as with network layouts, dragging the mark may cause
     14206 * related marks to change, in which case additional marks may also need to be
     14207 * rendered. This can be accomplished by listening for the drag
     14208 * psuedo-events:<ul>
     14209 *
     14210 * <li>dragstart (on mousedown)
     14211 * <li>drag (on mousemove)
     14212 * <li>dragend (on mouseup)
     14213 *
     14214 * </ul>For example, to render the parent panel while dragging, thus
     14215 * re-rendering all sibling marks:
     14216 *
     14217 * <pre>    .event("mousedown", pv.Behavior.drag())
     14218 *     .event("drag", function() this.parent)</pre>
     14219 *
     14220 * This behavior may be enhanced in the future to allow more flexible
     14221 * configuration of drag behavior.
     14222 *
     14223 * @extends pv.Behavior
     14224 * @see pv.Behavior
     14225 * @see pv.Behavior.select
     14226 * @see pv.Layout.force
     14227 */
     14228pv.Behavior.drag = function() {
     14229  var scene, // scene context
     14230      index, // scene context
     14231      p, // particle being dragged
     14232      v1, // initial mouse-particle offset
     14233      max;
     14234
     14235  /** @private */
     14236  function mousedown(d) {
     14237    index = this.index;
     14238    scene = this.scene;
     14239    var m = this.mouse();
     14240    v1 = ((p = d).fix = pv.vector(d.x, d.y)).minus(m);
     14241    max = {
     14242      x: this.parent.width() - (d.dx || 0),
     14243      y: this.parent.height() - (d.dy || 0)
     14244    };
     14245    scene.mark.context(scene, index, function() { this.render(); });
     14246    pv.Mark.dispatch("dragstart", scene, index);
     14247  }
     14248
     14249  /** @private */
     14250  function mousemove() {
     14251    if (!scene) return;
     14252    scene.mark.context(scene, index, function() {
     14253        var m = this.mouse();
     14254        p.x = p.fix.x = Math.max(0, Math.min(v1.x + m.x, max.x));
     14255        p.y = p.fix.y = Math.max(0, Math.min(v1.y + m.y, max.y));
     14256        this.render();
     14257      });
     14258    pv.Mark.dispatch("drag", scene, index);
     14259  }
     14260
     14261  /** @private */
     14262  function mouseup() {
     14263    if (!scene) return;
     14264    p.fix = null;
     14265    scene.mark.context(scene, index, function() { this.render(); });
     14266    pv.Mark.dispatch("dragend", scene, index);
     14267    scene = null;
     14268  }
     14269
     14270  pv.listen(window, "mousemove", mousemove);
     14271  pv.listen(window, "mouseup", mouseup);
     14272  return mousedown;
     14273};
     14274/**
     14275 * Returns a new point behavior to be registered on mousemove events.
     14276 *
     14277 * @class Implements interactive fuzzy pointing, identifying marks that are in
     14278 * close proximity to the mouse cursor. This behavior is an alternative to the
     14279 * native mouseover and mouseout events, improving usability. Rather than
     14280 * requiring the user to mouseover a mark exactly, the mouse simply needs to
     14281 * move near the given mark and a "point" event is triggered. In addition, if
     14282 * multiple marks overlap, the point behavior can be used to identify the mark
     14283 * instance closest to the cursor, as opposed to the one that is rendered on
     14284 * top.
     14285 *
     14286 * <p>The point behavior can also identify the closest mark instance for marks
     14287 * that produce a continuous graphic primitive. The point behavior can thus be
     14288 * used to provide details-on-demand for both discrete marks (such as dots and
     14289 * bars), as well as continuous marks (such as lines and areas).
     14290 *
     14291 * <p>This behavior is implemented by finding the closest mark instance to the
     14292 * mouse cursor on every mousemove event. If this closest mark is within the
     14293 * given radius threshold, which defaults to 30 pixels, a "point" psuedo-event
     14294 * is dispatched to the given mark instance. If any mark were previously
     14295 * pointed, it would receive a corresponding "unpoint" event. These two
     14296 * psuedo-event types correspond to the native "mouseover" and "mouseout"
     14297 * events, respectively. To increase the radius at which the point behavior can
     14298 * be applied, specify an appropriate threshold to the constructor, up to
     14299 * <tt>Infinity</tt>.
     14300 *
     14301 * <p>By default, the standard Cartesian distance is computed. However, with
     14302 * some visualizations it is desirable to consider only a single dimension, such
     14303 * as the <i>x</i>-dimension for an independent variable. In this case, the
     14304 * collapse parameter can be set to collapse the <i>y</i> dimension:
     14305 *
     14306 * <pre>    .event("mousemove", pv.Behavior.point(Infinity).collapse("y"))</pre>
     14307 *
     14308 * <p>This behavior only listens to mousemove events on the assigned panel,
     14309 * which is typically the root panel. The behavior will search recursively for
     14310 * descendant marks to point. If the mouse leaves the assigned panel, the
     14311 * behavior no longer receives mousemove events; an unpoint psuedo-event is
     14312 * automatically dispatched to unpoint any pointed mark. Marks may be re-pointed
     14313 * when the mouse reenters the panel.
     14314 *
     14315 * <p>Panels have transparent fill styles by default; this means that panels may
     14316 * not receive the initial mousemove event to start pointing. To fix this
     14317 * problem, either given the panel a visible fill style (such as "white"), or
     14318 * set the <tt>events</tt> property to "all" such that the panel receives events
     14319 * despite its transparent fill.
     14320 *
     14321 * <p>Note: this behavior does not currently wedge marks.
     14322 *
     14323 * @extends pv.Behavior
     14324 *
     14325 * @param {number} [r] the fuzzy radius threshold in pixels
     14326 * @see <a href="http://www.tovigrossman.com/papers/chi2005bubblecursor.pdf"
     14327 * >"The Bubble Cursor: Enhancing Target Acquisition by Dynamic Resizing of the
     14328 * Cursor's Activation Area"</a> by T. Grossman &amp; R. Balakrishnan, CHI 2005.
     14329 */
     14330pv.Behavior.point = function(r) {
     14331  var unpoint, // the current pointer target
     14332      collapse = null, // dimensions to collapse
     14333      kx = 1, // x-dimension cost scale
     14334      ky = 1, // y-dimension cost scale
     14335      r2 = arguments.length ? r * r : 900; // fuzzy radius
     14336
     14337  /** @private Search for the mark closest to the mouse. */
     14338  function search(scene, index) {
     14339    var s = scene[index],
     14340        point = {cost: Infinity};
     14341    for (var i = 0, n = s.visible && s.children.length; i < n; i++) {
     14342      var child = s.children[i], mark = child.mark, p;
     14343      if (mark.type == "panel") {
     14344        mark.scene = child;
     14345        for (var j = 0, m = child.length; j < m; j++) {
     14346          mark.index = j;
     14347          p = search(child, j);
     14348          if (p.cost < point.cost) point = p;
     14349        }
     14350        delete mark.scene;
     14351        delete mark.index;
     14352      } else if (mark.$handlers.point) {
     14353        var v = mark.mouse();
     14354        for (var j = 0, m = child.length; j < m; j++) {
     14355          var c = child[j],
     14356              dx = v.x - c.left - (c.width || 0) / 2,
     14357              dy = v.y - c.top - (c.height || 0) / 2,
     14358              dd = kx * dx * dx + ky * dy * dy;
     14359          if (dd < point.cost) {
     14360            point.distance = dx * dx + dy * dy;
     14361            point.cost = dd;
     14362            point.scene = child;
     14363            point.index = j;
     14364          }
     14365        }
     14366      }
     14367    }
     14368    return point;
     14369  }
     14370
     14371  /** @private */
     14372  function mousemove() {
     14373    /* If the closest mark is far away, clear the current target. */
     14374    var point = search(this.scene, this.index);
     14375    if ((point.cost == Infinity) || (point.distance > r2)) point = null;
     14376
     14377    /* Unpoint the old target, if it's not the new target. */
     14378    if (unpoint) {
     14379      if (point
     14380          && (unpoint.scene == point.scene)
     14381          && (unpoint.index == point.index)) return;
     14382      pv.Mark.dispatch("unpoint", unpoint.scene, unpoint.index);
     14383    }
     14384
     14385    /* Point the new target, if there is one. */
     14386    if (unpoint = point) {
     14387      pv.Mark.dispatch("point", point.scene, point.index);
     14388
     14389      /* Unpoint when the mouse leaves the root panel. */
     14390      pv.listen(this.root.canvas(), "mouseout", mouseout);
     14391    }
     14392  }
     14393
     14394  /** @private */
     14395  function mouseout(e) {
     14396    if (unpoint && !pv.ancestor(this, e.relatedTarget)) {
     14397      pv.Mark.dispatch("unpoint", unpoint.scene, unpoint.index);
     14398      unpoint = null;
     14399    }
     14400  }
     14401
     14402  /**
     14403   * Sets or gets the collapse parameter. By default, the standard Cartesian
     14404   * distance is computed. However, with some visualizations it is desirable to
     14405   * consider only a single dimension, such as the <i>x</i>-dimension for an
     14406   * independent variable. In this case, the collapse parameter can be set to
     14407   * collapse the <i>y</i> dimension:
     14408   *
     14409   * <pre>    .event("mousemove", pv.Behavior.point(Infinity).collapse("y"))</pre>
     14410   *
     14411   * @function
     14412   * @returns {pv.Behavior.point} this, or the current collapse parameter.
     14413   * @name pv.Behavior.point.prototype.collapse
     14414   * @param {string} [x] the new collapse parameter
     14415   */
     14416  mousemove.collapse = function(x) {
     14417    if (arguments.length) {
     14418      collapse = String(x);
     14419      switch (collapse) {
     14420        case "y": kx = 1; ky = 0; break;
     14421        case "x": kx = 0; ky = 1; break;
     14422        default: kx = 1; ky = 1; break;
     14423      }
     14424      return mousemove;
     14425    }
     14426    return collapse;
     14427  };
     14428
     14429  return mousemove;
     14430};
     14431/**
     14432 * Returns a new select behavior to be registered on mousedown events.
     14433 *
     14434 * @class Implements interactive selecting starting with mousedown events.
     14435 * Register this behavior on panels that should be selectable by the user, such
     14436 * for brushing and linking. This behavior can be used in tandom with
     14437 * {@link pv.Behavior.drag} to allow the selected region to be dragged
     14438 * interactively.
     14439 *
     14440 * <p>After the initial mousedown event is triggered, this behavior listens for
     14441 * mousemove and mouseup events on the window. This allows selecting to continue
     14442 * even if the mouse temporarily leaves the assigned panel, or even the root
     14443 * panel.
     14444 *
     14445 * <p>This behavior requires that the data associated with the mark being
     14446 * dragged have <tt>x</tt>, <tt>y</tt>, <tt>dx</tt> and <tt>dy</tt> attributes
     14447 * that correspond to the mark's location and dimensions in pixels. The mark's
     14448 * positional properties are not set directly by this behavior; instead, the
     14449 * positional properties should be defined as:
     14450 *
     14451 * <pre>    .left(function(d) d.x)
     14452 *     .top(function(d) d.y)
     14453 *     .width(function(d) d.dx)
     14454 *     .height(function(d) d.dy)</pre>
     14455 *
     14456 * Thus, the behavior does not resize the mark directly, but instead updates the
     14457 * selection by updating the assigned panel's underlying data. Note that if the
     14458 * positional properties are defined with bottom and right (rather than top and
     14459 * left), the drag behavior will be inverted, which will confuse users!
     14460 *
     14461 * <p>The select behavior is bounded by the assigned panel; the positional
     14462 * attributes are clamped such that the selection does not extend outside the
     14463 * panel's bounds.
     14464 *
     14465 * <p>The panel being selected is automatically re-rendered for each mouse event
     14466 * as part of the drag operation. This behavior may be enhanced in the future to
     14467 * allow more flexible configuration of select behavior. In some cases, such as
     14468 * with parallel coordinates, making a selection may cause related marks to
     14469 * change, in which case additional marks may also need to be rendered. This can
     14470 * be accomplished by listening for the select psuedo-events:<ul>
     14471 *
     14472 * <li>selectstart (on mousedown)
     14473 * <li>select (on mousemove)
     14474 * <li>selectend (on mouseup)
     14475 *
     14476 * </ul>For example, to render the parent panel while selecting, thus
     14477 * re-rendering all sibling marks:
     14478 *
     14479 * <pre>    .event("mousedown", pv.Behavior.drag())
     14480 *     .event("select", function() this.parent)</pre>
     14481 *
     14482 * This behavior may be enhanced in the future to allow more flexible
     14483 * configuration of the selection behavior.
     14484 *
     14485 * @extends pv.Behavior
     14486 * @see pv.Behavior.drag
     14487 */
     14488pv.Behavior.select = function() {
     14489  var scene, // scene context
     14490      index, // scene context
     14491      r, // region being selected
     14492      m1; // initial mouse position
     14493
     14494  /** @private */
     14495  function mousedown(d) {
     14496    index = this.index;
     14497    scene = this.scene;
     14498    m1 = this.mouse();
     14499    r = d;
     14500    r.x = m1.x;
     14501    r.y = m1.y;
     14502    r.dx = r.dy = 0;
     14503    pv.Mark.dispatch("selectstart", scene, index);
     14504  }
     14505
     14506  /** @private */
     14507  function mousemove() {
     14508    if (!scene) return;
     14509    scene.mark.context(scene, index, function() {
     14510        var m2 = this.mouse();
     14511        r.x = Math.max(0, Math.min(m1.x, m2.x));
     14512        r.y = Math.max(0, Math.min(m1.y, m2.y));
     14513        r.dx = Math.min(this.width(), Math.max(m2.x, m1.x)) - r.x;
     14514        r.dy = Math.min(this.height(), Math.max(m2.y, m1.y)) - r.y;
     14515        this.render();
     14516      });
     14517    pv.Mark.dispatch("select", scene, index);
     14518  }
     14519
     14520  /** @private */
     14521  function mouseup() {
     14522    if (!scene) return;
     14523    pv.Mark.dispatch("selectend", scene, index);
     14524    scene = null;
     14525  }
     14526
     14527  pv.listen(window, "mousemove", mousemove);
     14528  pv.listen(window, "mouseup", mouseup);
     14529  return mousedown;
     14530};
     14531/**
     14532 * Returns a new resize behavior to be registered on mousedown events.
     14533 *
     14534 * @class Implements interactive resizing of a selection starting with mousedown
     14535 * events. Register this behavior on selection handles that should be resizeable
     14536 * by the user, such for brushing and linking. This behavior can be used in
     14537 * tandom with {@link pv.Behavior.select} and {@link pv.Behavior.drag} to allow
     14538 * the selected region to be selected and dragged interactively.
     14539 *
     14540 * <p>After the initial mousedown event is triggered, this behavior listens for
     14541 * mousemove and mouseup events on the window. This allows resizing to continue
     14542 * even if the mouse temporarily leaves the assigned panel, or even the root
     14543 * panel.
     14544 *
     14545 * <p>This behavior requires that the data associated with the mark being
     14546 * resized have <tt>x</tt>, <tt>y</tt>, <tt>dx</tt> and <tt>dy</tt> attributes
     14547 * that correspond to the mark's location and dimensions in pixels. The mark's
     14548 * positional properties are not set directly by this behavior; instead, the
     14549 * positional properties should be defined as:
     14550 *
     14551 * <pre>    .left(function(d) d.x)
     14552 *     .top(function(d) d.y)
     14553 *     .width(function(d) d.dx)
     14554 *     .height(function(d) d.dy)</pre>
     14555 *
     14556 * Thus, the behavior does not resize the mark directly, but instead updates the
     14557 * size by updating the assigned panel's underlying data. Note that if the
     14558 * positional properties are defined with bottom and right (rather than top and
     14559 * left), the resize behavior will be inverted, which will confuse users!
     14560 *
     14561 * <p>The resize behavior is bounded by the assigned mark's enclosing panel; the
     14562 * positional attributes are clamped such that the selection does not extend
     14563 * outside the panel's bounds.
     14564 *
     14565 * <p>The mark being resized is automatically re-rendered for each mouse event
     14566 * as part of the resize operation. This behavior may be enhanced in the future
     14567 * to allow more flexible configuration. In some cases, such as with parallel
     14568 * coordinates, resizing the selection may cause related marks to change, in
     14569 * which case additional marks may also need to be rendered. This can be
     14570 * accomplished by listening for the select psuedo-events:<ul>
     14571 *
     14572 * <li>resizestart (on mousedown)
     14573 * <li>resize (on mousemove)
     14574 * <li>resizeend (on mouseup)
     14575 *
     14576 * </ul>For example, to render the parent panel while resizing, thus
     14577 * re-rendering all sibling marks:
     14578 *
     14579 * <pre>    .event("mousedown", pv.Behavior.resize("left"))
     14580 *     .event("resize", function() this.parent)</pre>
     14581 *
     14582 * This behavior may be enhanced in the future to allow more flexible
     14583 * configuration of the selection behavior.
     14584 *
     14585 * @extends pv.Behavior
     14586 * @see pv.Behavior.select
     14587 * @see pv.Behavior.drag
     14588 */
     14589pv.Behavior.resize = function(side) {
     14590  var scene, // scene context
     14591      index, // scene context
     14592      r, // region being selected
     14593      m1; // initial mouse position
     14594
     14595  /** @private */
     14596  function mousedown(d) {
     14597    index = this.index;
     14598    scene = this.scene;
     14599    m1 = this.mouse();
     14600    r = d;
     14601    switch (side) {
     14602      case "left": m1.x = r.x + r.dx; break;
     14603      case "right": m1.x = r.x; break;
     14604      case "top": m1.y = r.y + r.dy; break;
     14605      case "bottom": m1.y = r.y; break;
     14606    }
     14607    pv.Mark.dispatch("resizestart", scene, index);
     14608  }
     14609
     14610  /** @private */
     14611  function mousemove() {
     14612    if (!scene) return;
     14613    scene.mark.context(scene, index, function() {
     14614        var m2 = this.mouse();
     14615        r.x = Math.max(0, Math.min(m1.x, m2.x));
     14616        r.y = Math.max(0, Math.min(m1.y, m2.y));
     14617        r.dx = Math.min(this.parent.width(), Math.max(m2.x, m1.x)) - r.x;
     14618        r.dy = Math.min(this.parent.height(), Math.max(m2.y, m1.y)) - r.y;
     14619        this.render();
     14620      });
     14621    pv.Mark.dispatch("resize", scene, index);
     14622  }
     14623
     14624  /** @private */
     14625  function mouseup() {
     14626    if (!scene) return;
     14627    pv.Mark.dispatch("resizeend", scene, index);
     14628    scene = null;
     14629  }
     14630
     14631  pv.listen(window, "mousemove", mousemove);
     14632  pv.listen(window, "mouseup", mouseup);
     14633  return mousedown;
     14634};
     14635/**
     14636 * Returns a new pan behavior to be registered on mousedown events.
     14637 *
     14638 * @class Implements interactive panning starting with mousedown events.
     14639 * Register this behavior on panels to allow panning. This behavior can be used
     14640 * in tandem with {@link pv.Behavior.zoom} to allow both panning and zooming:
     14641 *
     14642 * <pre>    .event("mousedown", pv.Behavior.pan())
     14643 *     .event("mousewheel", pv.Behavior.zoom())</pre>
     14644 *
     14645 * The pan behavior currently supports only mouse events; support for keyboard
     14646 * shortcuts to improve accessibility may be added in the future.
     14647 *
     14648 * <p>After the initial mousedown event is triggered, this behavior listens for
     14649 * mousemove and mouseup events on the window. This allows panning to continue
     14650 * even if the mouse temporarily leaves the panel that is being panned, or even
     14651 * the root panel.
     14652 *
     14653 * <p>The implementation of this behavior relies on the panel's
     14654 * <tt>transform</tt> property, which specifies a matrix transformation that is
     14655 * applied to child marks. Note that the transform property only affects the
     14656 * panel's children, but not the panel itself; therefore the panel's fill and
     14657 * stroke will not change when the contents are panned.
     14658 *
     14659 * <p>Panels have transparent fill styles by default; this means that panels may
     14660 * not receive the initial mousedown event to start panning. To fix this
     14661 * problem, either given the panel a visible fill style (such as "white"), or
     14662 * set the <tt>events</tt> property to "all" such that the panel receives events
     14663 * despite its transparent fill.
     14664 *
     14665 * <p>The pan behavior has optional support for bounding. If enabled, the user
     14666 * will not be able to pan the panel outside of the initial bounds. This feature
     14667 * is designed to work in conjunction with the zoom behavior; otherwise,
     14668 * bounding the panel effectively disables all panning.
     14669 *
     14670 * @extends pv.Behavior
     14671 * @see pv.Behavior.zoom
     14672 * @see pv.Panel#transform
     14673 */
     14674pv.Behavior.pan = function() {
     14675  var scene, // scene context
     14676      index, // scene context
     14677      m1, // transformation matrix at the start of panning
     14678      v1, // mouse location at the start of panning
     14679      k, // inverse scale
     14680      bound; // whether to bound to the panel
     14681
     14682  /** @private */
     14683  function mousedown() {
     14684    index = this.index;
     14685    scene = this.scene;
     14686    v1 = pv.vector(pv.event.pageX, pv.event.pageY);
     14687    m1 = this.transform();
     14688    k = 1 / (m1.k * this.scale);
     14689    if (bound) {
     14690      bound = {
     14691        x: (1 - m1.k) * this.width(),
     14692        y: (1 - m1.k) * this.height()
     14693      };
     14694    }
     14695  }
     14696
     14697  /** @private */
     14698  function mousemove() {
     14699    if (!scene) return;
     14700    scene.mark.context(scene, index, function() {
     14701        var x = (pv.event.pageX - v1.x) * k,
     14702            y = (pv.event.pageY - v1.y) * k,
     14703            m = m1.translate(x, y);
     14704        if (bound) {
     14705          m.x = Math.max(bound.x, Math.min(0, m.x));
     14706          m.y = Math.max(bound.y, Math.min(0, m.y));
     14707        }
     14708        this.transform(m).render();
     14709      });
     14710    pv.Mark.dispatch("pan", scene, index);
     14711  }
     14712
     14713  /** @private */
     14714  function mouseup() {
     14715    scene = null;
     14716  }
     14717
     14718  /**
     14719   * Sets or gets the bound parameter. If bounding is enabled, the user will not
     14720   * be able to pan outside the initial panel bounds; this typically applies
     14721   * only when the pan behavior is used in tandem with the zoom behavior.
     14722   * Bounding is not enabled by default.
     14723   *
     14724   * <p>Note: enabling bounding after panning has already occurred will not
     14725   * immediately reset the transform. Bounding should be enabled before the
     14726   * panning behavior is applied.
     14727   *
     14728   * @function
     14729   * @returns {pv.Behavior.pan} this, or the current bound parameter.
     14730   * @name pv.Behavior.pan.prototype.bound
     14731   * @param {boolean} [x] the new bound parameter.
     14732   */
     14733  mousedown.bound = function(x) {
     14734    if (arguments.length) {
     14735      bound = Boolean(x);
     14736      return this;
     14737    }
     14738    return Boolean(bound);
     14739  };
     14740
     14741  pv.listen(window, "mousemove", mousemove);
     14742  pv.listen(window, "mouseup", mouseup);
     14743  return mousedown;
     14744};
     14745/**
     14746 * Returns a new zoom behavior to be registered on mousewheel events.
     14747 *
     14748 * @class Implements interactive zooming using mousewheel events. Register this
     14749 * behavior on panels to allow zooming. This behavior can be used in tandem with
     14750 * {@link pv.Behavior.pan} to allow both panning and zooming:
     14751 *
     14752 * <pre>    .event("mousedown", pv.Behavior.pan())
     14753 *     .event("mousewheel", pv.Behavior.zoom())</pre>
     14754 *
     14755 * The zoom behavior currently supports only mousewheel events; support for
     14756 * keyboard shortcuts and gesture events to improve accessibility may be added
     14757 * in the future.
     14758 *
     14759 * <p>The implementation of this behavior relies on the panel's
     14760 * <tt>transform</tt> property, which specifies a matrix transformation that is
     14761 * applied to child marks. Note that the transform property only affects the
     14762 * panel's children, but not the panel itself; therefore the panel's fill and
     14763 * stroke will not change when the contents are zoomed. The built-in support for
     14764 * transforms only supports uniform scaling and translates, which is sufficient
     14765 * for panning and zooming.  Note that this is not a strict geometric
     14766 * transformation, as the <tt>lineWidth</tt> property is scale-aware: strokes
     14767 * are drawn at constant size independent of scale.
     14768 *
     14769 * <p>Panels have transparent fill styles by default; this means that panels may
     14770 * not receive mousewheel events to zoom. To fix this problem, either given the
     14771 * panel a visible fill style (such as "white"), or set the <tt>events</tt>
     14772 * property to "all" such that the panel receives events despite its transparent
     14773 * fill.
     14774 *
     14775 * <p>The zoom behavior has optional support for bounding. If enabled, the user
     14776 * will not be able to zoom out farther than the initial bounds. This feature is
     14777 * designed to work in conjunction with the pan behavior.
     14778 *
     14779 * @extends pv.Behavior
     14780 * @see pv.Panel#transform
     14781 * @see pv.Mark#scale
     14782 * @param {number} speed
     14783 */
     14784pv.Behavior.zoom = function(speed) {
     14785  var bound; // whether to bound to the panel
     14786
     14787  if (!arguments.length) speed = 1 / 48;
     14788
     14789  /** @private */
     14790  function mousewheel() {
     14791    var v = this.mouse(),
     14792        k = pv.event.wheel * speed,
     14793        m = this.transform().translate(v.x, v.y)
     14794            .scale((k < 0) ? (1e3 / (1e3 - k)) : ((1e3 + k) / 1e3))
     14795            .translate(-v.x, -v.y);
     14796    if (bound) {
     14797      m.k = Math.max(1, m.k);
     14798      m.x = Math.max((1 - m.k) * this.width(), Math.min(0, m.x));
     14799      m.y = Math.max((1 - m.k) * this.height(), Math.min(0, m.y));
     14800    }
     14801    this.transform(m).render();
     14802    pv.Mark.dispatch("zoom", this.scene, this.index);
     14803  }
     14804
     14805  /**
     14806   * Sets or gets the bound parameter. If bounding is enabled, the user will not
     14807   * be able to zoom out farther than the initial panel bounds. Bounding is not
     14808   * enabled by default. If this behavior is used in tandem with the pan
     14809   * behavior, both should use the same bound parameter.
     14810   *
     14811   * <p>Note: enabling bounding after zooming has already occurred will not
     14812   * immediately reset the transform. Bounding should be enabled before the zoom
     14813   * behavior is applied.
     14814   *
     14815   * @function
     14816   * @returns {pv.Behavior.zoom} this, or the current bound parameter.
     14817   * @name pv.Behavior.zoom.prototype.bound
     14818   * @param {boolean} [x] the new bound parameter.
     14819   */
     14820  mousewheel.bound = function(x) {
     14821    if (arguments.length) {
     14822      bound = Boolean(x);
     14823      return this;
     14824    }
     14825    return Boolean(bound);
     14826  };
     14827
     14828  return mousewheel;
     14829};
     14830/**
     14831 * @ignore
     14832 * @namespace
     14833 */
     14834pv.Geo = function() {};
     14835/**
     14836 * Abstract; not implemented. There is no explicit constructor; this class
     14837 * merely serves to document the representation used by {@link pv.Geo.scale}.
     14838 *
     14839 * @class Represents a pair of geographic coordinates.
     14840 *
     14841 * @name pv.Geo.LatLng
     14842 * @see pv.Geo.scale
     14843 */
     14844
     14845/**
     14846 * The <i>latitude</i> coordinate in degrees; positive is North.
     14847 *
     14848 * @type number
     14849 * @name pv.Geo.LatLng.prototype.lat
     14850 */
     14851
     14852/**
     14853 * The <i>longitude</i> coordinate in degrees; positive is East.
     14854 *
     14855 * @type number
     14856 * @name pv.Geo.LatLng.prototype.lng
     14857 */
     14858/**
     14859 * Abstract; not implemented. There is no explicit constructor; this class
     14860 * merely serves to document the representation used by {@link pv.Geo.scale}.
     14861 *
     14862 * @class Represents a geographic projection. This class provides the core
     14863 * implementation for {@link pv.Geo.scale}s, mapping between geographic
     14864 * coordinates (latitude and longitude) and normalized screen space in the range
     14865 * [-1,1]. The remaining mapping between normalized screen space and actual
     14866 * pixels is performed by <tt>pv.Geo.scale</tt>.
     14867 *
     14868 * <p>Many geographic projections have a point around which the projection is
     14869 * centered. Rather than have each implementation add support for a
     14870 * user-specified center point, the <tt>pv.Geo.scale</tt> translates the
     14871 * geographic coordinates relative to the center point for both the forward and
     14872 * inverse projection.
     14873 *
     14874 * <p>In general, this class should not be used directly, unless the desire is
     14875 * to implement a new geographic projection. Instead, use <tt>pv.Geo.scale</tt>.
     14876 * Implementations are not required to implement inverse projections, but are
     14877 * needed for some forms of interactivity. Also note that some inverse
     14878 * projections are ambiguous, such as the connecting points in Dymaxian maps.
     14879 *
     14880 * @name pv.Geo.Projection
     14881 * @see pv.Geo.scale
     14882 */
     14883
     14884/**
     14885 * The <i>forward</i> projection.
     14886 *
     14887 * @function
     14888 * @name pv.Geo.Projection.prototype.project
     14889 * @param {pv.Geo.LatLng} latlng the latitude and longitude to project.
     14890 * @returns {pv.Vector} the xy-coordinates of the given point.
     14891 */
     14892
     14893/**
     14894 * The <i>inverse</i> projection; optional.
     14895 *
     14896 * @function
     14897 * @name pv.Geo.Projection.prototype.invert
     14898 * @param {pv.Vector} xy the x- and y-coordinates to invert.
     14899 * @returns {pv.Geo.LatLng} the latitude and longitude of the given point.
     14900 */
     14901/**
     14902 * The built-in projections.
     14903 *
     14904 * @see pv.Geo.Projection
     14905 * @namespace
     14906 */
     14907pv.Geo.projections = {
     14908
     14909  /** @see http://en.wikipedia.org/wiki/Mercator_projection */
     14910  mercator: {
     14911    project: function(latlng) {
     14912      return {
     14913          x: latlng.lng / 180,
     14914          y: latlng.lat > 85 ? 1 : latlng.lat < -85 ? -1
     14915              : Math.log(Math.tan(Math.PI / 4
     14916              + pv.radians(latlng.lat) / 2)) / Math.PI
     14917        };
     14918    },
     14919    invert: function(xy) {
     14920      return {
     14921          lng: xy.x * 180,
     14922          lat: pv.degrees(2 * Math.atan(Math.exp(xy.y * Math.PI)) - Math.PI / 2)
     14923        };
     14924    }
     14925  },
     14926
     14927  /** @see http://en.wikipedia.org/wiki/Gall-Peters_projection */
     14928  "gall-peters": {
     14929    project: function(latlng) {
     14930      return {
     14931          x: latlng.lng / 180,
     14932          y: Math.sin(pv.radians(latlng.lat))
     14933        };
     14934    },
     14935    invert: function(xy) {
     14936      return {
     14937          lng: xy.x * 180,
     14938          lat: pv.degrees(Math.asin(xy.y))
     14939        };
     14940    }
     14941  },
     14942
     14943  /** @see http://en.wikipedia.org/wiki/Sinusoidal_projection */
     14944  sinusoidal: {
     14945    project: function(latlng) {
     14946      return {
     14947          x: pv.radians(latlng.lng) * Math.cos(pv.radians(latlng.lat)) / Math.PI,
     14948          y: latlng.lat / 90
     14949        };
     14950    },
     14951    invert: function(xy) {
     14952      return {
     14953          lng: pv.degrees((xy.x * Math.PI) / Math.cos(xy.y * Math.PI / 2)),
     14954          lat: xy.y * 90
     14955        };
     14956    }
     14957  },
     14958
     14959  /** @see http://en.wikipedia.org/wiki/Aitoff_projection */
     14960  aitoff: {
     14961    project: function(latlng) {
     14962      var l = pv.radians(latlng.lng),
     14963          f = pv.radians(latlng.lat),
     14964          a = Math.acos(Math.cos(f) * Math.cos(l / 2));
     14965      return {
     14966          x: 2 * (a ? (Math.cos(f) * Math.sin(l / 2) * a / Math.sin(a)) : 0) / Math.PI,
     14967          y: 2 * (a ? (Math.sin(f) * a / Math.sin(a)) : 0) / Math.PI
     14968        };
     14969    },
     14970    invert: function(xy) {
     14971      var x = xy.x * Math.PI / 2,
     14972          y = xy.y * Math.PI / 2;
     14973      return {
     14974          lng: pv.degrees(x / Math.cos(y)),
     14975          lat: pv.degrees(y)
     14976        };
     14977    }
     14978  },
     14979
     14980  /** @see http://en.wikipedia.org/wiki/Hammer_projection */
     14981  hammer: {
     14982    project: function(latlng) {
     14983      var l = pv.radians(latlng.lng),
     14984          f = pv.radians(latlng.lat),
     14985          c = Math.sqrt(1 + Math.cos(f) * Math.cos(l / 2));
     14986      return {
     14987          x: 2 * Math.SQRT2 * Math.cos(f) * Math.sin(l / 2) / c / 3,
     14988          y: Math.SQRT2 * Math.sin(f) / c / 1.5
     14989        };
     14990    },
     14991    invert: function(xy) {
     14992      var x = xy.x * 3,
     14993          y = xy.y * 1.5,
     14994          z = Math.sqrt(1 - x * x / 16 - y * y / 4);
     14995      return {
     14996          lng: pv.degrees(2 * Math.atan2(z * x, 2 * (2 * z * z - 1))),
     14997          lat: pv.degrees(Math.asin(z * y))
     14998        };
     14999    }
     15000  },
     15001
     15002  /** The identity or "none" projection. */
     15003  identity: {
     15004    project: function(latlng) {
     15005      return {
     15006          x: latlng.lng / 180,
     15007          y: latlng.lat / 90
     15008        };
     15009    },
     15010    invert: function(xy) {
     15011      return {
     15012          lng: xy.x * 180,
     15013          lat: xy.y * 90
     15014        };
     15015    }
     15016  }
     15017};
     15018/**
     15019 * Returns a geographic scale. The arguments to this constructor are optional,
     15020 * and equivalent to calling {@link #projection}.
     15021 *
     15022 * @class Represents a geographic scale; a mapping between latitude-longitude
     15023 * coordinates and screen pixel coordinates. By default, the domain is inferred
     15024 * from the geographic coordinates, so that the domain fills the output range.
     15025 *
     15026 * <p>Note that geographic scales are two-dimensional transformations, rather
     15027 * than the one-dimensional bidrectional mapping typical of other scales.
     15028 * Rather than mapping (for example) between a numeric domain and a numeric
     15029 * range, geographic scales map between two coordinate objects: {@link
     15030 * pv.Geo.LatLng} and {@link pv.Vector}.
     15031 *
     15032 * @param {pv.Geo.Projection} [p] optional projection.
     15033 * @see pv.Geo.scale#ticks
     15034 */
     15035pv.Geo.scale = function(p) {
     15036  var rmin = {x: 0, y: 0}, // default range minimum
     15037      rmax = {x: 1, y: 1}, // default range maximum
     15038      d = [], // default domain
     15039      j = pv.Geo.projections.identity, // domain <-> normalized range
     15040      x = pv.Scale.linear(-1, 1).range(0, 1), // normalized <-> range
     15041      y = pv.Scale.linear(-1, 1).range(1, 0), // normalized <-> range
     15042      c = {lng: 0, lat: 0}, // Center Point
     15043      lastLatLng, // cached latlng
     15044      lastPoint; // cached point
     15045
     15046  /** @private */
     15047  function scale(latlng) {
     15048    if (!lastLatLng
     15049        || (latlng.lng != lastLatLng.lng)
     15050        || (latlng.lat != lastLatLng.lat)) {
     15051      lastLatLng = latlng;
     15052      var p = project(latlng);
     15053      lastPoint = {x: x(p.x), y: y(p.y)};
     15054    }
     15055    return lastPoint;
     15056  }
     15057
     15058  /** @private */
     15059  function project(latlng) {
     15060    var offset = {lng: latlng.lng - c.lng, lat: latlng.lat};
     15061    return j.project(offset);
     15062  }
     15063
     15064  /** @private */
     15065  function invert(xy) {
     15066    var latlng = j.invert(xy);
     15067    latlng.lng += c.lng;
     15068    return latlng;
     15069  }
     15070
     15071  /** Returns the projected x-coordinate. */
     15072  scale.x = function(latlng) {
     15073    return scale(latlng).x;
     15074  };
     15075
     15076  /** Returns the projected y-coordinate. */
     15077  scale.y = function(latlng) {
     15078    return scale(latlng).y;
     15079  };
     15080
     15081  /**
     15082   * Abstract; this is a local namespace on a given geographic scale.
     15083   *
     15084   * @namespace Tick functions for geographic scales. Because geographic scales
     15085   * represent two-dimensional transformations (as opposed to one-dimensional
     15086   * transformations typical of other scales), the tick values are similarly
     15087   * represented as two-dimensional coordinates in the input domain, i.e.,
     15088   * {@link pv.Geo.LatLng} objects.
     15089   *
     15090   * <p>Also, note that non-rectilinear projections, such as sinsuoidal and
     15091   * aitoff, may not produce straight lines for constant longitude or constant
     15092   * latitude. Therefore the returned array of ticks is a two-dimensional array,
     15093   * sampling various latitudes as constant longitude, and vice versa.
     15094   *
     15095   * <p>The tick lines can therefore be approximated as polylines, either with
     15096   * "linear" or "cardinal" interpolation. This is not as accurate as drawing
     15097   * the true curve through the projection space, but is usually sufficient.
     15098   *
     15099   * @name pv.Geo.scale.prototype.ticks
     15100   * @see pv.Geo.scale
     15101   * @see pv.Geo.LatLng
     15102   * @see pv.Line#interpolate
     15103   */
     15104  scale.ticks = {
     15105
     15106    /**
     15107     * Returns longitude ticks.
     15108     *
     15109     * @function
     15110     * @param {number} [m] the desired number of ticks.
     15111     * @returns {array} a nested array of <tt>pv.Geo.LatLng</tt> ticks.
     15112     * @name pv.Geo.scale.prototype.ticks.prototype.lng
     15113     */
     15114    lng: function(m) {
     15115      var lat, lng;
     15116      if (d.length > 1) {
     15117        var s = pv.Scale.linear();
     15118        if (m == undefined) m = 10;
     15119        lat = s.domain(d, function(d) { return d.lat; }).ticks(m);
     15120        lng = s.domain(d, function(d) { return d.lng; }).ticks(m);
     15121      } else {
     15122        lat = pv.range(-80, 81, 10);
     15123        lng = pv.range(-180, 181, 10);
     15124      }
     15125      return lng.map(function(lng) {
     15126        return lat.map(function(lat) {
     15127          return {lat: lat, lng: lng};
     15128        });
     15129      });
     15130    },
     15131
     15132    /**
     15133     * Returns latitude ticks.
     15134     *
     15135     * @function
     15136     * @param {number} [m] the desired number of ticks.
     15137     * @returns {array} a nested array of <tt>pv.Geo.LatLng</tt> ticks.
     15138     * @name pv.Geo.scale.prototype.ticks.prototype.lat
     15139     */
     15140    lat: function(m) {
     15141      return pv.transpose(scale.ticks.lng(m));
     15142    }
     15143  };
     15144
     15145  /**
     15146   * Inverts the specified value in the output range, returning the
     15147   * corresponding value in the input domain. This is frequently used to convert
     15148   * the mouse location (see {@link pv.Mark#mouse}) to a value in the input
     15149   * domain. Inversion is only supported for numeric ranges, and not colors.
     15150   *
     15151   * <p>Note that this method does not do any rounding or bounds checking. If
     15152   * the input domain is discrete (e.g., an array index), the returned value
     15153   * should be rounded. If the specified <tt>y</tt> value is outside the range,
     15154   * the returned value may be equivalently outside the input domain.
     15155   *
     15156   * @function
     15157   * @name pv.Geo.scale.prototype.invert
     15158   * @param {number} y a value in the output range (a pixel location).
     15159   * @returns {number} a value in the input domain.
     15160   */
     15161  scale.invert = function(p) {
     15162    return invert({x: x.invert(p.x), y: y.invert(p.y)});
     15163  };
     15164
     15165  /**
     15166   * Sets or gets the input domain. Note that unlike quantitative scales, the
     15167   * domain cannot be reduced to a simple rectangle (i.e., minimum and maximum
     15168   * values for latitude and longitude). Instead, the domain values must be
     15169   * projected to normalized space, effectively finding the domain in normalized
     15170   * space rather than in terms of latitude and longitude. Thus, changing the
     15171   * projection requires recomputing the normalized domain.
     15172   *
     15173   * <p>This method can be invoked several ways:
     15174   *
     15175   * <p>1. <tt>domain(values...)</tt>
     15176   *
     15177   * <p>Specifying the domain as a series of {@link pv.Geo.LatLng}s is the most
     15178   * explicit and recommended approach. However, if the domain values are
     15179   * derived from data, you may find the second method more appropriate.
     15180   *
     15181   * <p>2. <tt>domain(array, f)</tt>
     15182   *
     15183   * <p>Rather than enumerating the domain explicitly, you can specify a single
     15184   * argument of an array. In addition, you can specify an optional accessor
     15185   * function to extract the domain values (as {@link pv.Geo.LatLng}s) from the
     15186   * array. If the specified array has fewer than two elements, this scale will
     15187   * default to the full normalized domain.
     15188   *
     15189   * <p>2. <tt>domain()</tt>
     15190   *
     15191   * <p>Invoking the <tt>domain</tt> method with no arguments returns the
     15192   * current domain as an array.
     15193   *
     15194   * @function
     15195   * @name pv.Geo.scale.prototype.domain
     15196   * @param {...} domain... domain values.
     15197   * @returns {pv.Geo.scale} <tt>this</tt>, or the current domain.
     15198   */
     15199  scale.domain = function(array, f) {
     15200    if (arguments.length) {
     15201      d = (array instanceof Array)
     15202          ? ((arguments.length > 1) ? pv.map(array, f) : array)
     15203          : Array.prototype.slice.call(arguments);
     15204      if (d.length > 1) {
     15205        var lngs = d.map(function(c) { return c.lng; });
     15206        var lats = d.map(function(c) { return c.lat; });
     15207        c = {
     15208          lng: (pv.max(lngs) + pv.min(lngs)) / 2,
     15209          lat: (pv.max(lats) + pv.min(lats)) / 2
     15210        };
     15211        var n = d.map(project); // normalized domain
     15212        x.domain(n, function(p) { return p.x; });
     15213        y.domain(n, function(p) { return p.y; });
     15214      } else {
     15215        c = {lng: 0, lat: 0};
     15216        x.domain(-1, 1);
     15217        y.domain(-1, 1);
     15218      }
     15219      lastLatLng = null; // invalidate the cache
     15220      return this;
     15221    }
     15222    return d;
     15223  };
     15224
     15225  /**
     15226   * Sets or gets the output range. This method can be invoked several ways:
     15227   *
     15228   * <p>1. <tt>range(min, max)</tt>
     15229   *
     15230   * <p>If two objects are specified, the arguments should be {@link pv.Vector}s
     15231   * which specify the minimum and maximum values of the x- and y-coordinates
     15232   * explicitly.
     15233   *
     15234   * <p>2. <tt>range(width, height)</tt>
     15235   *
     15236   * <p>If two numbers are specified, the arguments specify the maximum values
     15237   * of the x- and y-coordinates explicitly; the minimum values are implicitly
     15238   * zero.
     15239   *
     15240   * <p>3. <tt>range()</tt>
     15241   *
     15242   * <p>Invoking the <tt>range</tt> method with no arguments returns the current
     15243   * range as an array of two {@link pv.Vector}s: the minimum (top-left) and
     15244   * maximum (bottom-right) values.
     15245   *
     15246   * @function
     15247   * @name pv.Geo.scale.prototype.range
     15248   * @param {...} range... range values.
     15249   * @returns {pv.Geo.scale} <tt>this</tt>, or the current range.
     15250   */
     15251  scale.range = function(min, max) {
     15252    if (arguments.length) {
     15253      if (typeof min == "object") {
     15254        rmin = {x: Number(min.x), y: Number(min.y)};
     15255        rmax = {x: Number(max.x), y: Number(max.y)};
     15256      } else {
     15257        rmin = {x: 0, y: 0};
     15258        rmax = {x: Number(min), y: Number(max)};
     15259      }
     15260      x.range(rmin.x, rmax.x);
     15261      y.range(rmax.y, rmin.y); // XXX flipped?
     15262      lastLatLng = null; // invalidate the cache
     15263      return this;
     15264    }
     15265    return [rmin, rmax];
     15266  };
     15267
     15268  /**
     15269   * Sets or gets the projection. This method can be invoked several ways:
     15270   *
     15271   * <p>1. <tt>projection(string)</tt>
     15272   *
     15273   * <p>Specifying a string sets the projection to the given named projection in
     15274   * {@link pv.Geo.projections}. If no such projection is found, the identity
     15275   * projection is used.
     15276   *
     15277   * <p>2. <tt>projection(object)</tt>
     15278   *
     15279   * <p>Specifying an object sets the projection to the given custom projection,
     15280   * which must implement the <i>forward</i> and <i>inverse</i> methods per the
     15281   * {@link pv.Geo.Projection} interface.
     15282   *
     15283   * <p>3. <tt>projection()</tt>
     15284   *
     15285   * <p>Invoking the <tt>projection</tt> method with no arguments returns the
     15286   * current object that defined the projection.
     15287   *
     15288   * @function
     15289   * @name pv.Scale.geo.prototype.projection
     15290   * @param {...} range... range values.
     15291   * @returns {pv.Scale.geo} <tt>this</tt>, or the current range.
     15292   */
     15293  scale.projection = function(p) {
     15294    if (arguments.length) {
     15295      j = typeof p == "string"
     15296          ? pv.Geo.projections[p] || pv.Geo.projections.identity
     15297          : p;
     15298      return this.domain(d); // recompute normalized domain
     15299    }
     15300    return p;
     15301  };
     15302
     15303  /**
     15304   * Returns a view of this scale by the specified accessor function <tt>f</tt>.
     15305   * Given a scale <tt>g</tt>, <tt>g.by(function(d) d.foo)</tt> is equivalent to
     15306   * <tt>function(d) g(d.foo)</tt>. This method should be used judiciously; it
     15307   * is typically more clear to invoke the scale directly, passing in the value
     15308   * to be scaled.
     15309   *
     15310   * @function
     15311   * @name pv.Geo.scale.prototype.by
     15312   * @param {function} f an accessor function.
     15313   * @returns {pv.Geo.scale} a view of this scale by the specified accessor
     15314   * function.
     15315   */
     15316  scale.by = function(f) {
     15317    function by() { return scale(f.apply(this, arguments)); }
     15318    for (var method in scale) by[method] = scale[method];
     15319    return by;
     15320  };
     15321
     15322  if (arguments.length) scale.projection(p);
     15323  return scale;
     15324};
  • 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..14a83e4 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"))
     172        self.putChild("protovis-d3.2.js", f("protovis-d3.2.js"))
    167173
    168174    def child_helper_status(self, ctx):
    169175        # 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..6c785e1 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):
     371    def OFFrenderHTTP(self, ctx):
    357372        req = inevow.IRequest(ctx)
    358373        t = get_arg(req, "t")
    359374        if t == "json":
    360375            return self.json(req)
    361376        return rend.Page.renderHTTP(self, ctx)
    362377
    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
     378    def _find_overlap(self, events, start_key, end_key):
     379        # given a list of event dicts, return a new list in which each event
     380        # has an extra "row" key (an int, starting at 0). This is a hint to
     381        # our JS frontend about how to overlap the parts of the graph it is
     382        # drawing.
     383
     384        # we must always make a copy, since we're going to be adding "row"
     385        # keys and don't want to change the original objects. If we're
     386        # stringifying serverids, we'll also be changing the serverid keys.
     387        new_events = []
     388        rows = []
     389        for ev in events:
     390            ev = ev.copy()
     391            if "serverid" in ev:
     392                ev["serverid"] = base32.b2a(ev["serverid"])
     393            # find an empty slot in the rows
     394            free_slot = None
     395            for row,finished in enumerate(rows):
     396                if finished is not None:
     397                    if ev[start_key] > finished:
     398                        free_slot = row
     399                        break
     400            if free_slot is None:
     401                free_slot = len(rows)
     402                rows.append(ev[end_key])
     403            else:
     404                rows[free_slot] = ev[end_key]
     405            ev["row"] = free_slot
     406            new_events.append(ev)
     407        return new_events
     408
     409    def _find_overlap_requests(self, events):
     410        """We compute a three-element 'row tuple' for each event: (serverid,
     411        shnum, row). All elements are ints. The first is a mapping from
     412        serverid to group number, the second is a mapping from shnum to
     413        subgroup number. The third is a row within the subgroup.
     414
     415        We also return a list of lists of rowcounts, so renderers can decide
     416        how much vertical space to give to each row.
     417        """
     418
     419        serverid_to_group = {}
     420        serverid_and_shnum_to_subgroup = {}
     421        groupnum_to_rows = {} # maps groupnum to a table of rows. Each table
     422                              # is a list with an element for each row number
     423                              # (int starting from 0) that contains a
     424                              # finish_time, indicating that the row is empty
     425                              # beyond that time. If finish_time is None, it
     426                              # indicate a response that has not yet
     427                              # completed, so the row cannot be reused.
     428        new_events = []
     429        for ev in events:
     430            # DownloadStatus promises to give us events in temporal order
     431            ev = ev.copy()
     432            ev["serverid"] = base32.b2a(ev["serverid"])
     433            if ev["serverid"] not in serverid_to_group:
     434                groupnum = len(serverid_to_group)
     435                serverid_to_group[ev["serverid"]] = groupnum
     436            groupnum = serverid_to_group[ev["serverid"]]
     437            if groupnum not in groupnum_to_rows:
     438                groupnum_to_rows[groupnum] = []
     439            rows = groupnum_to_rows[groupnum]
     440            # find an empty slot in the rows
     441            free_slot = None
     442            for row,finished in enumerate(rows):
     443                if finished is not None:
     444                    if ev["start_time"] > finished:
     445                        free_slot = row
     446                        break
     447            if free_slot is None:
     448                free_slot = len(rows)
     449                rows.append(ev["finish_time"])
     450            else:
     451                rows[free_slot] = ev["finish_time"]
     452            ev["row"] = (groupnum, free_slot)
     453            new_events.append(ev)
     454        # maybe also return serverid_to_group, groupnum_to_rows, and some
     455        # indication of the highest finish_time
     456        #
     457        # actually, return the highest rownum for each groupnum
     458        highest_rownums = [len(groupnum_to_rows[groupnum])
     459                           for groupnum in range(len(serverid_to_group))]
     460        return new_events, highest_rownums
     461
     462    def child_timeline_parameters(self, ctx):
     463        ds = self.download_status
     464        d = { "start": tfmt(ds.started),
     465              "end": tfmt(ds.started+2.0),
     466              }
     467        return simplejson.dumps(d, indent=1) + "\n"
     468
     469    def child_event_json(self, ctx):
     470        inevow.IRequest(ctx).setHeader("content-type", "text/plain")
     471        data = { } # this will be returned to the GET
     472        ds = self.download_status
     473
     474        data["misc"] = self._find_overlap(ds.misc_events,
     475                                          "start_time", "finish_time")
     476        data["read"] = self._find_overlap(ds.read_events,
     477                                          "start_time", "finish_time")
     478        data["segment"] = self._find_overlap(ds.segment_events,
     479                                             "start_time", "finish_time")
     480        # TODO: overlap on DYHB isn't very useful, and usually gets in the
     481        # way. So don't do it.
     482        data["dyhb"] = self._find_overlap(ds.dyhb_requests,
     483                                          "start_time", "finish_time")
     484        data["block"],data["block_rownums"] = self._find_overlap_requests(ds.block_requests)
     485
     486        servernums = {}
     487        serverid_strings = {}
     488        for d_ev in data["dyhb"]:
     489            if d_ev["serverid"] not in servernums:
     490                servernum = len(servernums)
     491                servernums[d_ev["serverid"]] = servernum
     492                #title= "%s: %s" % ( ",".join([str(shnum) for shnum in shnums]))
     493                serverid_strings[servernum] = d_ev["serverid"][:4]
     494        data["server_info"] = dict([(serverid, {"num": servernums[serverid],
     495                                                "color": self.color(base32.a2b(serverid)),
     496                                                "short": serverid_strings[servernums[serverid]],
     497                                                })
     498                                   for serverid in servernums.keys()])
     499        data["num_serverids"] = len(serverid_strings)
     500        # we'd prefer the keys of serverids[] to be ints, but this is JSON,
     501        # so they get converted to strings. Stupid javascript.
     502        data["serverids"] = serverid_strings
     503        data["bounds"] = {"min": ds.first_timestamp,
     504                          "max": ds.last_timestamp,
     505                          }
     506        # for testing
     507        ## data["bounds"]["max"] = tfmt(max([d_ev["finish_time"]
     508        ##                                   for d_ev in data["dyhb"]
     509        ##                                   if d_ev["finish_time"] is not None]
     510        ##                                  ))
    380511        return simplejson.dumps(data, indent=1) + "\n"
    381512
     513    def render_timeline_link(self, ctx, data):
     514        from nevow import url
     515        return T.a(href=url.URL.fromContext(ctx).child("timeline"))["timeline"]
     516
     517    def _rate_and_time(self, bytes, seconds):
     518        time_s = self.render_time(None, seconds)
     519        if seconds != 0:
     520            rate = self.render_rate(None, 1.0 * bytes / seconds)
     521            return T.span(title=rate)[time_s]
     522        return T.span[time_s]
     523
    382524    def render_events(self, ctx, data):
    383525        if not self.download_status.storage_index:
    384526            return
    class DownloadStatusPage(DownloadResultsRendererMixin, rend.Page): 
    388530        t = T.table(class_="status-download-events")
    389531        t[T.tr[T.td["serverid"], T.td["sent"], T.td["received"],
    390532               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
     533        for d_ev in self.download_status.dyhb_requests:
     534            serverid = d_ev["serverid"]
     535            sent = d_ev["start_time"]
     536            shnums = d_ev["response_shnums"]
     537            received = d_ev["finish_time"]
    398538            serverid_s = idlib.shortnodeid_b2a(serverid)
    399539            rtt = None
    400540            if received is not None:
    class DownloadStatusPage(DownloadResultsRendererMixin, rend.Page): 
    413553               T.td["time"], T.td["decrypttime"], T.td["pausedtime"],
    414554               T.td["speed"]]]
    415555        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
     556            start = r_ev["start"]
     557            length = r_ev["length"]
     558            bytes = r_ev["bytes_returned"]
     559            decrypt_time = ""
     560            if bytes:
     561                decrypt_time = self._rate_and_time(bytes, r_ev["decrypt_time"])
     562            speed, rtt = "",""
     563            if r_ev["finish_time"] is not None:
     564                rtt = r_ev["finish_time"] - r_ev["start_time"] - r_ev["paused_time"]
    419565                speed = self.render_rate(None, compute_rate(bytes, rtt))
    420566                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 = "","","",""
     567            paused = self.render_time(None, r_ev["paused_time"])
     568
    425569            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],
     570                   T.td[srt(r_ev["start_time"])], T.td[srt(r_ev["finish_time"])],
     571                   T.td[bytes], T.td[rtt],
     572                   T.td[decrypt_time], T.td[paused],
    428573                   T.td[speed],
    429574                   ]]
    430575        l["Read Events:", t]
    431576
    432577        t = T.table(class_="status-download-events")
    433         t[T.tr[T.td["type"], T.td["segnum"], T.td["when"], T.td["range"],
     578        t[T.tr[T.td["segnum"], T.td["start"], T.td["active"], T.td["finish"],
     579               T.td["range"],
    434580               T.td["decodetime"], T.td["segtime"], T.td["speed"]]]
    435         reqtime = (None, None)
    436581        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]
     582            range_s = ""
     583            segtime_s = ""
     584            speed = ""
     585            decode_time = ""
     586            if s_ev["finish_time"] is not None:
     587                if s_ev["success"]:
     588                    segtime = s_ev["finish_time"] - s_ev["active_time"]
     589                    segtime_s = self.render_time(None, segtime)
     590                    seglen = s_ev["segment_length"]
     591                    range_s = "[%d:+%d]" % (s_ev["segment_start"], seglen)
    445592                    speed = self.render_rate(None, compute_rate(seglen, segtime))
    446                     segtime = self.render_time(None, segtime)
     593                    decode_time = self._rate_and_time(seglen, s_ev["decode_time"])
    447594                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]]]
     595                    # error
     596                    range_s = "error"
     597            else:
     598                # not finished yet
     599                pass
     600
     601            t[T.tr[T.td["seg%d" % s_ev["segment_number"]],
     602                   T.td[srt(s_ev["start_time"])],
     603                   T.td[srt(s_ev["active_time"])],
     604                   T.td[srt(s_ev["finish_time"])],
     605                   T.td[range_s],
     606                   T.td[decode_time],
     607                   T.td[segtime_s], T.td[speed]]]
    456608        l["Segment Events:", t]
    457609
    458610        t = T.table(border="1")
    459611        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
     612               T.td["txtime"], T.td["rxtime"],
     613               T.td["received"], T.td["RTT"]]]
     614        for r_ev in self.download_status.block_requests:
    469615            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],
     616            if r_ev["finish_time"] is not None:
     617                rtt = r_ev["finish_time"] - r_ev["start_time"]
     618            serverid_s = idlib.shortnodeid_b2a(r_ev["serverid"])
     619            t[T.tr(style="background: %s" % self.color(r_ev["serverid"]))[
     620                T.td[serverid_s], T.td[r_ev["shnum"]],
     621                T.td["[%d:+%d]" % (r_ev["start"], r_ev["length"])],
     622                T.td[srt(r_ev["start_time"])], T.td[srt(r_ev["finish_time"])],
     623                T.td[r_ev["response_length"] or ""],
    477624                T.td[self.render_time(None, rtt)],
    478625                ]]
    479626        l["Requests:", t]
    class DownloadStatusPage(DownloadResultsRendererMixin, rend.Page): 
    524671    def render_status(self, ctx, data):
    525672        return data.get_status()
    526673
     674class DownloadStatusTimelinePage(rend.Page):
     675    docFactory = getxmlfile("download-status-timeline.xhtml")
     676
     677    def render_started(self, ctx, data):
     678        TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
     679        started_s = time.strftime(TIME_FORMAT,
     680                                  time.localtime(data.get_started()))
     681        return started_s + " (%s)" % data.get_started()
     682
     683    def render_si(self, ctx, data):
     684        si_s = base32.b2a_or_none(data.get_storage_index())
     685        if si_s is None:
     686            si_s = "(None)"
     687        return si_s
     688
     689    def render_helper(self, ctx, data):
     690        return {True: "Yes",
     691                False: "No"}[data.using_helper()]
     692
     693    def render_total_size(self, ctx, data):
     694        size = data.get_size()
     695        if size is None:
     696            return "(unknown)"
     697        return size
     698
     699    def render_progress(self, ctx, data):
     700        progress = data.get_progress()
     701        # TODO: make an ascii-art bar
     702        return "%.1f%%" % (100.0 * progress)
     703
     704    def render_status(self, ctx, data):
     705        return data.get_status()
     706
    527707class RetrieveStatusPage(rend.Page, RateAndTimeMixin):
    528708    docFactory = getxmlfile("retrieve-status.xhtml")
    529709
  • 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