IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 501
E..)$p@.@..].........[...}.....B...........
...|...|GET /cosmo/console/home/browse/ HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/cosmo/console/users
Cookie: JSESSIONID=4F6FC0BD92E7D16DB86C9930CB4C0167


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 2222
E...6?@.@..............[...B.}.............
.......|HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Pragma: No-cache
Cache-Control: no-cache,no-store,max-age=0
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/html;charset=ISO-8859-1
Transfer-Encoding: chunked
Date: Sun, 08 Oct 2006 23:32:50 GMT

78e
























  
  


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-NZ" xml:lang="en-NZ">
  <head>
    <title>
      Cosmo | /
    </title>
    <link rel="stylesheet" type="text/css"
          href="/cosmo/cosmo.css"/>
    <script type="text/javascript"
            src="/cosmo/cosmo.js"></script>
  </head>
  <body class="bodystyle">
    <table border="0" cellpadding="0" cellspacing="0" width="100%">
      <tr>
        <td align="left" valign="top">
          <div class="lg">
            <a href="/cosmo/console/account"><img src="/cosmo/cosmo_logo.gif" alt="Cosmo Sharing Server" /></a>
          </div>
        </td>
        
          <td align="right" valign="top">
            <!-- main navbar -->
            <div class="mdData">
              <a href="/cosmo/console/help">Help</a>
              |
              
              <a href="/cosmo/console/about" onclick="simplePopUp('/cosmo/console/about', 340, 280, false); return false;">About</a>
              |
              <a href="/cosmo/console/logout">Log Out</a>
            </div>
            <div class="mdData" style="margin-top:8px;">
              Logged in as <strong>root</strong>
            </div>
            <!-- end main navbar -->
          </td>
        
      </tr>
    </table>
    <hr noshade="noshade"/>
    
      
        
          <!-- admin console navbar -->
          <div class="md">
            Admin console:
            <a href="/cosmo/console/users">Users</a>
            |
            <a href="/cosmo/console/home/browse/">Home Directories</a>
            |
            <a href="/cosmo/console/status">Server Status</a>
            <!-- end admin console navbar -->
          </div>
          <hr noshade="noshade"/>
        
      
      
    
    <div class="md">
      <!-- page body -->
      

IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 1463
E...6@@.@..............[.....}.............
..."....5b0



















<div class="hd" style="margin-top: 12px;">
  /
</div>

<div style="margin-top:12px;">

<a href="/cosmo/feed/atom/1.0/">[subscribe to feed]</a>
</div>



<div class="hd" style="margin-top: 12px;">
  Contents
</div>

<div style="margin-top:12px;">
  <table cellpadding="4" cellspacing="1" border="0" width="100%">
    <tr>
      <td class="smTableColHead" style="width:1%;">
        &nbsp;
      </td>
      <td class="smTableColHead" style="text-align:left;">
        Name
      </td>
      <td class="smTableColHead">
        Type
      </td>
      <td class="smTableColHead">
        Created
      </td>
      <td class="smTableColHead">
        Last Modified
      </td>
      <td class="smTableColHead">
        Size
      </td>
    </tr>
    
    <tr>
      <td class="smTableData" style="text-align:center; white-space:nowrap;">
      <a href="/cosmo/console/home/browse/andrew">[browse]</a>
        
      </td>
      <td class="smTableData">
        andrew
      </td>
      <td class="smTableData" style="text-align:center;">
        Home
      </td>
      <td class="smTableData" style="text-align:center;">         
        Mon Oct 09 12:20:37 NZDT 2006
      </td>
      <td class="smTableData" style="text-align:center;">
        <span class="disabled">-</span>
      </td>
      <td class="smTableData" style="text-align:center;">
        <span class="disabled">-</span>
      </td>
    </tr>
    
  </table>
</div>


  
  


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 821
E..i6A@.@..L...........[.....}.......^.....
......."32e



























  


<div class="hd" style="margin-top: 12px;">
  Tickets
</div>

<div style="margin-top:12px;">
<a href="/cosmo/console/home/ticket//new">[new ticket]</a>
</div>

<div style="margin-top:12px;">
  <table cellpadding="4" cellspacing="1" border="0" width="100%">
    <tr>
      <td class="smTableColHead" style="width:1%;">
        &nbsp;
      </td>
      <td class="smTableColHead" style="text-align:left;">
        Id
      </td>
      <td class="smTableColHead">
        Sharing Links
      </td>
      <td class="smTableColHead">
        Owner
      </td>
      <td class="smTableColHead">
        Timeout
      </td>
      <td class="smTableColHead">
        Privileges
      </td>
      <td class="smTableColHead">
        Created
      </td>
    </tr>
    
  </table>
</div>



  


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 465
E...6B@.@..............[.....}.............
........1c5





















<div class="hd" style="margin-top: 12px;">
  Properties
</div>

<div style="margin-top:12px;">
  <table cellpadding="4" cellspacing="1" border="0" width="100%">
    <tr>
      <td class="smTableColHead" style="text-align:left;">
        Name
      </td>
      <td class="smTableColHead" style="text-align:left;">
        Value
      </td>
    </tr>
    
  </table>
</div>


      <!-- end page body -->
    </div>
  </body>
</html>

0


IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 502
E..*$u@.@..W.........[...}.................
..,$....GET /cosmo/console/status HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/cosmo/console/home/browse/
Cookie: JSESSIONID=4F6FC0BD92E7D16DB86C9930CB4C0167


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 2215
E...6D@.@..............[.....}.....".......
..-...,$HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Pragma: No-cache
Cache-Control: no-cache,no-store,max-age=0
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/html;charset=ISO-8859-1
Transfer-Encoding: chunked
Date: Sun, 08 Oct 2006 23:33:14 GMT

787
























  
  


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-NZ" xml:lang="en-NZ">
  <head>
    <title>
      Cosmo | Server Status
    </title>
    <link rel="stylesheet" type="text/css"
          href="/cosmo/cosmo.css"/>
    <script type="text/javascript"
            src="/cosmo/cosmo.js"></script>
  </head>
  <body class="bodystyle">
    <table border="0" cellpadding="0" cellspacing="0" width="100%">
      <tr>
        <td align="left" valign="top">
          <div class="lg">
            <a href="/cosmo/console/account"><img src="/cosmo/cosmo_logo.gif" alt="Cosmo Sharing Server" /></a>
          </div>
        </td>
        
          <td align="right" valign="top">
            <!-- main navbar -->
            <div class="mdData">
              <a href="/cosmo/console/help">Help</a>
              |
              
              <a href="/cosmo/console/about" onclick="simplePopUp('/cosmo/console/about', 340, 280, false); return false;">About</a>
              |
              <a href="/cosmo/console/logout">Log Out</a>
            </div>
            <div class="mdData" style="margin-top:8px;">
              Logged in as <strong>root</strong>
            </div>
            <!-- end main navbar -->
          </td>
        
      </tr>
    </table>
    <hr noshade="noshade"/>
    
      
        
          <!-- admin console navbar -->
          <div class="md">
            Admin console:
            <a href="/cosmo/console/users">Users</a>
            |
            <a href="/cosmo/console/home/browse/">Home Directories</a>
            |
            <strong>Server Status</strong>
            <!-- end admin console navbar -->
          </div>
          <hr noshade="noshade"/>
        
      
      
    
    <div class="md">
      <!-- page body -->
      

IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 1193
E...6E@.@..............[...T.}.....".......
..2...-.49d



































<!-- simple memory stats -->
<div style="margin-top:24px; text-align: left;">
  <table cellpadding="4" cellspacing="1" border="0">
    <tr>
      <td class="smTableColHead">
        Memory
      </td>
      <td class="smTableColHead">
        KB
      </td>
    </tr>
    <tr>
      <td class="smLabel">
        Max
      </td>
      <td class="smData" style="text-align:right">
        64832
      </td>
    </tr>
    <tr>
      <td class="smLabel">
        Total
      </td>
      <td class="smData" style="text-align:right">
        32908
      </td>
    </tr>
    <tr>
      <td class="smLabel">
        Used
      </td>
      <td class="smData" style="text-align:right">
        25749
      </td>
    </tr>
    <tr>
      <td class="smLabel">
        Free
      </td>
      <td class="smData" style="text-align:right">
        7159
      </td>
    </tr>
  </table>
</div>

<!-- controls -->
<div class="md" style="margin-top:24px; text-align: left;">
  <a href="/cosmo/console/status/gc">Run garbage collector</a>
  |
  <a href="/cosmo/console/status/dump">Dump raw data</a>
</div>

      <!-- end page body -->
    </div>
  </body>
</html>

0


IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 496
E..$$x@.@..Z.........[...}.................
..K ..2.GET /cosmo/console/logout HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/cosmo/console/status
Cookie: JSESSIONID=4F6FC0BD92E7D16DB86C9930CB4C0167


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 308
E..h6G@.@..G...........[.....}.....3.\.....
..L...K HTTP/1.1 302 Moved Temporarily
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Pragma: No-cache
Cache-Control: no-cache,no-store,max-age=0
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Location: http://localhost:8080/cosmo/
Content-Type: text/html
Content-Length: 0
Date: Sun, 08 Oct 2006 23:33:22 GMT


IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 482
E...$z@.@..f.........[...}.....1...........
..L...L.GET /cosmo/ HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/cosmo/console/status
Cookie: JSESSIONID=4F6FC0BD92E7D16DB86C9930CB4C0167


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 1262
E.."6I@.@..............[...1.}.....C.......
..M...L.HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Set-Cookie: JSESSIONID=F08FE7925DD8B2743222B1C2D8583D18; Path=/cosmo
Pragma: No-cache
Cache-Control: no-cache,no-store,max-age=0
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/html;charset=ISO-8859-1
Transfer-Encoding: chunked
Date: Sun, 08 Oct 2006 23:33:22 GMT

388

























<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-NZ" xml:lang="en-NZ">
  <head>
    <title>
      Cosmo | Log in
    </title>
    <link rel="stylesheet" type="text/css"
          href="/cosmo/cosmo.css"/>
    <script type="text/javascript"
            src="/cosmo/cosmo.js"></script>
  </head>
  <body class="bodystyle">
    <table border="0" cellpadding="0" cellspacing="0" width="100%">
      <tr>
        <td align="left" valign="top">
          <div class="lg">
            <a href="/cosmo/console/account"><img src="/cosmo/cosmo_logo.gif" alt="Cosmo Sharing Server" /></a>
          </div>
        </td>
        
      </tr>
    </table>
    <hr noshade="noshade"/>
    
      
      
      
    
    <div class="md">
      <!-- page body -->
      

IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 2212
E...6J@.@..............[..
..}.....C.......
..M...ME898





















<form method="POST" action="/cosmo/console/j_acegi_security_check">

<div style="width:100%;" align="center">

<div id="loginErrDiv" style="width:280px; margin-top:8px; text-align:left;">
<div class="sm" style="height:36px;">



&nbsp;


</div>
</div>

<div class="widgetBorder" style="margin-top:4px; width:280px;">
<div class="widgetContent" style="padding:16px;">

  <table border="0" cellpadding="0" cellspacing="0">
  <tr>
  <td colspan="2"><div class="hd" style="margin-bottom:8px; text-align:left;">Log In</div></td>
  </tr>
    <tr>
      <td class="mdLabel" style="text-align:right;">
        Username:&nbsp;&nbsp;
      </td>
      <td>
          <input type="text" name="j_username" size="16" maxlength="32" class="textInput"/>
      </td>
    </tr>
    <tr>
    <td colspan="2" class="spacerDiv" style="height:8px;"></td>
    </tr>
    <tr>
      <td class="mdLabel" style="text-align:right;">
	Password:&nbsp;&nbsp;
      </td>
      <td>
          <input type="password" name="j_password" size="16" maxlength="16" class="textInput"/>
      </td>
    </tr>
    <tr>
    <td colspan="2" class="spacerDiv" style="height:16px;"></td>
    </tr>
    <tr>
    <td colspan="2" style="text-align:right;"><input type="submit" name="ok" value="Log In" class="buttonInput"/></td>
    </tr>
  </table>

<div class="sm" style="margin-top:12px;">

<a href="/cosmo/console/forgot" onclick="popup('/cosmo/console/forgot', 'forgot', 'resizable=yes,width=560,height=300,left=40,screenx=40,top=20,screeny=20,scrollbars'); return false">Forgot your username or password?</a>
</div>
  
</div>
</div>

<div class="mdData" style="margin-top:18px;">Don&#8217;t have an account?</div>
<div><a href="/cosmo/console/account/new">Click here to create one.</a></div>

<div class="sm" style="margin-top:24px;">

<a href="/cosmo/console/about" onclick="simplePopUp('/cosmo/console/about', 340, 280, false); return false;">About Cosmo Sharing Server ...</a>
</div>


</div>

</form>

<script type="text/javascript" language="JavaScript1.3">

window.onload=doFocus;

function doFocus() {
	document.forms[0].j_username.focus();
}

</script>

      <!-- end page body -->
    </div>
  </body>
</html>

0


IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 376
E...$}@.@............[...}.................
......M.GET /scooby HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 172
E...6L@.@..............[.....}.H...T.......
...}....HTTP/1.1 302 Moved Temporarily
Server: Apache-Coyote/1.1
Location: http://localhost:8080/scooby/
Transfer-Encoding: chunked
Date: Sun, 08 Oct 2006 23:33:48 GMT

0


IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 377
E...$.@.@............[...}.H...o...........
...~...}GET /scooby/ HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 283
E..O6N@.@..Y...........[...o.}.....e.C.....
.......~HTTP/1.1 302 Moved Temporarily
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; Path=/scooby
Location: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Content-Length: 0
Date: Sun, 08 Oct 2006 23:33:48 GMT


IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 484
E...$.@.@..^.........[...}...........
.....
........GET /scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8 HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 2755
E.
.6P@.@..............[.....~.....v.......
...e....HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=ISO-8859-1
Content-Language: en-NZ
Content-Length: 2581
Date: Sun, 08 Oct 2006 23:33:51 GMT





 <!DOCTYPE html
  PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<title>Welcome to Scooby</title>

<script type="text/javascript" src="i18n.js"></script>
<script type="text/javascript" src="js/scooby/ui/ui.conf.js"></script>
<script type="text/javascript" src="js/scooby/ui/button.js"></script>
<script type="text/javascript" src="js/scooby/ui/login.js"></script>
<script type="text/javascript" src="js/scooby/ui/styler.js"></script>
<script type="text/javascript" src="js/scooby/util/ajax.js"></script>
<script type="text/javascript" src="js/scooby/util/cookie.js"></script>
<script type="text/javascript" src="js/scooby/util/log.js"></script>
<script type="text/javascript" src="js/scooby/util/popup.js"></script>


<script type="text/javascript">

var AUTH_PROC = 'j_acegi_security_check';

</script>

<script type="text/javascript" src="js/scooby/ui/global.css.js"></script>

</head>

<body>
  <div>
    <div style="margin:auto; width:280px; text-align:left; padding-top:24px;">
      <div id="logoDiv" style="padding-bottom:12px;"></div>
      <div id="promptDiv" class="promptText" style="height:46px;">
        Enter a username and password, and click the Submit button.
      </div>
      <div class="baseWidget">
        <div style="padding:12px;">
          <form id="loginForm" name="loginForm" method="post" 
            action="j_acegi_security_check" onsubmit="return false;">
            <table>
              <tr>
                <td class="formTitleHoriz">
                  Username:&nbsp;
                </td>
                <td style="padding-bottom:4px;">
                  <input class="inputText" type="text" name="j_username"/>
                </td>
              </tr>
              <tr>
                <td class="formTitleHoriz">
                  Password:&nbsp;
                </td>
                <td><input class="inputText" type="password" 
                  name="j_password" onfocus="Login.loginFocus=true;" 
                  onblur="Login.loginFocus=false;"/></td>
              </tr>
            </table>
            <div id="submitButtonDiv" 
              style="margin-top:16px; float:right;">
            </div>
            <div style="clear:both;"></div>
          </form>
        </div>
      </div>
      <div style="padding-top:36px; text-align:center;">
        <a href="javascript:Popup.open('about.html', 380, 280);">
          About Scooby Web Calendar ...
        </a>
      </div>
    </div>
  </div>
</body>

</html>

IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 435
E...$.@.@............[...~...."M...........
...p...eGET /scooby/i18n.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 3882
E..^6R@.@..E...........[.."M.~.X....
S.....
.......pHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=ISO-8859-1
Content-Language: en-NZ
Content-Length: 3708
Date: Sun, 08 Oct 2006 23:33:51 GMT





 
var localText = [];

function getText(str) {
    return localText[str];
}


    localText["App.Button.Save"] = "Save";

    localText["App.August"] = "August";

    localText["Main.LogOut"] = "Log Out";

    localText["App.Sat"] = "Sat";

    localText["App.Nov"] = "Nov";

    localText["App.Tue"] = "Tue";

    localText["App.October"] = "October";

    localText["App.Button.Cancel"] = "Cancel";

    localText["Main.DetailForm.Title"] = "Title";

    localText["App.Friday"] = "Friday";

    localText["App.Jun"] = "Jun";

    localText["Login.Error.NoLoginWithRootAccount"] = "You cannot log in to Scooby with the Cosmo root user account.";

    localText["App.Saturday"] = "Saturday";

    localText["App.Wed"] = "Wed";

    localText["App.Thu"] = "Thu";

    localText["App.Sun"] = "Sun";

    localText["App.July"] = "July";

    localText["App.Jul"] = "Jul";

    localText["App.Oct"] = "Oct";

    localText["App.February"] = "February";

    localText["Main.Error.EventNewSaveFailed"] = "Could not save this new event.";

    localText["App.November"] = "November";

    localText["App.Wednesday"] = "Wednesday";

    localText["Login.Error.AuthFailed"] = "Could not verify the entered username and password. Please try again.";

    localText["Main.Processing"] = "Processing ...";

    localText["Login.Prompt"] = "Enter a username and password, and click the Submit button.";

    localText["Main.Error.LoadEventsFailed"] = "Could not load events.";

    localText["App.January"] = "January";

    localText["App.PM"] = "PM";

    localText["App.Fri"] = "Fri";

    localText["App.Dec"] = "Dec";

    localText["Main.Preferences"] = "Preferences";

    localText["Login.Username"] = "Username:";

    localText["Main.About"] = "About Scooby";

    localText["App.Jan"] = "Jan";

    localText["Login.About"] = "About Scooby Web Calendar ...";

    localText["Main.Error.EventRemoveFailed"] = "Could not remove this event.";

    localText["App.September"] = "September";

    localText["App.Feb"] = "Feb";

    localText["App.May"] = "May";

    localText["App.Thursday"] = "Thursday";

    localText["Main.Error.EventTimezoneMismatch"] = "Calendar event Start and End have mismatched timezones.";

    localText["Main.Welcome"] = "Welcome,";

    localText["Main.DetailForm.At"] = "at";

    localText["App.June"] = "June";

    localText["No"] = "No";

    localText["Main.DetailForm.Ends"] = "Ends";

    localText["Login.Password"] = "Password:";

    localText["App.Welcome"] = "Welcome to Scooby";

    localText["App.Button.OK"] = "OK";

    localText["App.Sep"] = "Sep";

    localText["App.December"] = "December";

    localText["Login.Error.RequiredFields"] = "Both Username and Password are required fields.";

    localText["App.Monday"] = "Monday";

    localText["App.March"] = "March";

    localText["App.Mon"] = "Mon";

    localText["Main.Prompt.EventRemoveConfirm"] = "Remove this event?";

    localText["App.Tuesday"] = "Tuesday";

    localText["Main.DetailForm.Description"] = "Description";

    localText["App.Sunday"] = "Sunday";

    localText["Main.NewEvent"] = "New Event";

    localText["Yes"] = "Yes";

    localText["App.Aug"] = "Aug";

    localText["Main.Error.InitCalCreateFailed"] = "Could not create calendar for new user.";

    localText["App.Button.Submit"] = "Submit";

    localText["App.Mar"] = "Mar";

    localText["Main.DetailForm.Starts"] = "Starts";

    localText["Main.Error.EventEditSaveFailed"] = "Could not save the changes to this event.";

    localText["App.April"] = "April";

    localText["App.Button.Remove"] = "Remove";

    localText["App.Apr"] = "Apr";

    localText["App.Noon"] = "Noon";

    localText["App.AM"] = "AM";

IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 451
E...$.@.@..}.........[...~.X..1w...........
........GET /scooby/js/scooby/ui/ui.conf.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 2620
E.
p6T@.@..1...........[..1w.~.......e.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"2408-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: text/javascript
Content-Length: 2408
Date: Sun, 08 Oct 2006 23:33:51 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

// Configurable UI options
var TEMPLATE_DIRECTORY = 'default'; // Template directory to use
var DISPLAY_WIDTH_PERCENT = 1.0; // Percent of window width to draw entire display
var DISPLAY_HEIGHT_PERCENT = 1.0; // Percent of window height to draw entire display
var TOP_MENU_HEIGHT = 48; // Height for top navigation area -- Month name and nav arrows
var LEFT_SIDEBAR_WIDTH = 160; // Width of lefthand sidebar
var RIGHT_SIDEBAR_WIDTH = 270; // Width of righthand sidebar
var CAL_TOP_NAV_HEIGHT = 36; // Height for top navigation area -- Month name and nav arrows
var DAY_LIST_DIV_HEIGHT = 16; // Height for list of days/dates for each day col
var ALL_DAY_RESIZE_HANDLE_HEIGHT = 8; // Height for resizer handle under area for 'no time' events
var ALL_DAY_RESIZE_AREA_HEIGHT = 60-ALL_DAY_RESIZE_HANDLE_HEIGHT;
var HOUR_UNIT_HEIGHT = 80; // Height of one hour block in each day col
var HOUR_DIV_HEIGHT = (HOUR_UNIT_HEIGHT - 1); // Allow one px for border on outside of box per retarded CSS spec
var VIEW_DIV_HEIGHT = (HOUR_UNIT_HEIGHT*24); // 24 hours' worth of height
var BLOCK_RESIZE_LIP_HEIGHT = 6; // Height in pixels of resizeable 'grab' area at top and bottom of block
var EVENT_DETAIL_FORM_HEIGHT = 380;
var EVENT_INFO_FORM_WIDTH = RIGHT_SIDEBAR_WIDTH-12; // Width of the form on the right that displays info for the selected event
var SCROLLBAR_SPACER_WIDTH = 20; // Spacer to prevent horizontal scrollbar in view pane
var HOUR_LISTING_WIDTH = 42; // Space for the vertical listing of hours on the left of the events
var PROCESSING_ANIM_HEIGHT = 36;
var PROCESSING_ANIM_WIDTH = 120;
var DIALOG_BOX_WIDTH = 380;
var DIALOG_BOX_HEIGHT = 280;
var LOGO_GRAPHIC = 'logo_main.gif';
var LOGO_GRAPHIC_SM = 'logo_sm.gif';
var BUTTON_DIR_PATH = 'templates/' + TEMPLATE_DIRECTORY + '/images/';


IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 450
E...$.@.@..}.........[...~....;............
........GET /scooby/js/scooby/ui/button.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 9000
E.#\6U@.@..D...........[..;..~......!Q.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"13716-1149754364000"
Last-Modified: Thu, 08 Jun 2006 08:12:44 GMT
Content-Type: text/javascript
Content-Length: 13716
Date: Sun, 08 Oct 2006 23:33:51 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

/**
 * @fileoverview Calendar events -- links the Block to the CalEventData
 * @author Matthew Eernisse mailto:mde@osafoundation.org
 * @license Apache License 2.0
 */

/**
 * @object Button -- creates a button with a text label, and images
 * for endcaps and a stretchable, tiling middle section. Comes in
 * two sizes, normal and small.
 * TO-DO: Refactor so this big ol' string function doesn't come
 * first
 * TO-DO: Consider doing this all with DOM methods 
 * FIXME: A standard object constructor function should probably not
 * return a DOM node. Is it an object? Or a DOM node? Make it a static
 * method of a Button singleton or something
 */
function Button(elemId, width, onClickHandler, displayText, isSmall) {

    /**
     * The main function that returns the markup for the button
     * TO-DO: Do this all with DOM methods
     */
    this.htmlString = function() {
        var str = '';
        str += '<table cellpadding="0" cellspacing="0"' +
            ' border="0" style="width:' + this.width + 'px;';
        if (this.disable) {
            str += ' opacity:0.6;';
            if (document.all) {
                str += ' filter:alpha(opacity=60);';
            }
        }
        str += '">\n';
        str += '<tr>\n';
        str += '<td id="' + this.elemId + 'Left" style="width:' + 
            this.capWidth + 'px; height:' + this.buttonHeight +
           'px; background:url(' + BUTTON_DIR_PATH + 'button_left';
        if (this.disable) {
            str += '_dim';
        }
        if (this.isSmall) {
            str += '_sm';
        }
        str += '.gif);';
        if (!this.disable) {
            str += ' cursor:pointer;"';
            str += ' onmouseover="Button.over(\'' + this.elemId +
              '\', ' + this.isSmall + ');" onmouseout="Button.out(\'' + 
              this.elemId + '\', ' + this.isSmall + ');"';
        }
        else {
            str += '"';
        }
        str += '></td>\n';
        str += '<td id="' + this.elemId + 'Center" style="height:' + 
            this.buttonHeight +
           'px; text-align:center; background:url(' + BUTTON_DIR_PATH + 
           'button_center';
        if (this.disable) {
            str += '_dim';
        }
        if (this.isSmall) {
            str += '_sm';
        }
        str += '.gif);';
        if (!this.disable) {
            str += ' cursor:pointer;"';
            str += ' onmouseover="Button.over(\'' + this.elemId +
              '\', ' + this.isSmall + ');" onmouseout="Button.out(\'' + 
              this.elemId + '\', ' + this.isSmall + ');"';
        }
        else {
            str += '"';
        }
        str += '>';
        str += '<div id="' + this.elemId + 'Text" class="buttonText';
        if (this.disable) {
           str += 'Disabled';
        }
        if (this.isSmall) {
            str += 'Sm';
        }
        str += '">' + this.displayText + '</div>';
        str += '</td>\n';
        str += '<td id="' + this.elemId + 'Right" style="width:' + 
            this.capWidth + 'px; height:' + this.buttonHeight +
           'px; background:url(' + BUTTON_DIR_PATH + 'button_right';
        if (this.disable) {
            str += '_dim';
        }
        if (this.isSmall) {
            str += '_sm';
        }
        str += '.gif);';
        if (!this.disable) {
                str += ' cursor:pointer;"';
                str += ' onmouseover="Button.over(\'' + this.elemId +
                  '\', ' + this.isSmall + ');" onmouseout="Button.out(\'' + 
                  this.elemId + '\', ' + this.isSmall + ');"';
       }
        else {
            str += '"';
        }
        str += '></td>\n';
        str += '</tr>\n';
        str += '</table>\n';
        return str;
    }
   
    // The DOM node for the button
    this.domNode = null;
    // The id/prefix of the DOM elements for this button
    this.elemId = elemId;
    // Width in pixels of the button
    this.width = width;
    // Handler function
    this.onClickHandler = onClickHandler;
    // Text label for the button
    this.displayText = displayText;
    // Create a normal-sized or small button
    this.isSmall = isSmall ? true : false;
    // Width in pixels of the end-cap graphics
    this.capWidth = isSmall ? 9 : 10;
    // Height in pixels of the button
    this.buttonHeight = isSmall ? 18 : 24;
    // Disable button if there's no handler
    this.disable = onClickHandler ? false : true;

    // Create the surrounding span for the button table
    var but = document.createElement('span');
    var cells = null;
    // Create the button span (inline element)
    but.id = this.elemId;
    but.style.background = 'transparent';
    // The button content table is complex -- use innerHTML
    but.innerHTML = this.htmlString(); 
    // If a handler is defined, attach it to the 
    // three cells of the button (left, center, right)
    if (this.onClickHandler) {
    cells = but.getElementsByTagName('TD');
        for (var i = 0; i < cells.length; i++) {
            cells[i].onclick = this.onClickHandler;
        }
    }
    this.domNode = but;
}

/**
 * Highlight a button on mouseover
 */
Button.over = function(elemId, isSmall) {
    var suf = isSmall ? '_sm' : '';
    document.getElementById(elemId + 'Left').style.background = 
        'url(' + BUTTON_DIR_PATH + 'button_left_lit' + suf + '.gif)';
    document.getElementById(elemId + 'Center').style.background = 
        'url(' + BUTTON_DIR_PATH + 'button_center_lit' + suf + '.gif)';
    document.getElementById(elemId + 'Right').style.background = 
        'url(' + BUTTON_DIR_PATH + 'button_right_lit' + suf + '.gif)';
    //document.getElementById(elemId + 'Text').style.color = '#ccccaa';
}

/**
 * Return button to normal state on mouseout
 */
Button.out = function(elemId, isSmall) {
    var suf = isSmall ? '_sm' : '';
    document.getElementById(elemId + 'Left').style.background = 
        'url(' + BUTTON_DIR_PATH + 'button_left' + suf + '.gif)';
    document.getElementById(elemId + 'Center').style.background = 
        'url(' + BUTTON_DIR_PATH + 'button_center' + suf + '.gif)';
    document.getElementById(elemId + 'Right').style.background = 
        'url(' + BUTTON_DIR_PATH + 'button_right' + suf + '.gif)';
    //document.getElementById(elemId + 'Text').style.color = '#339933';
}

/**
 * Function that creates the panel of buttons for the bottom of a
 * dialog box. Three clusters of buttons -- right-aligned, centered,
 * and left-aligned.
 * TO-DO: Try to get rid of innerHTML usage, try to accomplish
 * with floats and margin:auto
 */
var ButtonPanel = function(btnsLeft, btnsCenter, btnsRight) {

    // Button arrays for each section
    this.btnsLeft = btnsLeft ? btnsLeft : [];
    this.btnsCenter = btnsCenter ? btnsCenter : [];
    this.btnsRight = btnsRight ? btnsRight : [];

    this.sectionCell = function(area, btns) {
        var sectionCell = document.createElement('td');
        var areaLowerCase = area.toLowerCase();
        var tbl = document.createElement('table');
        var bdy = document.createElement('tbody');
        var row = document.createElement('tr');
        var cell = null;

        tbl.setAttribute('cellpadding', '0');
        tbl.setAttribute('cellspacing', '0');

        sectionCell.id = 'buttonPanel' + area;
        sectionCell.style.width = '33%';
        sectionCell.innerHTML = '<div id="buttonPanel' + area + 'Div" align="' + areaLowerCase + '"></div>';
        // Insert table of buttons for this section
        if (btns.length) {
            div = sectionCell.firstChild;
            div.appendChild(tbl);
            tbl.appendChild(bdy);
            bdy.appendChild(row);
            for (var i = 0; i < btns.length; i++) {
                cell = document.createElement('td');
                cell.appendChild(btns[i].domNode);
                row.appendChild(cell);
                // Spacer between buttons
                if (i < btns.length-1) {
                    cell = document.createElement('td');
                    cell.setAttribute('width', '1%');
                    cell.innerHTML = '&nbsp;';
                    row.appendChild(cell);
                }
            }
       
IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 4930
E..v6V@.@..)...........[..^..~.......k.....
........ }
        return sectionCell;
    }

    var tbl = document.createElement('table');
    var bdy = document.createElement('tbody');
    var row = document.createElement('tr');

    tbl.setAttribute('width', '100%');
    tbl.setAttribute('cellpadding', '0');
    tbl.setAttribute('cellspacing', '0');
    tbl.appendChild(bdy);
    bdy.appendChild(row);

    row.appendChild(this.sectionCell('Left', this.btnsLeft));
    row.appendChild(this.sectionCell('Center', this.btnsCenter));
    row.appendChild(this.sectionCell('Right', this.btnsRight));

    // Return DOM element of the button-panel table
    return tbl;

}

/**
 * @object NavButtonSet -- creates a two-button cluster with a
 * left and right arrow
 * Note that the line-height CSS property needs to be set
 * for the table cells otherwise they inherit from the
 * container the NavButtonSet is sitting in
 */
function NavButtonSet(id, leftHandler, rightHandler) {
    
    var self = this;
    
    // Private method to create the button-set table
    var doButtonTable = function() {
        var nbTable = document.createElement('table');
        var nbTBody = document.createElement('tbody');
        var nbRow = document.createElement('tr');
        var nbDivider = null;
        
        nbTable.style.width = '45px';
        nbTable.style.height = '18px';
        nbTable.cellPadding = '0px'; // Note camelCaps here, fun!! :)
        nbTable.cellSpacing = '0px'; // More camelCaps, yay!
        nbTable.appendChild(nbTBody);
        nbTBody.appendChild(nbRow);
        // Left endcap
        nbRow.appendChild(doButtonSetCap('Left', leftHandler));
        // Left arrow
        nbRow.appendChild(doButtonSetCenter('Left', leftHandler));
        // Divider cell
        nbDivider = document.createElement('td');
        nbDivider.id = id + 'ButtonDivider';
        nbDivider.style.width = '1px';
        nbDivider.style.height = '18px';
        nbDivider.style.lineHeight = '1px';
        nbDivider.style.background = '#aaaaaa';
        nbRow.appendChild(nbDivider);
        // Right arrow
        nbRow.appendChild(doButtonSetCenter('Right', rightHandler));
        // Right endcap
        nbRow.appendChild(doButtonSetCap('Right', rightHandler));
        return nbTable;
    }
    // Private method to make the endcaps
    var doButtonSetCap = function(side, clickHandler) {
        var nbData = document.createElement('td');
        var lowerCaseSide = side.toLowerCase();
        
        nbData.id = id + side;
        nbData.style.width = '9px';
        nbData.style.height = '18px';
        nbData.style.lineHeight = '18px';
        nbData.style.cursor = 'pointer';
        nbData.style.background = 'url(' + BUTTON_DIR_PATH + 
            'button_' + lowerCaseSide + '_sm.gif)';
        nbData.onmouseover = function() { self.rolloverHandler(side, 'lit'); }
        nbData.onmouseout = function() { self.rolloverHandler(side, ''); }
        nbData.onclick = clickHandler;
        return nbData;
    }
    // Private method to make the center arrow cells
    var doButtonSetCenter = function(side, clickHandler) {
        var nbData = document.createElement('td');
        var nbImg = document.createElement('img');
        var lowerCaseSide = side.toLowerCase();
        
        nbData.id = id + 'Center' + side;
        nbData.style.width = '13px';
        nbData.style.height = '18px';
        nbData.style.lineHeight = '18px';
        nbData.style.textAlign = lowerCaseSide;
        nbData.style.background = 'url(' + BUTTON_DIR_PATH + 
            'button_center_sm.gif)';
        nbData.style.backgroundRepeat = 'repeat-x';
        nbData.style.cursor = 'pointer';
        nbImg.src = BUTTON_DIR_PATH + 'nav_arrow_' + 
            lowerCaseSide + '.gif'; 
        nbData.appendChild(nbImg);
        nbData.onmouseover = function() { self.rolloverHandler(side, 'lit'); }
        nbData.onmouseout = function() { self.rolloverHandler(side, ''); }
        nbData.onclick = clickHandler;
        return nbData;
    }
    
    // Main properties and methods
    // ========
    this.id = id;
    this.domNode = doButtonTable();
    this.rolloverHandler = function(side, state) {
        // Do the rollover for the entire button set together
        // Doing just one side at a time looks freaky
        this.doRollover(side, state);
    };
    this.doRollover = function(side, state) {
        var btnSide = side.toLowerCase();
        var btnState = state ? '_' + state : '';
        var dividerColor = state == 'lit' ? '#000000' : '#aaaaaa';
        document.getElementById(this.id + 'Center' + side).style.background = 
            'url(' + BUTTON_DIR_PATH + 'button_center' + btnState + '_sm.gif)';
        document.getElementById(this.id + side).style.background = 
            'url(' + BUTTON_DIR_PATH + 'button_' + btnSide + btnState + '_sm.gif)';
        document.getElementById(this.id + 'ButtonDivider').style.background = dividerColor;
    };
}

IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 449
E...$.@.@..|.........[...~....r............
........GET /scooby/js/scooby/ui/login.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 3639
E..k6X@.@..2...........[..r..~.......`.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"3427-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: text/javascript
Content-Length: 3427
Date: Sun, 08 Oct 2006 23:33:51 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

/**
 * @fileoverview Login -- used for user auth
 * @author Matthew Eernisse mailto:mde@osafoundation.org
 * @license Apache License 2.0
 */

/**
 * @object Login -- singleton for doing user auth
 */
var Login = new function () {

    this.loginFocus = false;
    this.loginReq = null;
    this.loginForm = null; 
    this.authProc = '';

    this.init = function() {
        var self = Login;
        var but = new Button('submitButton', 74, Login.doLogin, 
            getText('App.Button.Submit'));
        self.loginReq = new Ajax();
        self.loginForm = document.getElementById('loginForm');
        self.authProc = AUTH_PROC;
        self.loginForm.j_username.focus();
        // Add logo and button
        document.getElementById('logoDiv').innerHTML = 
            '<img src="templates/'+ TEMPLATE_DIRECTORY + 
            '/images/' + LOGO_GRAPHIC + '" alt="">';
        document.getElementById('submitButtonDiv').appendChild(but.domNode);
    }
    this.handleLoginResp = function(str) {
        var self = Login;
        if (str.indexOf('login.js') > -1) {
            self.showErr(getText('Login.Error.AuthFailed'));
            self.loginForm.j_password.value = ''; 
        }
        else {
            self.showPrompt('normal', 'Logging you on. Please wait ...');
            location = 'main.page';
        }
    }
    this.doLogin = function() {
        var self = Login;
        var un = self.loginForm.j_username.value;
        var pw = self.loginForm.j_password.value;
        var postData = '';
        var err = '';

        if (!un || !pw) {
            err = getText('Login.Error.RequiredFields');
        }
        else {
            if (un == 'root') {
                err = getText(
                    'Login.Error.NoLoginWithRootAccount');
            }
        }
        if (err) {
            self.showErr(err);
        }
        else {
            Cookie.set('username', un);
            postData = 'j_username=' + un + '&j_password=' + pw;
            self.loginReq.doPost(self.authProc, postData, 
                Login.handleLoginResp);
        }
    }

    this.showErr = function(str) {
        this.showPrompt('error', str);
    }

    this.showPrompt = function(promptType, str) {
        var promptDiv = document.getElementById('promptDiv');
        if (promptType.toLowerCase() == 'error') {
            promptDiv.className = 'promptTextError';
        }
        else {
            promptDiv.className = 'promptText';
        }
        promptDiv.innerHTML = str;
    }
    this.keyUpHandler = function(e) {
        e = !e ? window.event : e;
        if (e.keyCode == 13 && Login.loginFocus) {
            Login.doLogin();
            return false;
        }
    }
}

Login.constructor = null;

document.onkeyup = Login.keyUpHandler;
window.onload = Login.init;


IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 450
E...$.@.@..z.........[...~.....T...........
........GET /scooby/js/scooby/ui/styler.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 2324
E.	H6Z@.@..S...........[...T.~	`.....=.....
...2....HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"2112-1147764336000"
Last-Modified: Tue, 16 May 2006 07:25:36 GMT
Content-Type: text/javascript
Content-Length: 2112
Date: Sun, 08 Oct 2006 23:33:51 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

Styler = new function() {
    this.styles = [];
    this.maxIndex = 0;
    this.addStyle = function(name) {
        var dynStyle = document.createElement('style');
        dynStyle.type = 'text/css';
        dynStyle.media = 'all';
        document.getElementsByTagName("head")[0].appendChild(dynStyle);
        this.styles[name] = new StyleWrapper(document.styleSheets[this.maxIndex]);
        this.maxIndex++;
    };
    this.doOldDocumentDotWriteHack = function(rules) {
        var styleStr = '';
        styleStr += '<style type="text/css">\n';
        for (var i = 0; i < rules.length; i++) {
            styleStr += rules[i] + '\n';
        }
        styleStr += '</style>\n';
        document.write(styleStr);
    }
}

function StyleWrapper(sheet) {
    this.sheet = sheet;
    this.getRules = function() {
        var rules = document.all ? this.sheet.rules : this.sheet.cssRules;
        return rules;
    };
    this.splitRule = new RegExp('\s*(\{)\s*');
    this.rules = this.getRules();
    this.addRule = function(rule, index) {
        if (document.all) {
            var ruleItem = rule.replace(/\s*}/, '');
            var ruleItemArray = ruleItem.split(this.splitRule);
            this.sheet.addRule(ruleItemArray[0], ruleItemArray[1], index);
        }
        else {
            this.sheet.insertRule(rule, index);
        }
    }
    this.loadRules = function(rules) {
        for (var i = 0; i < rules.length; i++) {
            this.addRule(rules[i]);
        }
    }
}
IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 450
E...$.@.@..y.........[...~	`...h...........
...E...2GET /scooby/js/scooby/util/ajax.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 6003
E...6\@.@..............[...h.~."...........
...i...EHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"5791-1147764336000"
Last-Modified: Tue, 16 May 2006 07:25:36 GMT
Content-Type: text/javascript
Content-Length: 5791
Date: Sun, 08 Oct 2006 23:33:52 GMT

/*
 * Copyright 2006 SitePoint Pty. Ltd, www.sitepoint.com
 * and Matthew Eernisse (mde@fleegix.org) 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS;
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Version 1.2.1
*/

function Ajax() {

  // Properties
  // ================================
  this.req = null;
  this.reqId = 0;
  this.url = null;
  this.status = null;
  this.statusText = '';
  this.method = 'GET';
  this.async = true;
  this.dataPayload = null;
  this.readyState = null;
  this.responseText = null;
  this.responseXML = null;
  this.handleResp = null;
  this.responseFormat = 'text', // 'text', 'xml', 'object'
  this.mimeType = null;
  this.headers = [];
  
  var i = 0;
  var reqTry = [ 
    function() { return new XMLHttpRequest(); },
    function() { return new ActiveXObject('Msxml2.XMLHTTP') },
    function() { return new ActiveXObject('Microsoft.XMLHTTP' )} ];
  
  while (!this.req && (i < reqTry.length)) {
    try { this.req = reqTry[i++](); } 
    catch(e) {}
  }
  if (this.req) {
    this.reqId = 0;
  }
  else {
    alert('Could not create XMLHttpRequest object.');
  }
  
  // Methods
  // ================================
  this.doGet = function(url, hand, format) {
    this.url = url;
    this.handleResp = hand;
    this.responseFormat = format || 'text';
    return this.doReq();
  };
  this.doPost = function(url, dataPayload, hand, format) {
    this.url = url;
    this.dataPayload = dataPayload;
    this.handleResp = hand;
    this.responseFormat = format || 'text';
    this.method = 'POST';
    return this.doReq();
  };
  this.doReq = function() {
    var self = null;
    var req = null;
    var id = null;
    var headArr = [];
     
    req = this.req;
    this.reqId++;
    id = this.reqId;
    // Set up the request
    // ==========================
    req.open(this.method, this.url, this.async);
    
    // Override MIME type if necessary for Mozilla/Firefox & Safari
    if (this.mimeType && navigator.userAgent.indexOf('MSIE') == -1) {
      req.overrideMimeType(this.mimeType);
    }
    
    // Add any custom headers that are defined
    if (this.headers.length) {
      // Set any custom headers
      for (var i = 0; i < this.headers.length; i++) {
        headArr = this.headers[i].split(': ');
        req.setRequestHeader(headArr[i], headArr[1]);
      }
      this.headers = [];
    }
    // Otherwise set correct content-type for POST
    else {
      if (this.method == 'POST') {
        req.setRequestHeader('Content-Type', 
          'application/x-www-form-urlencoded');
      }
    }
    
    self = this; // Fix loss-of-scope in inner function
    req.onreadystatechange = function() {
      var resp = null;
      self.readyState = req.readyState;
      if (req.readyState == 4) {
        
        // Make these properties available to the Ajax object
        self.status = req.status;
        self.statusText = req.statusText;
        self.responseText = req.responseText;
        self.responseXML = req.responseXML;
        
        // Set the response according to the desired format
        switch(self.responseFormat) {
          // Text
          case 'text':
            resp = self.responseText;
            break;
          // XML
          case 'xml':
            resp = self.responseXML;
            break;
          // The object itself
          case 'object':
            resp = req;
            break;
        }
        
        // Request is successful -- pass off to response handler
        if (self.status > 199 && self.status < 300) {
          if (self.async) {
              // Make sure handler is defined
              if (!self.handleResp) {
                alert('No response handler defined ' +
                  'for this XMLHttpRequest object.');
                return;
              }
              else {
                self.handleResp(resp, id);
              }
          }
        }
        // Request fails -- pass to error handler
        else {
          self.handleErr(resp);
        }
      }
    }
    // Send the request, along with any data for POSTing
    // ==========================
    req.send(this.dataPayload);
    if (this.async) {
        return id;
    }
    else {
        return req;
    }
  };
  this.abort = function() {
    if (this.req) {
      this.req.onreadystatechange = function() { };
      this.req.abort();
      this.req = null;
    }
  };
  this.handleErr = function() {
    var errorWin;
    // Create new window and display error
    try {
      errorWin = window.open('', 'errorWin');
      errorWin.document.body.innerHTML = this.responseText;
    }
    // If pop-up gets blocked, inform user
    catch(e) {
      alert('An error occurred, but the error message cannot be' +
      ' displayed because of your browser\'s pop-up blocker.\n' +
      'Please allow pop-ups from this Web site.');
    }
  };
  this.setMimeType = function(mimeType) {
    this.mimeType = mimeType;
  };
  this.setHandlerResp = function(funcRef) {
    this.handleResp = funcRef;
  };
  this.setHandlerErr = function(funcRef) {
    this.handleErr = funcRef; 
  };
  this.setHandlerBoth = function(funcRef) {
    this.handleResp = funcRef;
    this.handleErr = funcRef;
  };
  this.setRequestHeader = function(headerName, headerValue) {
    this.headers.push(headerName + ': ' + headerValue);
  };
}

IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 452
E...$.@.@..v.........[...~."...............
...l...iGET /scooby/js/scooby/util/cookie.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 1883
E...6]@.@..	...........[.....~.............
.......lHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"1671-1147764336000"
Last-Modified: Tue, 16 May 2006 07:25:36 GMT
Content-Type: text/javascript
Content-Length: 1671
Date: Sun, 08 Oct 2006 23:33:52 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var Cookie = new function() {
    this.set = function(name, value, days, path) {
        var expires = '';
        var setPath = path ? path : '/';
        if (days) {
            var date = new Date();
            date.setTime(date.getTime() + (days*24*60*60*1000));
            expires = '; expires=' + date.toGMTString();
        }
        else {
            expires = '';
        }
        document.cookie = name + '=' + value + expires + '; path=' + setPath;
    };
    this.get = function(name) {
        var nameEqual = name + '=';
        var cookieArr = document.cookie.split(';');
        for(var i = 0; i < cookieArr.length; i++) {
            var cook = cookieArr[i];
            while (cook.charAt(0) == ' ') {
                cook = cook.substring(1,cook.length);
            }
            if (cook.indexOf(nameEqual) == 0) {
                return cook.substring(nameEqual.length, cook.length);
            }
        }
        return null;
    };
    this.destroy = function(name, path) {
        this.set(name, '', -1, path);
    };
}

IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 449
E...$.@.@..x.........[...~.....6...........
........GET /scooby/js/scooby/util/log.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 1170
E...6^@.@..............[...6.~.............
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"960-1147764336000"
Last-Modified: Tue, 16 May 2006 07:25:36 GMT
Content-Type: text/javascript
Content-Length: 960
Date: Sun, 08 Oct 2006 23:33:52 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

Log = new function() {
    this.print = function(msg) {
        printfire(msg);
    }
}

// For debugging with FireBug
function printfire() {
    if (document.createEvent) {
        printfire.args = arguments;
        var ev = document.createEvent("Events");
        ev.initEvent("printfire", false, true);
        dispatchEvent(ev);
    }
}
IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 451
E...$.@.@..u.........[...~.................
........GET /scooby/js/scooby/util/popup.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 1843
E..g6_@.@../...........[.....~.j.....\.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"1631-1147848826000"
Last-Modified: Wed, 17 May 2006 06:53:46 GMT
Content-Type: text/javascript
Content-Length: 1631
Date: Sun, 08 Oct 2006 23:33:52 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var Popup = new function() {
    
    // Reference to the pop-up window
    this.win = null;
    /**
     * Opens a new pop-up window -- optional final
     * param allows turning on the scrollbars for the window
     */
    this.open = function(url, w, h, scroller) {
        var self = Popup;
        var scr = scroller ? 1 : 0;
        if(!self.win || self.win.closed) {
            self.win = null;    
            self.win = window.open(url, 'thePopupWin', 'width=' + w + ',height=' + 
                h + ',location=0,menubar=0,resizable=1,scrollbars=' + scr + 
                ',status=0,titlebar=1,toolbar=0');
        }
        else {	    
            self.win.focus(); 
            self.win.document.location = url;
        }
    };
    /**
     * Sends the opener of the window to a new location
     * and closes the pop up.     
     */
    this.goURLMainWin = function(url) {
        var self = Popup;
        location = url;
        self.win.window.close();
    };
}

Popup.constructor = null;



IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 454
E...$.@.@..q.........[...~.j...............
........GET /scooby/js/scooby/ui/global.css.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 2085
E..Y6`@.@..<...........[.....~.0.....N.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"1873-1150421392000"
Last-Modified: Fri, 16 Jun 2006 01:29:52 GMT
Content-Type: text/javascript
Content-Length: 1873
Date: Sun, 08 Oct 2006 23:33:52 GMT


var uiPrefReq = new Ajax();
var uiStyles = '';
var uiPref = []; 
var arr = [];
var repl = null;
var dynRules = [];

uiPrefReq.async = false;
uiStyles = uiPrefReq.doGet('templates/' + TEMPLATE_DIRECTORY + '/ui.css');
if (uiStyles.status != 200) {
    alert('Could not load stylesheet.');
}
else {
    doStyles(uiStyles.responseText);
}

// FiXME: Refactor with objects
function doStyles(str) {
    var uiStyles = str;

    // Remove comments
    uiStyles = uiStyles.replace(/\/\/.*/g, '');

    pat = /(\$\S+)(\s*=\s*)(\S+)(\s*)\n/g;
    while (arr = pat.exec(uiStyles)) {
        repl = new Object();
        repl.rule = arr[0];
        repl.name = arr[1];
        repl.val = arr[3];
        dynRules.push(repl);
    }
    for (var i = 0; i < dynRules.length; i++) {
        var pat = new RegExp('\\' + dynRules[i].name, 'gi');
        uiStyles = uiStyles.replace(dynRules[i].rule, '');
        uiStyles = uiStyles.replace(pat, dynRules[i].val);
    }
    
    // Replace line breaks with spaces
    uiStyles = uiStyles.replace(/\n/g, ' ');
    // Replace multiple spaces with single spaces
    uiStyles = uiStyles.replace(/\s+/g, ' ');
    // Replace multi-line comments
    uiStyles = uiStyles.replace(/\/\*.*?\*\//g, '');
    // Add line break after each style declaration
    uiStyles = uiStyles.replace(/}/g, '}\n');
    // Trim
    uiStyles = uiStyles.replace(/^\s+/g, '').replace(/\s+$/g, '');

    //Log.print(uiStyles);

    // Split into array of styles
    uiStyles = uiStyles.split(/\n/g);

    // Safari can't deal with DOM methods for styles
    if (navigator.userAgent.indexOf('Safari') > -1) {
        Styler.doOldDocumentDotWriteHack(uiStyles);
    }
    else {
        // Create stylesheet and load styles
        // ===================
        Styler.addStyle('global');
        Styler.styles['global'].loadRules(uiStyles);
    }
}


uiPrefReq = null;


IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 454
E...$.@.@..p.........[...~.0... ...........
........GET /scooby/templates/default/ui.css HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 9000
E.#\6a@.@..8...........[... .~......!Q.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"13348-1150433618000"
Last-Modified: Fri, 16 Jun 2006 04:53:38 GMT
Content-Type: text/css
Content-Length: 13348
Date: Sun, 08 Oct 2006 23:33:52 GMT

// Base font for all text
$fontFamily = verdana,arial,sans-serif
// Base color for all text
$fontColor = #000000
// Base font sizes
$fontSizeS = 9px
$fontSizeM = 11px
$fontSizeL = 12px
// Color for base areas of UI
$mainUIColor = #ffffff 
// Selected event block color
$selectedBlockColor = #005ac8
// Unselected event block color
$unselectedBlockColor = #007efe
// Processing event block color
$processingBlockColor = #9fd1fc
// Block outline color
$blockBorderColor = #ffffff
// Text color for selected blocks
$selectedTextColor = #ffffff
// Text color for unselected block
$unselectedTextColor = #ffffff
// Text color for processing text
$processingTextColor = #ffffff
// Color for stripe on left side with timeline
$sideStripeColor = #ffffff
// Main border line color
$uiDetailBorderColor = #999999
// Color for 3D highlight of borders
$uiDetailBorderHighlightColor = #999999
// Medium color for text labels
$labelTextMedColor = #333333
// Color of text and nav arrows on week-to-week nav
$navBarTextColor = #333333
// Color for link and button text
$linkTextColor = #333399
// Color for disabled link and button text
$linkTextDisabledColor = #999999
// Color for week-to-week nav bar
$calTopNavColor = #ffffff

/* Main styles, element types
 * ==================================== */
/* All elements -- don't set the default font size here
 * because it will affect vertical size of block elements */
* { 
    font-family:$fontFamily; 
    padding:0px; 
    margin:0px; 
    border:0px; 
    color:$fontColor;
}
/* Set default font size here
 * height of 100% ensures that IE can get an accurate measure
 * of the window height */
body { 
    padding:0px; 
    margin:0px; 
    border:0px; 
    height:100%; 
    font-size:$fontSizeL; 
}
/* Stupid default behavior is to act like 
 * a block-level element */
form { 
    display:inline;
}
/* Set p, td, div each separately since IE can't handle comma-
 * separated lists in with addRule for CSS */ 
p { 
    font-size:$fontSizeM;
}
td { 
    font-size:$fontSizeM;
}
div { 
    font-size:$fontSizeM;
}
/* Default link color -- 
 * generally color for text of active buttons should match */
a { 
    color:$linkTextColor;
}

/* Main styles, classes 
 * ==================================== */
/* Login 
 * --------------------- */
/* Login prompt -- default state */
.promptText { 
    color:$labelTextMedColor;
}
/* Login prompt -- error state */
.promptTextError { 
    color:#aa3333;
}

/* Buttons 
 * --------------------- */
/* Normal-sized buttons */
$buttonHeight = 24px
.buttonText { 
    font-size:$fontSizeM;
    color:$linkTextColor; 
    height:$buttonHeight; 
    line-height:$buttonHeight;
    vertical-align:middle;
}
.buttonTextDisabled { 
    font-size:$fontSizeM; 
    color:$linkTextDisabledColor; 
    height:$buttonHeight;
    line-height:$buttonHeight;
    vertical-align:middle; 
}
/* Small-sized buttons */
$smButtonHeight = 18px
.buttonTextSm { 
    font-size:$fontSizeS;
    color:$linkTextColor; 
    height:$smButtonHeight; 
    line-height:$smButtonHeight;
    vertical-align:middle; 
}
.buttonTextDisabledSm { 
    font-size:$fontSizeS;
    color:$linkTextDisabledColor; 
    height:$smButtonHeight; 
    line-height:$smButtonHeight;
    vertical-align:middle;
}

/* General widget/UI 
 * --------------------- */
/* Main areas of the UI surface */
.baseWidget {  
    background:$mainUIColor; 
    border:1px solid $uiDetailBorderColor; 
}
/* Embossed divider line */
.embossedLine { 
    height:1px; 
    font-size:0px;
    background:$uiDetailBorderColor; 
    border-bottom:1px solid #ffffff;
    margin-left:auto; 
    margin-right:auto; 
}

/* Form elements and labels 
 * --------------------- */
/* Label appearing above form inputs */
.labelTextVert { 
    color:$labelTextMedColor;
    font-weight:bold; 
    margin-bottom:2px; 
}
/* Label appearing to left of form inputs */
.labelTextHoriz { 
    color:$labelTextMedColor;
    font-weight:bold; 
}
/* For block elements containing form inputs */
.formElem { 
    margin-bottom:8px; 
}
/* Text inputs */
.inputText { 
    font-family:$fontFamily;
    font-size:$fontSizeM; 
    color:#000000;
    border:1px 
    solid #aaaaaa; 
    padding:2px; 
}
/* Disabled text inputs */
.inputTextDim { 
    font-family:$fontFamily;
    font-size:$fontSizeM;
    color:#999999;
    border:1px solid #aaaaaa; 
    padding:2px; 
}
/* Select box */
.selectElem { 
    font-family:$fontFamily;
    font-size:$fontSizeM; 
    color:#000000; 
    border:1px solid #aaaaaa; 
}

/* General layout -- floats and clear
 * --------------------- */
.floatLeft { 
    float:left; 
}
.floatRight { 
    float:right; 
}
.clearAll { 
    clear:both; 
    font-size:0px; 
}

/* Calendar-specific styles, classes 
 * ==================================== */
/* Error dialog box text */
.errText { 
    font-weight:bold; 
    margin-bottom:4px; 
}

/* Event canvases -- all-day and timed events
 * --------------------- */
/* Contain day/date at column top -- e.g., 'Sun 11' */
.dayListDayDiv { 
    position:absolute;
    border-right:1px solid #cccccc;
    border-bottom:1px solid #cccccc; 
    text-align:center; 
    -moz-user-select:none;
}
/* Day columns for resizable all-day event area */
.allDayListDayDiv { 
    height:100%;
    position:absolute; 
    border-right:1px solid #dfdfdf;
}
/* Day columns for timed event canvas */
.dayDiv { 
    position:absolute;
    border-right:1px solid #dfdfdf; 
}
/* Top half-hour of a one-hour block on the timed canvas */
.hourDivTop { 
    width:100%;
    border-bottom:1px dotted #cccccc;
    vertical-align:top; 
}
/* Bottom half-hour of a one-hour block on the timed canvas */
.hourDivBottom { 
    width:100%; 
    border-bottom:1px solid #dddddd;
    vertical-align:top;
}
/* Left-hand timeline containing times of day -- e.g., '11 AM' */
.hourDivSubLeft { 
    color:#999999; 
    font-size:9px;
    padding-top:2px; 
    padding-right:3px; 
    letter-spacing:-1px;
    text-align:right; 
    font-weight:bold;
}
/* AM/PM in lighter color after time of day */
.hourDivSubLeft span {
    color:#cccccc;
}
/* Adds colored background to current day column */
.currentDayDay {
    background:#eeeeee;
}

/* Event blocks 
 * --------------------- */
/* Normal, unselected */
.eventDiv { 
    position:absolute; 
    width:1px;
    left:0px;
    color:$unselectedTextColor;
    background:$unselectedBlockColor; 
    opacity:1.0;
    z-index:5; 
    visibility:hidden; 
    border:1px solid $blockBorderColor; 
    background-image:url(templates/default/images/block_gradient_unsel.gif); 
    background-repeat: repeat-y;
    background-position:0px 0px;
}
/* Selected block */
.eventDivSel { 
    position:absolute; 
    width:1px;
    left:0px;
    color:$selectedTextColor;
    background:$selectedBlockColor; 
    opacity:1.0;;
    z-index:5; 
    visibility:hidden; 
    border:1px solid $blockBorderColor; 
    background-image:url(templates/default/images/block_gradient_sel.gif); 
    background-repeat: repeat-y;
    background-position:0px 0px;
}
/* Processing block */
.eventDivProc { 
    position:absolute; 
    width:1px;
    left:0px; 
    color:$processingTextColor;
    background:$processingBlockColor; 
    opacity:1.0; 
    z-index:5; 
    visibility:hidden; 
    border:1px solid $blockBorderColor; 
}
/* Resize handle at the top of timed event blocks */
.eventResizeTop { 
    background:transparent;
    padding:0px; 
    margin:0px; 
    font-size:1px; 
    cursor:n-resize; 
}
/* Resize handle at the bottom of timed event blocks */
.eventResizeBottom { 
    background:transparent;
    padding:0px; 
    margin:0px; 
    font-size:1px; 
    cursor:s-resize;
}
/* Central content area bewteen resize handles --
 * this area contains two divs, one each of the
 * following two classes */
.eventContent { 
    background:transparent;
    color:inherit;
    cursor:move; 
    overflow:hidden; 
}
/* Contains the event time on the event block */
.eventTime { 
    color:inherit;
    font-size:9px;
    font-weight:bold; 
    letter-spacing:1px; 
    overflow:hidden;
}
/* Contains the event title on the event block */
.eventTitle { 
    color:inherit; 
    font-size:10px;
    overflow:hidden; 
}


/* Cal-specific, specific elements by id 
 * ==================================== */
/* Top menubar
 * --------------------- */
/* Menubar main */
#menuBarDiv { 
    position:absolute;
    background:#ffffff; 
    color:#333333;
    font-size:$fontSizeS;
    font-weight:bold; 
    line-height:68px;
    text-align:right;
    border-bottom:1px solid #dddddd; 
    vertical-align:bottom;
}
/* The pipe character between links */
#menuBarDiv span { 
    font-weight:normal; 
    color:$uiDetailBorderColor;
}
/* Scooby logo up on menubar */
#smallLogoDiv {
    position:absolute;
    top:4px;
    left:4px;
   
IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 4555
E...6b@.@..............[...H.~.............
........ width:135px;
    height:40px;
}

/* Main content area under menubar -- contains both
 * sidebars and the central column for main content
 * --------------------- */
#calDiv { 
    position:absolute;
    border-top:0px solid #dfdfdf; 
}

/* Left sidebar 
 * --------------------- */
/* Left sidebar main */
#leftSidebarDiv { 
    position:absolute;
    background:$mainUIColor; 
}
/* Calendar select box */
#calSelectNav { 
    padding-left:12px;
    position:absolute;
    top:8px;
}
/* Right sidebar
 * --------------------- */
/* Right sidebar main */
#rightSidebarDiv { 
    position:absolute;
    background:#ffffff;
}
/* Center column for main content
 * --------------------- */
/* Mask to cover center content area while loading or rendering view */
#maskDiv { 
    position:absolute;
    background:#ffffff; z-index:1000; 
    border:1px solid $uiDetailBorderColor;
    border-top:0px; 
    border-bottom:0px;
}
/* Small box that contains 'Processing ...' message inside mask */
#processingDiv { 
    position:absolute; 
    color:$linkTextColor;
    width:120px; 
    height:40px; 
    border:1px solid $linkTextColor;
    line-height:36px; 
    text-align:center; 
}
/* Nav button set for week-to-week nav */
#calTopNavDiv { 
    border-left:1px solid $uiDetailBorderColor; 
    border-right:1px solid $uiDetailBorderColor; 
    position:absolute;
    border-bottom:1px solid #dfdfdf;
    background:$calTopNavColor;
    line-height:34px;
    vertical-align:middle;
}
/* Label for month and year */
#monthHeaderDiv { 
    font-size:14px;
    font-weight:bold;
    color:#333333;
    color:$navBarTextColor;
    letter-spacing:-1px; 
}
/* List of day/dates -- e.g., 'Sun 11 | Mon 12 | Tue 12' */
#dayListDiv { 
    border-left:1px solid $uiDetailBorderColor; 
    border-right:1px solid $uiDetailBorderColor; 
    position:absolute; 
}
/* Shoves the day list over the width of the timeline sidebar */
#dayListSpacer {
    background:$sideStripeColor;    
}
/* Main container for resizable all-day event area */
#allDayResizeMainDiv { 
    border-left:1px solid $uiDetailBorderColor; 
    border-right:1px solid $uiDetailBorderColor; 
    position:absolute;
    background:#ffffff; 
    z-index:100; 
}
/* Handle to grab and drag to resize all-day event area */
#allDayResizeHandleDiv {
    border-top:1px solid $uiDetailBorderHighlightColor; 
    border-bottom:1px solid $uiDetailBorderColor; 
    border-right:1px solid $uiDetailBorderColor;
    position:absolute;
    background:$mainUIColor; 
    line-height:12px;
    font-size:1px; cursor:s-resize;
    z-index:100; 
    text-align:center;
    vertical-align:middle;
}
/* Shoves hour cols for all-day events over the width of 
 * the timeline sidebar */
#allDayHourSpacerDiv { 
    position:absolute;
    background:$sideStripeColor;
    border-right:1px solid #dfdfdf; 
}
/* Contains the cols for the all-day events */
#allDayContentDiv { 
    position:absolute;
    overflow:hidden; 
    -moz-user-select:none 
}
/* Scrolling area containing the timeline and timed events */
#timedScrollingMainDiv { 
    border-left:1px solid $uiDetailBorderColor; 
    border-right:1px solid $uiDetailBorderColor;
    position:absolute;
    overflow:auto; overflow-y:auto; overflow-x:hidden;
    -moz-user-select:none;
}
/* Timeline containing hours with AM/PM alongside timed events */
#timedHourListDiv { 
    position:absolute;
    border-right:1px solid #dfdfdf; 
    background:$sideStripeColor;
    -moz-user-select:none;
}
/* Canvas for timed events */
#timedContentDiv { 
    position:absolute;
    -moz-user-select:none 
}
/* Contains event detail form */
#eventInfoDiv { 
    position:absolute;
}

/* Fake popup for modal dialog box 
 * --------------------- */
/* Main box for fake popup */
#fauxPopDiv { 
    position:absolute; 
    width:360px;
    height:280px;
    background:$mainUIColor; 
    border:1px solid $uiDetailBorderColor;
    z-index:2000; 
    display:block; 
}
/* Fake popup center content area -- inside padding */
#fauxPopContentDiv {
    padding:24px; 
}
/* Area for text content */
#fauxPopTextDiv { 
    height:228px;
    font-size:$fontSizeL; 
}
/* Area for button panel of small buttons at the bottom */
#fauxPopButtonDiv { 
    text-align:center;
}

/* Transparent mask to cover entire UI when displaying
 * modal dialog box -- effectively disables all user input 
 * --------------------- */
#fullMaskDiv { 
    position:absolute; 
    left:0px;
    top:0px; 
    width:100%; 
    height:100%; 
    background:transparent;
    z-index:1000; 
    display:none; 
}



IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 482
E...$.@.@..R.........[...~.................
...M....GET /scooby/templates/default/images/logo_main.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 485
E.....@.@.sU.........`.......66-...........
...V...VGET /scooby/templates/default/images/button_right.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 486
E....+@.@............_....Y..'.@...........
...V...VGET /scooby/templates/default/images/button_center.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 484
E....h@.@.Qu.........^.../+1.........
.....
...V...VGET /scooby/templates/default/images/button_left.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8


IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 673
E...N.@.@..	..........._.'.@..[f...........
...l...VHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"469-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 469
Date: Sun, 08 Oct 2006 23:33:52 GMT

GIF89a...............................xxx...yyy...............................................................................................................................................................!.......,...........@.b(\..E.p.P .Ng.	."..l...b.\.`.%.................q~<...c..........................................................$#....&.....	..	%....!.. ........................*...........+..
..
.........>...B..."...B.81....4l..@#.	 7...KV+Z([E....../....;
IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 7625
E...6c@.@..............[.....~.............
...s...MHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"7419-1147143600000"
Last-Modified: Tue, 09 May 2006 03:00:00 GMT
Content-Type: image/gif
Content-Length: 7419
Date: Sun, 08 Oct 2006 23:33:52 GMT

GIF89a..;.........................p..e..w........7w..Y.#.....e..2..w.....(U.(d.......S.....#}................Fh..........$...........5........4......y.+..7e.-...u..........&[..I.5].2j....U..Hw..k.h..=..i..B.....S..H..c..D..z...u.D......D........d.Vx.s......z.Bz.^..
r.k...........9[....c..)l.L..z.....d..r........%s.V..p...j.d..El..T..l.x...d.Zv.(...........g......`.8..8q.9.....c{.H..:b.[......[.R..d..S|..`.+S.I...S....o......n..m.......1S.......(.....h..S.....j..a........}..z...S....9.....Np....q...M.[....................P..
_....q..:o.-..-a.0.....X.....e..............c......b.3x....a...b.....k.{..Pn.F...O.......7..=..&Eq.f.|..R...Z..[....z..o..j..l.....?o.....>..|.......o....................h.........p....t...W........o.T.....R.....*M.............m...........at..h.G.....!.......,......;........H......*\.......H.H."...3j...c...BZ.I2$...S.\..%..!=.:.....A.,.p2...@.
....-1.
.X..@-(A<..J....0o...'N..`.u.uB...h...H...,V....GE..L.....-.%....<....nhl(p.....#.....`..'.+aB....C..H.\......\.....O.>X.6J..+j.};#...a...Z.q..Xt....n..."..0:...m3.^.:.......`.x.....
..zA.
).$I.${..%.$P..~...4_.....~..hRE...Gq..Q..4......._.<.w......c.!(..5...M....p..D..*...T.....2....8....3.....6.`y.t..._."..b.x.-.d.e.?.s.sD.x../J._[. ..Sl2...'.R.|.,...m....b..\......'@.T.M....s....x"*..'0.#.A...yc4...
.`......M.....'4'(.....TT.A..Q..s....@.........P....g'....k. P .. dq	;.|0)Tf.7(R..SE.|.K...j"...@.jm.,[@)W.....R........bX.V./.....\.O...pG....
.#.;..~x...qtP.......\i....Je...,PJ17@..f..<C".d...c.<......7.d.Y8....8.....A,.L...b.a4.|C........*F.A.]...X.
!U..-..,q.....$...!........;..J+.d!.\s. .4.....qX.x\.l0.z.Y..#.$.ua6.>.7.....X....z....h.Vs"7Ta..!/1...@`..6'.......9..)...|..`...N*.W)...wj../D3M...PA.4...(.|#..l...9Yfz..6....n..Wp....#|.D......!...`..8@.Z.t".XM.`..x........	A....D.L.v.>$B.....?a.B..r....F..M.0.*..=^.>i.lu/.C7.LA."."........&....0....@.6..7.A*<s.....
l`..p
..@....L< .R...2......M.q.q..=...Q. )....CA.B...y...@.....
N......<.....".t..RP`.q..(C!...........
U4...T.4Z	.Z6....G.*p.......c,..6dE.........GPH.hTcij..+h..ZH.51@.>D+...&6............p.$......;...
<..I..
...Ny....l!
.D%.......?......_..A.*..LXT....F....b....D....S\....&@.P.(}ll^..J...}....YNs.../......P!Q.>......2(p..B...x..v..
......PC.U....R....*.....-..
H.
B@5.d..%...JL..! .S...Z...[..V.:
J...x..P7A.?..p8..!&..Jd".!..$6.YIX4.]..2...'h..r.......
..p.+/(....
.1>...:..M.........T..`...F....i...zp.
T@.M.v.....*..08#.n.....].Z..4PA+.....W.dxE..p.
.5....c.+..p...%/~...7......~w../.@.8.F.$ ..Z...@.2<..!H . ..!...i....p...\^#D.5.(@.>P...x.=........o..x...l.3.Y
|@.q
...&..
Xr;h..8....(...`.`....=.)bQ.#....(.6n0......h.r..<./Gy.
.. .lfj...&H..0@.-[c...G/..._...h......3X.........$.V.X@."@.i.s9..8.
z0.-KA...@.0..-.....GN.#...B.5@C ...b.c.... ....D9..h..X..
.....6..Pl_|y.%.B.r...H.a...
...i_..[.6...	..{..h.9.0.n.{.h...i.... ..&B...f...._h....o.....x@.h...........p..D....X..%.ik...`....N.nS..j....@.m.;.h..,..[../...D.k^s@.....F.pp	,.......^.ob3...(.,..s.......q.a..l..,p...-......J..T@.2P..@..'...8..
4..2........0.8.P....
;p..r..`|...V.2...........1..9 ...
...N.`p...V...A{`.A
.@.zj...xA
	......k8..?P>0...
."..S....|.S.	...._.a./....NE*Lp..+}.s....?..t ..@A...o4......37{.......0P...&.	..z...e........!.i.@r.g,........i.....vN....p..}...9.....s...W..U.......=...@	5..:...p.H..CP...}...8 .....P
......:.|{...A...&....@8"@*L...`pV...P.....0..E%..., j..|.P
8...f.s@q....P[..*P ..@	....C......@
.e.8.}}......'.`....E.0c, .!.i	`
&.yq....|:..	....y...	.z....@..P.....2M.`.....p..@.1........-..T..Gp..0..0._0.....P.M.....d.	..2#!7.2)...P..~..J........k.	(....".c..0.Q..g..u...P.l0.N .\@....1..H....*-.....-..
...p....8@.T.
t..$..0..`.cO.:&.........$......p.SF.;@.m.....(..
..R....
-@.k..\p	..*+s=. .w..m..1..)P.fI.Ry.	........60
.P....<p.CW.A.!FQ(..(T".....`.....H.-....7' .*.....M..6..N`..@..	...L1t.P...P....
..6P....m@.-..0..+.....b..*`.+..0..).
<.
xiz......-..Q...p
.p.-I.-....
....@"j.......
4......<..Q...@....)@.1@..0
....9.H..G..x..{....W...~...I.HI....P...0.e......p..p	........L..?#..C .T..u...p.M..1..0..v....4.8.>`....$@...
...*.~\.....$..Qp	l.....<.
.@.=f&..;3.w%0..... ..../	....u`.ip....up....tp....72(P.
....0..p..F~/.....).....i"....iy.u...6.K.0

..	..o...I(. 
C.....k9
....p.....
.0....c.T....@p.+......bU.`.D....	......:J...
..kZi+F!.~%..g.x.l
.	...M..S9..P..P.jj..P........t....Q@.<...p.8.........8..).....3.w8	:......... .M .....q....b...9
U.n.../I.v..E..jU*.(..0.9..<:.	./".
..............@.+...... .D..T..G.W...&.	...39.l.d
.....t........-.....O...W....Q..=...P.6...i.j..Y..0f.#p.;:.M..i.n%P.`......@..Q*.......K.M...p
...<0./..@`.}....u..
.P..Z.o..w.{.r.[..0..>......k.0... |.a&......w....
...G..^..n...`.
....
%........k..0)....O *V..........y.......iZ.....j...w.0..@.0P.....8._ 
}.....j.h&.
t......0........[.]`....#P...
]P....Q....S2........>P.@..EP.....`u;....9.0
...<.....(.E.;.%...0.e..
..`........L<.....0....<I..\....j..bI....E.........{.....Y..lz4..D..o...k...
c..E ...
j...f.
..8j.. ..0...L.B`.{l..@.G....E...e............l.> .....(...F..)e................	.i..Z.
...u.}...[.(......:.)..l..&.q...0
..p.....P....)p......x"....
<@..[..,.B`.g........t.`.<........<..|....G@.fD.'..................@.....2....$..>..... .....1..Qzu9.....dK....
.....$.....k,08* ..`....E...].....z..Z*.X..p.1...[..|..,..l.m.K^K8R...K....]........d`u...!j....x ..,..`..|
:..0l+...[........A}.].....4M.<..xMm...............:;..p	tH=...q.....2...=.t
......*v.(. .,..,.	t\....U...;.\.........*.(... ........ ..@!.x!..
..	.P.
.......0..5...?l...`.N............l.\....Q.	.7...........9+.<...;..... ....P..T..@..5......`....4..".....B0....0.	.....J]...	.[........{L.(...=%..8.......,....'XL.Z"2..^0.....[.....[.. .*.%..W......G.. ..%l.>... ..p.X0.c7.'p...._.....c^..k.4.......A.B......	...	.@....}..d0...2.".6...............m.h...@....m...@
....|.x.....z].+37.8.. .a>....x@.........JP...m.9..e..9l.. ....d...<i.]....Qs.4..`...>..........c.;"12..........{.. ..p
.~;S2AR.
......G.........k..[...B`30............sX.S..kt.....`..p.`..*..i.K.0e~$B..2c.@....W.
+....lK.`R.,..:.N....+.....K.f..4.r+...6..J...p.M....%^...W.m.v...9`......K...P.7K...R."..bN...........20.S.&..f_.b/EmG.T.........d.........p..1..-E.+.0.._.RT..o...6..,...........2."2.X...`AJ8..~4.o.O...5pb...*...b..8............r(..u.oz..
.%.O..?`"......w*.......!..&~......O*..'s.-./..?].58....N..@ @...n.YP@....j.r.A..T*T.j.A@.('...&L@...F.:......".qp..>....2...O.?.".p+.C...J\........%P....J#.Z &...C......	(
.~......A.ZE.pi..a...u..	.'..#...].^o-Y..F4w..0h.....$.l.9.T;...@......U..
.8..%.
..KUu.....3Q.O....m[X..5l.Q..lg ......~.;*m..{.#h....E....;.........A.6kbl...>.#<v..!..9.N.0;........N...Dp.v.1a9..L.A........E(.1.NC.J0`..R.....b"..F1..:.P...Z.f..x....L...%z.....L...>................`*1:.F..><.:..4A.sR<G..X.F..R1.......-..R..Th...........	6...%E..q....:..n.Q..uhl..3n...5.`E..Z)....T.@%."..(...C..h.N.L.../.je.]O.S..v.a*...P...{.'b..u(.
.a..~"..e
...d.....M.. .....K.1$.:jlb.&Xi...r..H#W.w.W	..V9...U.....Y....*..0.(R...'}...V...Y....a.i...VL....;..C............%.=i....w......P..S..	...u:..:$.[X
8..'.Y`....6 ........d..Y...N.;...f.....3&......l..R..o..........l`o..cac.vX....H{...:..kn....&.L.....6...6 .......[.....6X..B.=..?....7......}v.j....,8...Xx .=...z=.P`...0...F..s......n....#.i..U..H.i..of....9
.Y....}j....g....D...O...e...r!...$..|..%.U	.q.1......C.RE....'73
.4.2.u.a.......v....d[.v5....Ys..
..7..P.~...v...l,#&..F:..(..4MC....:.|.u@..G<..[|1d.....7$.D..O<..Cb.2
..>!......}..}.U ...=~1. . Z....|EA].d%-yIL^.,.X.xtS..q1..$e)M...=(C.:e+]..W*I...e-myKJ......;
IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 811
E.._u.@.@..............^...../-......T.....
...t...VHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"607-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 607
Date: Sun, 08 Oct 2006 23:33:52 GMT

GIF89a
....A.xxx...........................yyy........................................................................................................................}}~....................................................................................................................................................................................................................................!.....A.,....
.......AA=.&1.>..A.!3..
../9(....-....+%2...4........
...............
.............
...........		.........	....
....
7.....?"
.
.......<P..o...5 @..A....l.0..T...n.......#...6....*.=..D....$A....;
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 831
E..s.|@.@.E............`.66-.........h.....
.......VHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"627-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 627
Date: Sun, 08 Oct 2006 23:33:52 GMT

GIF89a
.........xxx............yyy.............................................................................................................................................}}~...........................................................................................................................................................................................................................!.......,....
........H...=" .
.....6,?
....>.@......R;..5.OWB.%......F......#....T+K......LJD....
......
.........8 ....$	..	.)2....I....9
.4....YEG*...!@...:lP.D...!LT...G....h...
.Z....`......p......-.....I.i.<Lc0.

F..A.baB .;
IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 582
E..z.-@.@..N........._....[f.'.......o.....
.......lPOST /scooby/j_acegi_security_check HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 35
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew
Pragma: no-cache
Cache-Control: no-cache


IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 35
E..W..@.@..p........._....]..'.......K.....
...E...Ej_username=andrew&j_password=werdna
IP 127.0.0.1.58204 > 127.0.0.1.8080: tcp 148
E.....@.@............\...~^]...............
...S....HEAD /cosmo/home/andrew/ HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080


IP 127.0.0.1.8080 > 127.0.0.1.58204: tcp 135
E.....@.@.!i...........\.....~^............
.......SHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Transfer-Encoding: chunked
Date: Sun, 08 Oct 2006 23:34:03 GMT


IP 127.0.0.1.58204 > 127.0.0.1.8080: tcp 221
E.....@.@..I.........\...~^................
........PROPFIND /cosmo/home/andrew HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
Content-Type: text/xml; charset=utf-8
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080
Content-Length: 156
Depth: 0


IP 127.0.0.1.58204 > 127.0.0.1.8080: tcp 156
E.....@.@............\...~_................
........<?xml version="1.0" encoding="utf-8" ?><D:propfind xmlns:D="DAV:"><D:prop><ZZ:scoobyprefs xmlns:ZZ="http://osafoundation.org/scooby"/></D:prop></D:propfind>
IP 127.0.0.1.8080 > 127.0.0.1.58204: tcp 697
E.....@.@..4...........\.....~`j...3.......
........HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 521
Date: Sun, 08 Oct 2006 23:34:03 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/</D:href>
        <D:propstat>
            <D:prop/>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
        <D:propstat>
            <D:prop>
                <ZZ:scoobyprefs xmlns:ZZ="http://osafoundation.org/scooby"/>
            </D:prop>
            <D:status>HTTP/1.1 404 Not Found</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 167
E...N.@.@.............._.'....]....#.......
.......EHTTP/1.1 302 Moved Temporarily
Server: Apache-Coyote/1.1
Location: http://localhost:8080/scooby/main.page
Content-Length: 0
Date: Sun, 08 Oct 2006 23:34:03 GMT


IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 499
E..'$.@.@..?.........[...~.................
.......sGET /scooby/main.page HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew
Pragma: no-cache
Cache-Control: no-cache


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 5415
E..[6e@.@..5...........[.....~.......P.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=ISO-8859-1
Content-Language: en-NZ
Content-Length: 5241
Date: Sun, 08 Oct 2006 23:34:03 GMT






<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<title>Welcome to Scooby</title>

<script type="text/javascript">
    var djConfig = {isDebug: true};
</script>

<script type="text/javascript" src="js/lib/dojo-event_and_io/dojo.js.uncompressed.js"></script>
<script type="text/javascript">

    dojo.setModulePrefix("scooby", "../../scooby"); // path is relative to dojo root

    dojo.require("dojo.widget.*");
    dojo.require("scooby.ui.widget.Debug");
    dojo.widget.manager.registerWidgetPackage("scooby.ui.widget");
    
 
    
</script>



<script type="text/javascript" src="i18n.js"></script>
<script type="text/javascript" src="js/scooby/util/log.js"></script>
<script type="text/javascript" src="js/scooby/util/ajax.js"></script>
<script type="text/javascript" src="js/scooby/util/date.js"></script>
<script type="text/javascript" src="js/scooby/util/hash.js"></script>
<script type="text/javascript" src="js/scooby/util/debug.js"></script>
<script type="text/javascript" src="js/scooby/util/validate.js"></script>
<script type="text/javascript" src="js/scooby/util/cookie.js"></script>
<script type="text/javascript" src="js/scooby/util/text.js"></script>
<script type="text/javascript" src="js/scooby/util/popup.js"></script>
<script type="text/javascript" src="js/scooby/model/scoobydate.js"></script>
<script type="text/javascript" src="js/scooby/model/model.js"></script>
<script type="text/javascript" src="js/scooby/ui/ui.conf.js"></script>
<script type="text/javascript" src="js/scooby/ui/block.js"></script>
<script type="text/javascript" src="js/scooby/ui/dialog.js"></script>
<script type="text/javascript" src="js/scooby/ui/cal_main.js"></script>
<script type="text/javascript" src="js/scooby/ui/draggable.js"></script>
<script type="text/javascript" src="js/scooby/ui/resize_area.js"></script>
<script type="text/javascript" src="js/scooby/ui/cal_form.js"></script>
<script type="text/javascript" src="js/scooby/ui/event/handlers.js"></script>
<script type="text/javascript" src="js/scooby/ui/styler.js"></script>
<script type="text/javascript" src="js/scooby/ui/contentcontainer.js"></script>
<script type="text/javascript" src="js/scooby/ui/button.js"></script>
<script type="text/javascript" src="js/scooby/ui/widget/Layout.js"></script>
<script type="text/javascript" src="js/scooby/facade/pref.js"></script>
<script type="text/javascript" src="js/scooby/service/service_stub.js"></script>
<script type="text/javascript" src="js/scooby/service/json_service_impl.js"></script>
<script type="text/javascript" src="js/scooby/legacy/cal_event.js"></script>
<script type="text/javascript" src="js/scooby/legacy/async.js"></script>
<script type="text/javascript" src="js/lib/jsonrpc-java-js/jsonrpc.js"></script>

<script type="text/javascript">
// FIXME: Need to get timeout value from server
var TIMEOUT_MIN = 30;

function init() {
    Cal.init();
}
</script>

<script type="text/javascript" src="js/scooby/ui/event/listeners.js"></script>
<script type="text/javascript" src="js/scooby/ui/global.css.js"></script>


</head>

<body id="body">
        <div id="menuBarDiv">
            <div id="smallLogoDiv"></div>
            <span class="menuBarLink">
                Welcome, andrew
            </span>
            <span class="menuBarDivider">|</span>
            <span class="menuBarLink"><a href="javascript:Popup.open('about.html', 380, 280);">
              About Scooby 
            </a></span>
            <span class="menuBarDivider">|</span>
            <span class="menuBarLink"><a href="redirect_login.html"> 
               Log Out
            </a></span>&nbsp;&nbsp;
        </div>
        <div id="calDiv">
            <form method="post" id="calForm" name="calForm" action="">
                <div id="leftSidebarDiv">
                    <div id="calSelectNav"></div>
                </div>
                <div id="calTopNavDiv">
                    <table cellpadding="0" cellspacing="0">
                        <tr>
                            <td>&nbsp;&nbsp;&nbsp;</td>
                            <td id="viewNavButtons"></td>
                            <td>&nbsp;&nbsp;&nbsp;</td>
                            <td id="monthHeaderDiv"></td>
                        </tr>
                    </table>
                </div>
                <div id="dayListDiv"></div>
                <div id="allDayResizeMainDiv">
                    <div id="allDayHourSpacerDiv"></div>
                    <div id="allDayContentDiv"></div>
                </div>
                <div id="allDayResizeHandleDiv"></div>
                <div id="timedScrollingMainDiv">
                    <div id="timedHourListDiv"></div>
                    <div id="timedContentDiv"></div>
                </div>
                <div id="rightSidebarDiv">
                    <div id="eventInfoDiv"></div>
                </div>
            </form>
        </div>
        <div id="maskDiv">
          <div id="processingDiv">
              Processing ...
          </div>
        </div>
        <div id="fullMaskDiv"></div>
        <div id="dojoDebug" dojoType="debug"><div>
</body>

</html>


IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 550
E..Z.j@.@.Q1.........^.../-..........O.....
.......tGET /scooby/main.page HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/login.page;jsessionid=E472942E4056EA912A7504A13680E6E8
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 5415
E..[u.@.@..............^.....//;...".P.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=ISO-8859-1
Content-Language: en-NZ
Content-Length: 5241
Date: Sun, 08 Oct 2006 23:34:03 GMT






<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<title>Welcome to Scooby</title>

<script type="text/javascript">
    var djConfig = {isDebug: true};
</script>

<script type="text/javascript" src="js/lib/dojo-event_and_io/dojo.js.uncompressed.js"></script>
<script type="text/javascript">

    dojo.setModulePrefix("scooby", "../../scooby"); // path is relative to dojo root

    dojo.require("dojo.widget.*");
    dojo.require("scooby.ui.widget.Debug");
    dojo.widget.manager.registerWidgetPackage("scooby.ui.widget");
    
 
    
</script>



<script type="text/javascript" src="i18n.js"></script>
<script type="text/javascript" src="js/scooby/util/log.js"></script>
<script type="text/javascript" src="js/scooby/util/ajax.js"></script>
<script type="text/javascript" src="js/scooby/util/date.js"></script>
<script type="text/javascript" src="js/scooby/util/hash.js"></script>
<script type="text/javascript" src="js/scooby/util/debug.js"></script>
<script type="text/javascript" src="js/scooby/util/validate.js"></script>
<script type="text/javascript" src="js/scooby/util/cookie.js"></script>
<script type="text/javascript" src="js/scooby/util/text.js"></script>
<script type="text/javascript" src="js/scooby/util/popup.js"></script>
<script type="text/javascript" src="js/scooby/model/scoobydate.js"></script>
<script type="text/javascript" src="js/scooby/model/model.js"></script>
<script type="text/javascript" src="js/scooby/ui/ui.conf.js"></script>
<script type="text/javascript" src="js/scooby/ui/block.js"></script>
<script type="text/javascript" src="js/scooby/ui/dialog.js"></script>
<script type="text/javascript" src="js/scooby/ui/cal_main.js"></script>
<script type="text/javascript" src="js/scooby/ui/draggable.js"></script>
<script type="text/javascript" src="js/scooby/ui/resize_area.js"></script>
<script type="text/javascript" src="js/scooby/ui/cal_form.js"></script>
<script type="text/javascript" src="js/scooby/ui/event/handlers.js"></script>
<script type="text/javascript" src="js/scooby/ui/styler.js"></script>
<script type="text/javascript" src="js/scooby/ui/contentcontainer.js"></script>
<script type="text/javascript" src="js/scooby/ui/button.js"></script>
<script type="text/javascript" src="js/scooby/ui/widget/Layout.js"></script>
<script type="text/javascript" src="js/scooby/facade/pref.js"></script>
<script type="text/javascript" src="js/scooby/service/service_stub.js"></script>
<script type="text/javascript" src="js/scooby/service/json_service_impl.js"></script>
<script type="text/javascript" src="js/scooby/legacy/cal_event.js"></script>
<script type="text/javascript" src="js/scooby/legacy/async.js"></script>
<script type="text/javascript" src="js/lib/jsonrpc-java-js/jsonrpc.js"></script>

<script type="text/javascript">
// FIXME: Need to get timeout value from server
var TIMEOUT_MIN = 30;

function init() {
    Cal.init();
}
</script>

<script type="text/javascript" src="js/scooby/ui/event/listeners.js"></script>
<script type="text/javascript" src="js/scooby/ui/global.css.js"></script>


</head>

<body id="body">
        <div id="menuBarDiv">
            <div id="smallLogoDiv"></div>
            <span class="menuBarLink">
                Welcome, andrew
            </span>
            <span class="menuBarDivider">|</span>
            <span class="menuBarLink"><a href="javascript:Popup.open('about.html', 380, 280);">
              About Scooby 
            </a></span>
            <span class="menuBarDivider">|</span>
            <span class="menuBarLink"><a href="redirect_login.html"> 
               Log Out
            </a></span>&nbsp;&nbsp;
        </div>
        <div id="calDiv">
            <form method="post" id="calForm" name="calForm" action="">
                <div id="leftSidebarDiv">
                    <div id="calSelectNav"></div>
                </div>
                <div id="calTopNavDiv">
                    <table cellpadding="0" cellspacing="0">
                        <tr>
                            <td>&nbsp;&nbsp;&nbsp;</td>
                            <td id="viewNavButtons"></td>
                            <td>&nbsp;&nbsp;&nbsp;</td>
                            <td id="monthHeaderDiv"></td>
                        </tr>
                    </table>
                </div>
                <div id="dayListDiv"></div>
                <div id="allDayResizeMainDiv">
                    <div id="allDayHourSpacerDiv"></div>
                    <div id="allDayContentDiv"></div>
                </div>
                <div id="allDayResizeHandleDiv"></div>
                <div id="timedScrollingMainDiv">
                    <div id="timedHourListDiv"></div>
                    <div id="timedContentDiv"></div>
                </div>
                <div id="rightSidebarDiv">
                    <div id="eventInfoDiv"></div>
                </div>
            </form>
        </div>
        <div id="maskDiv">
          <div id="processingDiv">
              Processing ...
          </div>
        </div>
        <div id="fullMaskDiv"></div>
        <div id="dojoDebug" dojoType="debug"><div>
</body>

</html>


IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 448
E.....@.@.sx.........`.......69l...........
........GET /scooby/js/lib/dojo-event_and_io/dojo.js.uncompressed.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\.~@.@.%............`.69l..	y..."!Q.....
...b....HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"128244-1147389466000"
Last-Modified: Thu, 11 May 2006 23:17:46 GMT
Content-Type: text/javascript
Content-Length: 128244
Date: Sun, 08 Oct 2006 23:34:03 GMT

/**
* @file bootstrap1.js
*
* bootstrap file that runs before hostenv_*.js file.
*
* @author Copyright 2004 Mark D. Anderson (mda@discerning.com)
* @author Licensed under the Academic Free License 2.1 http://www.opensource.org/licenses/afl-2.1.php
*
* $Id: bootstrap1.js 3802 2006-05-07 22:33:47Z sjmiles $
*/

/**
 * The global djConfig can be set prior to loading the library, to override
 * certain settings.  It does not exist under dojo.* so that it can be set
 * before the dojo variable exists. Setting any of these variables *after* the
 * library has loaded does nothing at all. The variables that can be set are
 * as follows:
 */

/**
 * dj_global is an alias for the top-level global object in the host
 * environment (the "window" object in a browser).
 */
var dj_global = this; //typeof window == 'undefined' ? this : window;

/**
 *  True if name is defined, either on obj or globally if obj is false
 *  Note that 'defined' and 'exists' are not the same concept. 
 */
function dj_undef(name, obj){
	if(!obj){ obj = dj_global; }
	// exception if obj is not an Object
	return (typeof obj[name] == "undefined");
}

/**
 * djConfig is the global configuration object 
 */
if(dj_undef("djConfig")){ 
	var djConfig = {}; 
}

/**
 * dojo is the root variable of (almost all) our public symbols.
 */
if(dj_undef("dojo")){ 
	var dojo = {}; 
}

dojo.version = {
	major: 0, minor: 2, patch: 2, flag: "+",
	revision: Number("$Rev: 3802 $".match(/[0-9]+/)[0]),
	toString: function(){
		with(dojo.version){
			return major + "." + minor + "." + patch + flag + " (" + revision + ")";
		}
	}
}

/**
 * get 'obj[name]' if it is defined, otherwise create as Object if 'create' is true, otherwise return null
 * caveat: 'defined' and 'exists' are not the same concept
 */
dojo.evalProp = function(name, obj, create){
	return (obj && !dj_undef(name, obj) ? obj[name] : (create ? (obj[name]={}) : undefined));
}

/**
 * Parse a reference specified as a string descriptor into an object reference and a property name.
 */
dojo.parseObjPath = function(objpath, context, create){
	var obj = (context ? context : dj_global);
	var names = objpath.split('.');
	var prop = names.pop();
	for (var i=0,l=names.length;i<l && obj;i++){
		obj = dojo.evalProp(names[i], obj, create);
	}
	return {obj: obj, prop: prop};
}

/*
 * evaluate a string like "A.B" without using eval.
 */
dojo.evalObjPath = function(objpath, create){
	if(typeof objpath != "string"){ 
		return dj_global; 
	}
	// fast path for no periods
	if(objpath.indexOf('.') == -1){
		return dojo.evalProp(objpath, dj_global, create);
	}
	with (dojo.parseObjPath(objpath, dj_global, create)){
		return dojo.evalProp(prop, obj, create);
	}	
}

// ****************************************************************
// global public utils
// ****************************************************************

/*
 * utility to print an Error. 
 * TODO: overriding Error.prototype.toString won't accomplish this?
 * ... since natively generated Error objects do not always reflect such things?
 */
dojo.errorToString = function(excep){
	return ((!dj_undef("message", excep)) ? excep.message : (dj_undef("description", excep) ? excep : excep.description ));
}

/**
* Throws an Error object given the string err. For now, will also do a println
* to the user first.
*/
dojo.raise = function(message, excep){
	if(excep){
		message = message + ": "+dojo.errorToString(excep);
	}
	var he = dojo.hostenv;
	if((!dj_undef("hostenv", dojo))&&(!dj_undef("println", dojo.hostenv))){ 
		dojo.hostenv.println("FATAL: " + message);
	}
	throw Error(message);
}

//Stub functions so things don't break.
dojo.debug = function(){}
dojo.debugShallow = function(obj){}
dojo.profile = { start: function(){}, end: function(){}, stop: function(){}, dump: function(){} };

/**
 * We put eval() in this separate function to keep down the size of the trapped
 * evaluation context.
 *
 * Note that:
 * - JSC eval() takes an optional second argument which can be 'unsafe'.
 * - Mozilla/SpiderMonkey eval() takes an optional second argument which is the
 *   scope object for new symbols.
*/
function dj_eval(s){ return dj_global.eval ? dj_global.eval(s) : eval(s); }


/**
 * Convenience for throwing an exception because some function is not
 * implemented.
 */
dojo.unimplemented = function(funcname, extra){
	// FIXME: need to move this away from dj_*
	var mess = "'" + funcname + "' not implemented";
	if((!dj_undef(extra))&&(extra)){ mess += " " + extra; }
	dojo.raise(mess);
}

/**
 * Convenience for informing of deprecated behaviour.
 */
dojo.deprecated = function(behaviour, extra, removal){
	var mess = "DEPRECATED: " + behaviour;
	if(extra){ mess += " " + extra; }
	if(removal){ mess += " -- will be removed in version: " + removal; }
	dojo.debug(mess);
}

/**
 * Does inheritance
 */
dojo.inherits = function(subclass, superclass){
	if(typeof superclass != 'function'){ 
		dojo.raise("dojo.inherits: superclass argument ["+superclass+"] must be a function (subclass: [" + subclass + "']");
	}
	subclass.prototype = new superclass();
	subclass.prototype.constructor = subclass;
	subclass.superclass = superclass.prototype;
	// DEPRICATED: super is a reserved word, use 'superclass'
	subclass['super'] = superclass.prototype;
}

// an object that authors use determine what host we are running under
dojo.render = (function(){

	function vscaffold(prefs, names){
		var tmp = {
			capable: false,
			support: {
				builtin: false,
				plugin: false
			},
			prefixes: prefs
		};
		for(var x in names){
			tmp[x] = false;
		}
		return tmp;
	}

	return {
		name: "",
		ver: dojo.version,
		os: { win: false, linux: false, osx: false },
		html: vscaffold(["html"], ["ie", "opera", "khtml", "safari", "moz"]),
		svg: vscaffold(["svg"], ["corel", "adobe", "batik"]),
		vml: vscaffold(["vml"], ["ie"]),
		swf: vscaffold(["Swf", "Flash", "Mm"], ["mm"]),
		swt: vscaffold(["Swt"], ["ibm"])
	};
})();

// ****************************************************************
// dojo.hostenv methods that must be defined in hostenv_*.js
// ****************************************************************

/**
 * The interface definining the interaction with the EcmaScript host environment.
*/

/*
 * None of these methods should ever be called directly by library users.
 * Instead public methods such as loadModule should be called instead.
 */
dojo.hostenv = (function(){

	// default configuration options
	var config = {
		isDebug: false,
		allowQueryConfig: false,
		baseScriptUri: "",
		baseRelativePath: "",
		libraryScriptUri: "",
		iePreventClobber: false,
		ieClobberMinimal: true,
		preventBackButtonFix: true,
		searchIds: [],
		parseWidgets: true
	};

	if (typeof djConfig == "undefined") { djConfig = config; }
	else {
		for (var option in config) {
			if (typeof djConfig[option] == "undefined") {
				djConfig[option] = config[option];
			}
		}
	}

	return {
		name_: '(unset)',
		version_: '(unset)',

		/**
		 * Return the name of the hostenv.
		 */
		getName: function(){ return this.name_; },

		/**
		* Return the version of the hostenv.
		*/
		getVersion: function(){ return this.version_; },

		/**
		 * Read the plain/text contents at the specified uri.  If getText() is
		 * not implemented, then it is necessary to override loadUri() with an
		 * implementation that doesn't rely on it.
		 */
		getText: function(uri){
			dojo.unimplemented('getText', "uri=" + uri);
		}
	};
})();

/**
 * Return the base script uri that other scripts are found relative to.
 * It is either the empty string, or a non-empty string ending in '/'.
 */
dojo.hostenv.getBaseScriptUri = function(){
	if(djConfig.baseScriptUri.length){ 
		return djConfig.baseScriptUri;
	}
	var uri = new String(djConfig.libraryScriptUri||djConfig.baseRelativePath);
	if (!uri) { dojo.raise("Nothing returned by getLibraryScriptUri(): " + uri); }

	var lastslash = uri.lastIndexOf('/');
	djConfig.baseScriptUri = djConfig.baseRelativePath;
	return djConfig.baseScriptUri;
}

/*
 * loader.js - runs before the hostenv_*.js file. Contains all of the package loading methods.
 */

//A semi-colon is at the start of the line because after doing a build, this function definition
//get compressed onto the same line as the last line in bootstrap1.js. That list line is just a
//curly bracket, and the browser complains about that syntax. The semicolon fixes it. Putting it
//here instead of at the end of bootstrap1.js, since it is more of an issue for this file, (using
//the closure), and bootstrap1.js could change in the future.
;(function(){
	//Additional properties for dojo.hostenv
	var _addHostEnv = {
		pkgFileName: "__package__",
	
		// for recursion protection
		loading_modules_: {},
		loaded_modules_: {},
		addedToLoadingCount: [],
		removedFromLoadingCount: [],
	
		inFlightCount:
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.6\...	y..."!Q.....
.......b 0,
	
		// FIXME: it should be possible to pull module prefixes in from djConfig
		modulePrefixes_: {
			dojo: {name: "dojo", value: "src"}
		},
	
	
		setModulePrefix: function(module, prefix){
			this.modulePrefixes_[module] = {name: module, value: prefix};
		},
	
		getModulePrefix: function(module){
			var mp = this.modulePrefixes_;
			if((mp[module])&&(mp[module]["name"])){
				return mp[module].value;
			}
			return module;
		},
	
		getTextStack: [],
		loadUriStack: [],
		loadedUris: [],
	
		//WARNING: This variable is referenced by packages outside of bootstrap: FloatingPane.js and undo/browser.js
		post_load_: false,
		
		//Egad! Lots of test files push on this directly instead of using dojo.addOnLoad.
		modulesLoadedListeners: []
	};
	
	//Add all of these properties to dojo.hostenv
	for(var param in _addHostEnv){
		dojo.hostenv[param] = _addHostEnv[param];
	}
})();

/**
 * Loads and interprets the script located at relpath, which is relative to the
 * script root directory.  If the script is found but its interpretation causes
 * a runtime exception, that exception is not caught by us, so the caller will
 * see it.  We return a true value if and only if the script is found.
 *
 * For now, we do not have an implementation of a true search path.  We
 * consider only the single base script uri, as returned by getBaseScriptUri().
 *
 * @param relpath A relative path to a script (no leading '/', and typically
 * ending in '.js').
 * @param module A module whose existance to check for after loading a path.
 * Can be used to determine success or failure of the load.
 */
dojo.hostenv.loadPath = function(relpath, module /*optional*/, cb /*optional*/){
	if((relpath.charAt(0) == '/')||(relpath.match(/^\w+:/))){
		dojo.raise("relpath '" + relpath + "'; must be relative");
	}
	var uri = this.getBaseScriptUri() + relpath;
	if(djConfig.cacheBust && dojo.render.html.capable) { uri += "?" + String(djConfig.cacheBust).replace(/\W+/g,""); }
	try{
		return ((!module) ? this.loadUri(uri, cb) : this.loadUriAndCheck(uri, module, cb));
	}catch(e){
		dojo.debug(e);
		return false;
	}
}

/**
 * Reads the contents of the URI, and evaluates the contents.
 * Returns true if it succeeded. Returns false if the URI reading failed.
 * Throws if the evaluation throws.
 * The result of the eval is not available to the caller.
 */
dojo.hostenv.loadUri = function(uri, cb){
	if(this.loadedUris[uri]){
		return;
	}
	var contents = this.getText(uri, null, true);
	if(contents == null){ return 0; }
	this.loadedUris[uri] = true;
	var value = dj_eval(contents);
	return 1;
}

// FIXME: probably need to add logging to this method
dojo.hostenv.loadUriAndCheck = function(uri, module, cb){
	var ok = true;
	try{
		ok = this.loadUri(uri, cb);
	}catch(e){
		dojo.debug("failed loading ", uri, " with error: ", e);
	}
	return ((ok)&&(this.findModule(module, false))) ? true : false;
}

dojo.loaded = function(){ }

dojo.hostenv.loaded = function(){
	this.post_load_ = true;
	var mll = this.modulesLoadedListeners;
	//Clear listeners so new ones can be added
	//For other xdomain package loads after the initial load.
	this.modulesLoadedListeners = [];
	for(var x=0; x<mll.length; x++){
		mll[x]();
	}
	dojo.loaded();
}

/*
Call styles:
	dojo.addOnLoad(functionPointer)
	dojo.addOnLoad(object, "functionName")
*/
dojo.addOnLoad = function(obj, fcnName) {
	var dh = dojo.hostenv;
	if(arguments.length == 1) {
		dh.modulesLoadedListeners.push(obj);
	} else if(arguments.length > 1) {
		dh.modulesLoadedListeners.push(function() {
			obj[fcnName]();
		});
	}

	//Added for xdomain loading. dojo.addOnLoad is used to
	//indicate callbacks after doing some dojo.require() statements.
	//In the xdomain case, if all the requires are loaded (after initial
	//page load), then immediately call any listeners.
	if(dh.post_load_ && dh.inFlightCount == 0){
		dh.callLoaded();
	}
}

dojo.hostenv.modulesLoaded = function(){
	if(this.post_load_){ return; }
	if((this.loadUriStack.length==0)&&(this.getTextStack.length==0)){
		if(this.inFlightCount > 0){ 
			dojo.debug("files still in flight!");
			return;
		}
		dojo.hostenv.callLoaded();
	}
}

dojo.hostenv.callLoaded = function(){
	if(typeof setTimeout == "object"){
		setTimeout("dojo.hostenv.loaded();", 0);
	}else{
		dojo.hostenv.loaded();
	}
}

/**
* loadModule("A.B") first checks to see if symbol A.B is defined. 
* If it is, it is simply returned (nothing to do).
*
* If it is not defined, it will look for "A/B.js" in the script root directory,
* followed by "A.js".
*
* It throws if it cannot find a file to load, or if the symbol A.B is not
* defined after loading.
*
* It returns the object A.B.
*
* This does nothing about importing symbols into the current package.
* It is presumed that the caller will take care of that. For example, to import
* all symbols:
*
*    with (dojo.hostenv.loadModule("A.B")) {
*       ...
*    }
*
* And to import just the leaf symbol:
*
*    var B = dojo.hostenv.loadModule("A.B");
*    ...
*
* dj_load is an alias for dojo.hostenv.loadModule
*/
dojo.hostenv._global_omit_module_check = false;
dojo.hostenv.loadModule = function(modulename, exact_only, omit_module_check){
	if(!modulename){ return; }
	omit_module_check = this._global_omit_module_check || omit_module_check;
	var module = this.findModule(modulename, false);
	if(module){
		return module;
	}

	// protect against infinite recursion from mutual dependencies
	if(dj_undef(modulename, this.loading_modules_)){
		this.addedToLoadingCount.push(modulename);
	}
	this.loading_modules_[modulename] = 1;

	// convert periods to slashes
	var relpath = modulename.replace(/\./g, '/') + '.js';

	var syms = modulename.split(".");
	var nsyms = modulename.split(".");
	for (var i = syms.length - 1; i > 0; i--) {
		var parentModule = syms.slice(0, i).join(".");
		var parentModulePath = this.getModulePrefix(parentModule);
		if (parentModulePath != parentModule) {
			syms.splice(0, i, parentModulePath);
			break;
		}
	}
	var last = syms[syms.length - 1];
	// figure out if we're looking for a full package, if so, we want to do
	// things slightly diffrently
	if(last=="*"){
		modulename = (nsyms.slice(0, -1)).join('.');

		while(syms.length){
			syms.pop();
			syms.push(this.pkgFileName);
			relpath = syms.join("/") + '.js';
			if(relpath.charAt(0)=="/"){
				relpath = relpath.slice(1);
			}
			ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null));
			if(ok){ break; }
			syms.pop();
		}
	}else{
		relpath = syms.join("/") + '.js';
		modulename = nsyms.join('.');
		var ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null));
		if((!ok)&&(!exact_only)){
			syms.pop();
			while(syms.length){
				relpath = syms.join('/') + '.js';
				ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null));
				if(ok){ break; }
				syms.pop();
				relpath = syms.join('/') + '/'+this.pkgFileName+'.js';
				if(relpath.charAt(0)=="/"){
					relpath = relpath.slice(1);
				}
				ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null));
				if(ok){ break; }
			}
		}

		if((!ok)&&(!omit_module_check)){
			dojo.raise("Could not load '" + modulename + "'; last tried '" + relpath + "'");
		}
	}

	// check that the symbol was defined
	//Don't bother if we're doing xdomain (asynchronous) loading.
	if(!omit_module_check && !this["isXDomain"]){
		// pass in false so we can give better error
		module = this.findModule(modulename, false);
		if(!module){
			dojo.raise("symbol '" + modulename + "' is not defined after loading '" + relpath + "'"); 
		}
	}

	return module;
}

/**
* startPackage("A.B") follows the path, and at each level creates a new empty
* object or uses what already exists. It returns the result.
*/
dojo.hostenv.startPackage = function(packname){
	var modref = dojo.evalObjPath((packname.split(".").slice(0, -1)).join('.'));
	this.loaded_modules_[(new String(packname)).toLowerCase()] = modref;

	var syms = packname.split(/\./);
	if(syms[syms.length-1]=="*"){
		syms.pop();
	}
	return dojo.evalObjPath(syms.join("."), true);
}

/**
 * findModule("A.B") returns the object A.B if it exists, otherwise null.
 * @param modulename A string like 'A.B'.
 * @param must_exist Optional, defualt false. throw instead of returning null
 * if the module does not currently exist.
 */
dojo.hostenv.findModule = function(modulename, must_exist){
	// check cache
	/*
	if(!dj_undef(modulename, this.modules_)){
		return this.modules_[modulename];
	}
	*/

	var lmn = (new String(modulename)).toLowerCase();

	if(this.loaded_modules_[lmn]){
		return this.loaded_modules_[lmn];
	}

	// see if symbol is defined anyway
	var module = dojo.evalObjPath(modulename);
	if((modulename)&&(typeof module != 'undefined')&&(module)){
		this.loaded_modules_[lmn] = module;
		return module;
	}

	if(must_exist){
		dojo.raise("no loaded module named '" + modulename + "'");
	}
	return null;
}

//Start of old bootstrap2:

/*
 * This method taks a "map" of arrays which one can use to optionally load dojo
 * modules
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.6....	y..."!Q.....
......... The map is indexed by the possible dojo.hostenv.name_ values, with
 * two additional values: "default" and "common". The items in the "default"
 * array will be loaded if none of the other items have been choosen based on
 * the hostenv.name_ item. The items in the "common" array will _always_ be
 * loaded, regardless of which list is chosen.  Here's how it's normally
 * called:
 *
 *	dojo.kwCompoundRequire({
 *		browser: [
 *			["foo.bar.baz", true, true], // an example that passes multiple args to loadModule()
 *			"foo.sample.*",
 *			"foo.test,
 *		],
 *		default: [ "foo.sample.*" ],
 *		common: [ "really.important.module.*" ]
 *	});
 */
dojo.kwCompoundRequire = function(modMap){
	var common = modMap["common"]||[];
	var result = (modMap[dojo.hostenv.name_]) ? common.concat(modMap[dojo.hostenv.name_]||[]) : common.concat(modMap["default"]||[]);

	for(var x=0; x<result.length; x++){
		var curr = result[x];
		if(curr.constructor == Array){
			dojo.hostenv.loadModule.apply(dojo.hostenv, curr);
		}else{
			dojo.hostenv.loadModule(curr);
		}
	}
}

dojo.require = function(){
	dojo.hostenv.loadModule.apply(dojo.hostenv, arguments);
}

dojo.requireIf = function(){
	if((arguments[0] === true)||(arguments[0]=="common")||(arguments[0] && dojo.render[arguments[0]].capable)){
		var args = [];
		for (var i = 1; i < arguments.length; i++) { args.push(arguments[i]); }
		dojo.require.apply(dojo, args);
	}
}

dojo.requireAfterIf = dojo.requireIf;

dojo.provide = function(){
	return dojo.hostenv.startPackage.apply(dojo.hostenv, arguments);
}

dojo.setModulePrefix = function(module, prefix){
	return dojo.hostenv.setModulePrefix(module, prefix);
}

// determine if an object supports a given method
// useful for longer api chains where you have to test each object in the chain
dojo.exists = function(obj, name){
	var p = name.split(".");
	for(var i = 0; i < p.length; i++){
	if(!(obj[p[i]])) return false;
		obj = obj[p[i]];
	}
	return true;
}

// make jsc shut up (so we can use jsc to sanity check the code even if it will never run it).
/*@cc_on
@if (@_jscript_version >= 7)
var window; var XMLHttpRequest;
@end
@*/

if(typeof window == 'undefined'){
	dojo.raise("no window object");
}

// attempt to figure out the path to dojo if it isn't set in the config
(function() {
	// before we get any further with the config options, try to pick them out
	// of the URL. Most of this code is from NW
	if(djConfig.allowQueryConfig){
		var baseUrl = document.location.toString(); // FIXME: use location.query instead?
		var params = baseUrl.split("?", 2);
		if(params.length > 1){
			var paramStr = params[1];
			var pairs = paramStr.split("&");
			for(var x in pairs){
				var sp = pairs[x].split("=");
				// FIXME: is this eval dangerous?
				if((sp[0].length > 9)&&(sp[0].substr(0, 9) == "djConfig.")){
					var opt = sp[0].substr(9);
					try{
						djConfig[opt]=eval(sp[1]);
					}catch(e){
						djConfig[opt]=sp[1];
					}
				}
			}
		}
	}

	if(((djConfig["baseScriptUri"] == "")||(djConfig["baseRelativePath"] == "")) &&(document && document.getElementsByTagName)){
		var scripts = document.getElementsByTagName("script");
		var rePkg = /(__package__|dojo|bootstrap1)\.js([\?\.]|$)/i;
		for(var i = 0; i < scripts.length; i++) {
			var src = scripts[i].getAttribute("src");
			if(!src) { continue; }
			var m = src.match(rePkg);
			if(m) {
				root = src.substring(0, m.index);
				if(src.indexOf("bootstrap1") > -1) { root += "../"; }
				if(!this["djConfig"]) { djConfig = {}; }
				if(djConfig["baseScriptUri"] == "") { djConfig["baseScriptUri"] = root; }
				if(djConfig["baseRelativePath"] == "") { djConfig["baseRelativePath"] = root; }
				break;
			}
		}
	}

	// fill in the rendering support information in dojo.render.*
	var dr = dojo.render;
	var drh = dojo.render.html;
	var drs = dojo.render.svg;
	var dua = drh.UA = navigator.userAgent;
	var dav = drh.AV = navigator.appVersion;
	var t = true;
	var f = false;
	drh.capable = t;
	drh.support.builtin = t;

	dr.ver = parseFloat(drh.AV);
	dr.os.mac = dav.indexOf("Macintosh") >= 0;
	dr.os.win = dav.indexOf("Windows") >= 0;
	// could also be Solaris or something, but it's the same browser
	dr.os.linux = dav.indexOf("X11") >= 0;

	drh.opera = dua.indexOf("Opera") >= 0;
	drh.khtml = (dav.indexOf("Konqueror") >= 0)||(dav.indexOf("Safari") >= 0);
	drh.safari = dav.indexOf("Safari") >= 0;
	var geckoPos = dua.indexOf("Gecko");
	drh.mozilla = drh.moz = (geckoPos >= 0)&&(!drh.khtml);
	if (drh.mozilla) {
		// gecko version is YYYYMMDD
		drh.geckoVersion = dua.substring(geckoPos + 6, geckoPos + 14);
	}
	drh.ie = (document.all)&&(!drh.opera);
	drh.ie50 = drh.ie && dav.indexOf("MSIE 5.0")>=0;
	drh.ie55 = drh.ie && dav.indexOf("MSIE 5.5")>=0;
	drh.ie60 = drh.ie && dav.indexOf("MSIE 6.0")>=0;

	dr.vml.capable=drh.ie;
	drs.capable = f;
	drs.support.plugin = f;
	drs.support.builtin = f;
	if (document.implementation
		&& document.implementation.hasFeature
		&& document.implementation.hasFeature("org.w3c.dom.svg", "1.0")
	){
		drs.capable = t;
		drs.support.builtin = t;
		drs.support.plugin = f;
	}
})();

dojo.hostenv.startPackage("dojo.hostenv");

dojo.render.name = dojo.hostenv.name_ = 'browser';
dojo.hostenv.searchIds = [];

// These are in order of decreasing likelihood; this will change in time.
var DJ_XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];

dojo.hostenv.getXmlhttpObject = function(){
    var http = null;
	var last_e = null;
	try{ http = new XMLHttpRequest(); }catch(e){}
    if(!http){
		for(var i=0; i<3; ++i){
			var progid = DJ_XMLHTTP_PROGIDS[i];
			try{
				http = new ActiveXObject(progid);
			}catch(e){
				last_e = e;
			}

			if(http){
				DJ_XMLHTTP_PROGIDS = [progid];  // so faster next time
				break;
			}
		}

		/*if(http && !http.toString) {
			http.toString = function() { "[object XMLHttpRequest]"; }
		}*/
	}

	if(!http){
		return dojo.raise("XMLHTTP not available", last_e);
	}

	return http;
}

/**
 * Read the contents of the specified uri and return those contents.
 *
 * @param uri A relative or absolute uri. If absolute, it still must be in the
 * same "domain" as we are.
 *
 * @param async_cb If not specified, load synchronously. If specified, load
 * asynchronously, and use async_cb as the progress handler which takes the
 * xmlhttp object as its argument. If async_cb, this function returns null.
 *
 * @param fail_ok Default false. If fail_ok and !async_cb and loading fails,
 * return null instead of throwing.
 */
dojo.hostenv.getText = function(uri, async_cb, fail_ok){

	var http = this.getXmlhttpObject();

	if(async_cb){
		http.onreadystatechange = function(){
			if((4==http.readyState)&&(http["status"])){
				if(http.status==200){
					// dojo.debug("LOADED URI: "+uri);
					async_cb(http.responseText);
				}
			}
		}
	}

	http.open('GET', uri, async_cb ? true : false);
	try {
		http.send(null);
	} catch (e) {
		if (fail_ok && !async_cb) {
			return null;
		} else {
			throw e;
		}
	}
	if(async_cb){
		return null;
	}

	return http.responseText;
}

/*
 * It turns out that if we check *right now*, as this script file is being loaded,
 * then the last script element in the window DOM is ourselves.
 * That is because any subsequent script elements haven't shown up in the document
 * object yet.
 */
 /*
function dj_last_script_src() {
    var scripts = window.document.getElementsByTagName('script');
    if(scripts.length < 1){
		dojo.raise("No script elements in window.document, so can't figure out my script src");
	}
    var script = scripts[scripts.length - 1];
    var src = script.src;
    if(!src){
		dojo.raise("Last script element (out of " + scripts.length + ") has no src");
	}
    return src;
}

if(!dojo.hostenv["library_script_uri_"]){
	dojo.hostenv.library_script_uri_ = dj_last_script_src();
}
*/

dojo.hostenv.defaultDebugContainerId = 'dojoDebug';
dojo.hostenv._println_buffer = [];
dojo.hostenv._println_safe = false;
dojo.hostenv.println = function (line){
	if(!dojo.hostenv._println_safe){
		dojo.hostenv._println_buffer.push(line);
	}else{
		try {
			var console = document.getElementById(djConfig.debugContainerId ?
				djConfig.debugContainerId : dojo.hostenv.defaultDebugContainerId);
			if(!console) { console = document.getElementsByTagName("body")[0] || document.body; }

			var div = document.createElement("div");
			div.appendChild(document.createTextNode(line));
			console.appendChild(div);
		} catch (e) {
			try{
				// safari needs the output wrapped in an element for some reason
				document.write("<div>" + line + "</div>");
			}catch(e2){
				window.status = line;
			}
		}
	}
}

dojo.addOnLoad(function(){
	dojo.hostenv._println_safe = true;
	while(dojo.hostenv._println_buffer.length > 0){
		dojo.hostenv.println(dojo.hostenv._println_buffer.shift());
	}
});

function dj_addNodeEvtHdlr (node, evtName, fp, capture){
	var oldHandler = node["on"+evtName] || function(){};
	node["on"+evtName] = function(){
		fp.apply(node, arguments);
		oldHandler.apply(node, argument
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.6....	y..."!Q.....
........s);
	}
	return true;
}


/* Uncomment this to allow init after DOMLoad, not after window.onload

// Mozilla exposes the event we could use
if (dojo.render.html.mozilla) {
   document.addEventListener("DOMContentLoaded", dj_load_init, null);
}
// for Internet Explorer. readyState will not be achieved on init call, but dojo doesn't need it
//Tighten up the comments below to allow init after DOMLoad, not after window.onload
/ * @cc_on @ * /
/ * @if (@_win32)
    document.write("<script defer>dj_load_init()<"+"/script>");
/ * @end @ * /
*/

// default for other browsers
// potential TODO: apply setTimeout approach for other browsers
// that will cause flickering though ( document is loaded and THEN is processed)
// maybe show/hide required in this case..
// TODO: other browsers may support DOMContentLoaded/defer attribute. Add them to above.
dj_addNodeEvtHdlr(window, "load", function(){
	// allow multiple calls, only first one will take effect
	if(arguments.callee.initialized){ return; }
	arguments.callee.initialized = true;

	var initFunc = function(){
		//perform initialization
		if(dojo.render.html.ie){
			dojo.hostenv.makeWidgets();
		}
	};

	if(dojo.hostenv.inFlightCount == 0){
		initFunc();
		dojo.hostenv.modulesLoaded();
	}else{
		dojo.addOnLoad(initFunc);
	}
});

dojo.hostenv.makeWidgets = function(){
	// you can put searchIds in djConfig and dojo.hostenv at the moment
	// we should probably eventually move to one or the other
	var sids = [];
	if(djConfig.searchIds && djConfig.searchIds.length > 0) {
		sids = sids.concat(djConfig.searchIds);
	}
	if(dojo.hostenv.searchIds && dojo.hostenv.searchIds.length > 0) {
		sids = sids.concat(dojo.hostenv.searchIds);
	}

	if((djConfig.parseWidgets)||(sids.length > 0)){
		if(dojo.evalObjPath("dojo.widget.Parse")){
			// we must do this on a delay to avoid:
			//	http://www.shaftek.org/blog/archives/000212.html
			// IE is such a tremendous peice of shit.
			try{
				var parser = new dojo.xml.Parse();
				if(sids.length > 0){
					for(var x=0; x<sids.length; x++){
						var tmpNode = document.getElementById(sids[x]);
						if(!tmpNode){ continue; }
						var frag = parser.parseElement(tmpNode, null, true);
						dojo.widget.getParser().createComponents(frag);
					}
				}else if(djConfig.parseWidgets){
					var frag  = parser.parseElement(document.getElementsByTagName("body")[0] || document.body, null, true);
					dojo.widget.getParser().createComponents(frag);
				}
			}catch(e){
				dojo.debug("auto-build-widgets error:", e);
			}
		}
	}
}

dojo.addOnLoad(function(){
	if(!dojo.render.html.ie) {
		dojo.hostenv.makeWidgets();
	}
});

try {
	if (dojo.render.html.ie) {
		//	easier and safer VML addition.  Thanks Emil!
		document.namespaces.add("v", "urn:schemas-microsoft-com:vml");
		document.createStyleSheet().addRule("v\\:*", "behavior:url(#default#VML)");
	}
} catch (e) { }

// stub, over-ridden by debugging code. This will at least keep us from
// breaking when it's not included
dojo.hostenv.writeIncludes = function(){}

dojo.byId = function(id, doc){
	if(id && (typeof id == "string" || id instanceof String)){
		if(!doc){ doc = document; }
		return doc.getElementById(id);
	}
	return id; // assume it's a node
}

//Semicolon is for when this file is integrated with a custom build on one line
//with some other file's contents. Sometimes that makes things not get defined
//properly, particularly with the using the closure below to do all the work.
;(function(){
	//Don't do this work if dojo.js has already done it.
	if(typeof dj_usingBootstrap != "undefined"){
		return;
	}

	var isRhino = false;
	var isSpidermonkey = false;
	var isDashboard = false;
	if((typeof this["load"] == "function")&&(typeof this["Packages"] == "function")){
		isRhino = true;
	}else if(typeof this["load"] == "function"){
		isSpidermonkey  = true;
	}else if(window.widget){
		isDashboard = true;
	}

	var tmps = [];
	if((this["djConfig"])&&((djConfig["isDebug"])||(djConfig["debugAtAllCosts"]))){
		tmps.push("debug.js");
	}

	if((this["djConfig"])&&(djConfig["debugAtAllCosts"])&&(!isRhino)&&(!isDashboard)){
		tmps.push("browser_debug.js");
	}

	//Support compatibility packages. Right now this only allows setting one
	//compatibility package. Might need to revisit later down the line to support
	//more than one.
	if((this["djConfig"])&&(djConfig["compat"])){
		tmps.push("compat/" + djConfig["compat"] + ".js");
	}

	var loaderRoot = djConfig["baseScriptUri"];
	if((this["djConfig"])&&(djConfig["baseLoaderUri"])){
		loaderRoot = djConfig["baseLoaderUri"];
	}

	for(var x=0; x < tmps.length; x++){
		var spath = loaderRoot+"src/"+tmps[x];
		if(isRhino||isSpidermonkey){
			load(spath);
		} else {
			try {
				document.write("<scr"+"ipt type='text/javascript' src='"+spath+"'></scr"+"ipt>");
			} catch (e) {
				var script = document.createElement("script");
				script.src = spath;
				document.getElementsByTagName("head")[0].appendChild(script);
			}
		}
	}
})();

dojo.provide("dojo.string.common");

dojo.require("dojo.string");

/**
 * Trim whitespace from 'str'. If 'wh' > 0,
 * only trim from start, if 'wh' < 0, only trim
 * from end, otherwise trim both ends
 */
dojo.string.trim = function(str, wh){
	if(!str.replace){ return str; }
	if(!str.length){ return str; }
	var re = (wh > 0) ? (/^\s+/) : (wh < 0) ? (/\s+$/) : (/^\s+|\s+$/g);
	return str.replace(re, "");
}

/**
 * Trim whitespace at the beginning of 'str'
 */
dojo.string.trimStart = function(str) {
	return dojo.string.trim(str, 1);
}

/**
 * Trim whitespace at the end of 'str'
 */
dojo.string.trimEnd = function(str) {
	return dojo.string.trim(str, -1);
}

/**
 * Return 'str' repeated 'count' times, optionally
 * placing 'separator' between each rep
 */
dojo.string.repeat = function(str, count, separator) {
	var out = "";
	for(var i = 0; i < count; i++) {
		out += str;
		if(separator && i < count - 1) {
			out += separator;
		}
	}
	return out;
}

/**
 * Pad 'str' to guarantee that it is at least 'len' length
 * with the character 'c' at either the start (dir=1) or
 * end (dir=-1) of the string
 */
dojo.string.pad = function(str, len/*=2*/, c/*='0'*/, dir/*=1*/) {
	var out = String(str);
	if(!c) {
		c = '0';
	}
	if(!dir) {
		dir = 1;
	}
	while(out.length < len) {
		if(dir > 0) {
			out = c + out;
		} else {
			out += c;
		}
	}
	return out;
}

/** same as dojo.string.pad(str, len, c, 1) */
dojo.string.padLeft = function(str, len, c) {
	return dojo.string.pad(str, len, c, 1);
}

/** same as dojo.string.pad(str, len, c, -1) */
dojo.string.padRight = function(str, len, c) {
	return dojo.string.pad(str, len, c, -1);
}

dojo.provide("dojo.string");
dojo.require("dojo.string.common");

dojo.provide("dojo.lang.common");

dojo.require("dojo.lang");

/*
 * Adds the given properties/methods to the specified object
 */
dojo.lang.mixin = function(obj, props){
	var tobj = {};
	for(var x in props){
		// the "tobj" condition avoid copying properties in "props"
		// inherited from Object.prototype.  For example, if obj has a custom
		// toString() method, don't overwrite it with the toString() method
		// that props inherited from Object.protoype
		if(typeof tobj[x] == "undefined" || tobj[x] != props[x]) {
			obj[x] = props[x];
		}
	}
	// IE doesn't recognize custom toStrings in for..in
	if(dojo.render.html.ie && dojo.lang.isFunction(props["toString"]) && props["toString"] != obj["toString"]) {
		obj.toString = props.toString;
	}
	return obj;
}

/*
 * Adds the given properties/methods to the specified object's prototype
 */
dojo.lang.extend = function(ctor, props){
	this.mixin(ctor.prototype, props);
}

/**
 * See if val is in arr. Call signatures:
 *  find(array, value, identity) // recommended
 *  find(value, array, identity)
**/
dojo.lang.find = function(	/*Array*/	arr, 
							/*Object*/	val,
							/*boolean*/	identity,
							/*boolean*/	findLast){
	// support both (arr, val) and (val, arr)
	if(!dojo.lang.isArrayLike(arr) && dojo.lang.isArrayLike(val)) {
		var a = arr;
		arr = val;
		val = a;
	}
	var isString = dojo.lang.isString(arr);
	if(isString) { arr = arr.split(""); }

	if(findLast) {
		var step = -1;
		var i = arr.length - 1;
		var end = -1;
	} else {
		var step = 1;
		var i = 0;
		var end = arr.length;
	}
	if(identity){
		while(i != end) {
			if(arr[i] === val){ return i; }
			i += step;
		}
	}else{
		while(i != end) {
			if(arr[i] == val){ return i; }
			i += step;
		}
	}
	return -1;
}

dojo.lang.indexOf = dojo.lang.find;

dojo.lang.findLast = function(/*Array*/ arr, /*Object*/ val, /*boolean*/ identity){
	return dojo.lang.find(arr, val, identity, true);
}

dojo.lang.lastIndexOf = dojo.lang.findLast;

dojo.lang.inArray = function(arr /*Array*/, val /*Object*/){
	return dojo.lang.find(arr, val) > -1; // return: boolean
}

/**
 * Partial implmentation of is* functions from
 * http://www.crockford.com/javascript/recommend.html
 * NOTE: some of these may not be the best thing to use in all situations
 * as they aren't part of core JS and therefore can't work in every case.
 * 
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.6....	y..."!Q.....
...&....See WARNING messages inline for tips.
 *
 * The following is* functions are fairly "safe"
 */

dojo.lang.isObject = function(wh) {
	return typeof wh == "object" || dojo.lang.isArray(wh) || dojo.lang.isFunction(wh);
}

dojo.lang.isArray = function(wh) {
	return (wh instanceof Array || typeof wh == "array");
}

dojo.lang.isArrayLike = function(wh) {
	if(dojo.lang.isString(wh)){ return false; }
	if(dojo.lang.isFunction(wh)){ return false; } // keeps out built-in ctors (Number, String, ...) which have length properties
	if(dojo.lang.isArray(wh)){ return true; }
	if(typeof wh != "undefined" && wh
		&& dojo.lang.isNumber(wh.length) && isFinite(wh.length)){ return true; }
	return false;
}

dojo.lang.isFunction = function(wh) {
	return (wh instanceof Function || typeof wh == "function");
}

dojo.lang.isString = function(wh) {
	return (wh instanceof String || typeof wh == "string");
}

dojo.lang.isAlien = function(wh) {
	return !dojo.lang.isFunction() && /\{\s*\[native code\]\s*\}/.test(String(wh));
}

dojo.lang.isBoolean = function(wh) {
	return (wh instanceof Boolean || typeof wh == "boolean");
}

/**
 * The following is***() functions are somewhat "unsafe". Fortunately,
 * there are workarounds the the language provides and are mentioned
 * in the WARNING messages.
 *
 * WARNING: In most cases, isNaN(wh) is sufficient to determine whether or not
 * something is a number or can be used as such. For example, a number or string
 * can be used interchangably when accessing array items (arr["1"] is the same as
 * arr[1]) and isNaN will return false for both values ("1" and 1). Should you
 * use isNumber("1"), that will return false, which is generally not too useful.
 * Also, isNumber(NaN) returns true, again, this isn't generally useful, but there
 * are corner cases (like when you want to make sure that two things are really
 * the same type of thing). That is really where isNumber "shines".
 *
 * RECOMMENDATION: Use isNaN(wh) when possible
 */
dojo.lang.isNumber = function(wh) {
	return (wh instanceof Number || typeof wh == "number");
}

/**
 * WARNING: In some cases, isUndefined will not behave as you
 * might expect. If you do isUndefined(foo) and there is no earlier
 * reference to foo, an error will be thrown before isUndefined is
 * called. It behaves correctly if you scope yor object first, i.e.
 * isUndefined(foo.bar) where foo is an object and bar isn't a
 * property of the object.
 *
 * RECOMMENDATION: Use `typeof foo == "undefined"` when possible
 *
 * FIXME: Should isUndefined go away since it is error prone?
 */
dojo.lang.isUndefined = function(wh) {
	return ((wh == undefined)&&(typeof wh == "undefined"));
}

// end Crockford functions

dojo.provide("dojo.lang.extras");

dojo.require("dojo.lang.common");

/**
 * Sets a timeout in milliseconds to execute a function in a given context
 * with optional arguments.
 *
 * setTimeout (Object context, function func, number delay[, arg1[, ...]]);
 * setTimeout (function func, number delay[, arg1[, ...]]);
 */
dojo.lang.setTimeout = function(func, delay){
	var context = window, argsStart = 2;
	if(!dojo.lang.isFunction(func)){
		context = func;
		func = delay;
		delay = arguments[2];
		argsStart++;
	}

	if(dojo.lang.isString(func)){
		func = context[func];
	}
	
	var args = [];
	for (var i = argsStart; i < arguments.length; i++) {
		args.push(arguments[i]);
	}
	return setTimeout(function () { func.apply(context, args); }, delay);
}

dojo.lang.getNameInObj = function(ns, item){
	if(!ns){ ns = dj_global; }

	for(var x in ns){
		if(ns[x] === item){
			return new String(x);
		}
	}
	return null;
}

dojo.lang.shallowCopy = function(obj) {
	var ret = {}, key;
	for(key in obj) {
		if(dojo.lang.isUndefined(ret[key])) {
			ret[key] = obj[key];
		}
	}
	return ret;
}

/**
 * Return the first argument that isn't undefined
 */
dojo.lang.firstValued = function(/* ... */) {
	for(var i = 0; i < arguments.length; i++) {
		if(typeof arguments[i] != "undefined") {
			return arguments[i];
		}
	}
	return undefined;
}

/**
 * Get a value from a reference specified as a string descriptor,
 * (e.g. "A.B") in the given context.
 * 
 * getObjPathValue(String objpath [, Object context, Boolean create])
 *
 * If context is not specified, dj_global is used
 * If create is true, undefined objects in the path are created.
 */
dojo.lang.getObjPathValue = function(objpath, context, create){
	with(dojo.parseObjPath(objpath, context, create)){
		return dojo.evalProp(prop, obj, create);
	}
}

/**
 * Set a value on a reference specified as a string descriptor. 
 * (e.g. "A.B") in the given context.
 * 
 * setObjPathValue(String objpath, value [, Object context, Boolean create])
 *
 * If context is not specified, dj_global is used
 * If create is true, undefined objects in the path are created.
 */
dojo.lang.setObjPathValue = function(objpath, value, context, create){
	if(arguments.length < 4){
		create = true;
	}
	with(dojo.parseObjPath(objpath, context, create)){
		if(obj && (create || (prop in obj))){
			obj[prop] = value;
		}
	}
}

dojo.provide("dojo.io.IO");
dojo.require("dojo.string");
dojo.require("dojo.lang.extras");

/******************************************************************************
 *	Notes about dojo.io design:
 *	
 *	The dojo.io.* package has the unenviable task of making a lot of different
 *	types of I/O feel natural, despite a universal lack of good (or even
 *	reasonable!) I/O capability in the host environment. So lets pin this down
 *	a little bit further.
 *
 *	Rhino:
 *		perhaps the best situation anywhere. Access to Java classes allows you
 *		to do anything one might want in terms of I/O, both synchronously and
 *		async. Can open TCP sockets and perform low-latency client/server
 *		interactions. HTTP transport is available through Java HTTP client and
 *		server classes. Wish it were always this easy.
 *
 *	xpcshell:
 *		XPCOM for I/O. A cluster-fuck to be sure.
 *
 *	spidermonkey:
 *		S.O.L.
 *
 *	Browsers:
 *		Browsers generally do not provide any useable filesystem access. We are
 *		therefore limited to HTTP for moving information to and from Dojo
 *		instances living in a browser.
 *
 *		XMLHTTP:
 *			Sync or async, allows reading of arbitrary text files (including
 *			JS, which can then be eval()'d), writing requires server
 *			cooperation and is limited to HTTP mechanisms (POST and GET).
 *
 *		<iframe> hacks:
 *			iframe document hacks allow browsers to communicate asynchronously
 *			with a server via HTTP POST and GET operations. With significant
 *			effort and server cooperation, low-latency data transit between
 *			client and server can be acheived via iframe mechanisms (repubsub).
 *
 *		SVG:
 *			Adobe's SVG viewer implements helpful primitives for XML-based
 *			requests, but receipt of arbitrary text data seems unlikely w/o
 *			<![CDATA[]]> sections.
 *
 *
 *	A discussion between Dylan, Mark, Tom, and Alex helped to lay down a lot
 *	the IO API interface. A transcript of it can be found at:
 *		http://dojotoolkit.org/viewcvs/viewcvs.py/documents/irc/irc_io_api_log.txt?rev=307&view=auto
 *	
 *	Also referenced in the design of the API was the DOM 3 L&S spec:
 *		http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/load-save.html
 ******************************************************************************/

// a map of the available transport options. Transports should add themselves
// by calling add(name)
dojo.io.transports = [];
dojo.io.hdlrFuncNames = [ "load", "error", "timeout" ]; // we're omitting a progress() event for now

dojo.io.Request = function(url, mimetype, transport, changeUrl){
	if((arguments.length == 1)&&(arguments[0].constructor == Object)){
		this.fromKwArgs(arguments[0]);
	}else{
		this.url = url;
		if(mimetype){ this.mimetype = mimetype; }
		if(transport){ this.transport = transport; }
		if(arguments.length >= 4){ this.changeUrl = changeUrl; }
	}
}

dojo.lang.extend(dojo.io.Request, {

	/** The URL to hit */
	url: "",
	
	/** The mime type used to interrpret the response body */
	mimetype: "text/plain",
	
	/** The HTTP method to use */
	method: "GET",
	
	/** An Object containing key-value pairs to be included with the request */
	content: undefined, // Object
	
	/** The transport medium to use */
	transport: undefined, // String
	
	/** If defined the URL of the page is physically changed */
	changeUrl: undefined, // String
	
	/** A form node to use in the request */
	formNode: undefined, // HTMLFormElement
	
	/** Whether the request should be made synchronously */
	sync: false,
	
	bindSuccess: false,

	/** Cache/look for the request in the cache before attempting to request?
	 *  NOTE: this isn't a browser cache, this is internal and would only cache in-page
	 */
	useCache: false,

	/** Prevent the browser from caching this by adding a query string argument to the URL */
	preventCache: false,
	
	// events stuff
	load: function(type, data, evt){ },
	error: function(type, error){ },
	timeout: function(type){ },
	handle: function(){ },

	//FIXME
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.6.4..	y..."!Q.....
...G...&: change BrowserIO.js to use timeouts? IframeIO?
	// The number of seconds to wait until firing a timeout callback.
	// If it is zero, that means, don't do a timeout check.
	timeoutSeconds: 0,
	
	// the abort method needs to be filled in by the transport that accepts the
	// bind() request
	abort: function(){ },
	
	// backButton: function(){ },
	// forwardButton: function(){ },

	fromKwArgs: function(kwArgs){
		// normalize args
		if(kwArgs["url"]){ kwArgs.url = kwArgs.url.toString(); }
		if(kwArgs["formNode"]) { kwArgs.formNode = dojo.byId(kwArgs.formNode); }
		if(!kwArgs["method"] && kwArgs["formNode"] && kwArgs["formNode"].method) {
			kwArgs.method = kwArgs["formNode"].method;
		}
		
		// backwards compatibility
		if(!kwArgs["handle"] && kwArgs["handler"]){ kwArgs.handle = kwArgs.handler; }
		if(!kwArgs["load"] && kwArgs["loaded"]){ kwArgs.load = kwArgs.loaded; }
		if(!kwArgs["changeUrl"] && kwArgs["changeURL"]) { kwArgs.changeUrl = kwArgs.changeURL; }

		// encoding fun!
		kwArgs.encoding = dojo.lang.firstValued(kwArgs["encoding"], djConfig["bindEncoding"], "");

		kwArgs.sendTransport = dojo.lang.firstValued(kwArgs["sendTransport"], djConfig["ioSendTransport"], false);

		var isFunction = dojo.lang.isFunction;
		for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
			var fn = dojo.io.hdlrFuncNames[x];
			if(isFunction(kwArgs[fn])){ continue; }
			if(isFunction(kwArgs["handle"])){
				kwArgs[fn] = kwArgs.handle;
			}
			// handler is aliased above, shouldn't need this check
			/* else if(dojo.lang.isObject(kwArgs.handler)){
				if(isFunction(kwArgs.handler[fn])){
					kwArgs[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"]||function(){};
				}
			}*/
		}
		dojo.lang.mixin(this, kwArgs);
	}

});

dojo.io.Error = function(msg, type, num){
	this.message = msg;
	this.type =  type || "unknown"; // must be one of "io", "parse", "unknown"
	this.number = num || 0; // per-substrate error number, not normalized
}

dojo.io.transports.addTransport = function(name){
	this.push(name);
	// FIXME: do we need to handle things that aren't direct children of the
	// dojo.io namespace? (say, dojo.io.foo.fooTransport?)
	this[name] = dojo.io[name];
}

// binding interface, the various implementations register their capabilities
// and the bind() method dispatches
dojo.io.bind = function(request){
	// if the request asks for a particular implementation, use it
	if(!(request instanceof dojo.io.Request)){
		try{
			request = new dojo.io.Request(request);
		}catch(e){ dojo.debug(e); }
	}
	var tsName = "";
	if(request["transport"]){
		tsName = request["transport"];
		// FIXME: it would be good to call the error handler, although we'd
		// need to use setTimeout or similar to accomplish this and we can't
		// garuntee that this facility is available.
		if(!this[tsName]){ return request; }
	}else{
		// otherwise we do our best to auto-detect what available transports
		// will handle 
		for(var x=0; x<dojo.io.transports.length; x++){
			var tmp = dojo.io.transports[x];
			if((this[tmp])&&(this[tmp].canHandle(request))){
				tsName = tmp;
			}
		}
		if(tsName == ""){ return request; }
	}
	this[tsName].bind(request);
	request.bindSuccess = true;
	return request;
}

dojo.io.queueBind = function(request){
	if(!(request instanceof dojo.io.Request)){
		try{
			request = new dojo.io.Request(request);
		}catch(e){ dojo.debug(e); }
	}

	// make sure we get called if/when we get a response
	var oldLoad = request.load;
	request.load = function(){
		dojo.io._queueBindInFlight = false;
		var ret = oldLoad.apply(this, arguments);
		dojo.io._dispatchNextQueueBind();
		return ret;
	}

	var oldErr = request.error;
	request.error = function(){
		dojo.io._queueBindInFlight = false;
		var ret = oldErr.apply(this, arguments);
		dojo.io._dispatchNextQueueBind();
		return ret;
	}

	dojo.io._bindQueue.push(request);
	dojo.io._dispatchNextQueueBind();
	return request;
}

dojo.io._dispatchNextQueueBind = function(){
	if(!dojo.io._queueBindInFlight){
		dojo.io._queueBindInFlight = true;
		if(dojo.io._bindQueue.length > 0){
			dojo.io.bind(dojo.io._bindQueue.shift());
		}else{
			dojo.io._queueBindInFlight = false;
		}
	}
}
dojo.io._bindQueue = [];
dojo.io._queueBindInFlight = false;

dojo.io.argsFromMap = function(map, encoding, last){
	var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
	var mapped = [];
	var control = new Object();
	for(var name in map){
		var domap = function(elt){
			var val = enc(name)+"="+enc(elt);
			mapped[(last == name) ? "push" : "unshift"](val);
		}
		if(!control[name]){
			var value = map[name];
			// FIXME: should be isArrayLike?
			if (dojo.lang.isArray(value)){
				dojo.lang.forEach(value, domap);
			}else{
				domap(value);
			}
		}
	}
	return mapped.join("&");
}

dojo.io.setIFrameSrc = function(iframe, src, replace){
	try{
		var r = dojo.render.html;
		// dojo.debug(iframe);
		if(!replace){
			if(r.safari){
				iframe.location = src;
			}else{
				frames[iframe.name].location = src;
			}
		}else{
			// Fun with DOM 0 incompatibilities!
			var idoc;
			if(r.ie){
				idoc = iframe.contentWindow.document;
			}else if(r.safari){
				idoc = iframe.document;
			}else{ //  if(r.moz){
				idoc = iframe.contentWindow;
			}
			idoc.location.replace(src);
		}
	}catch(e){ 
		dojo.debug(e); 
		dojo.debug("setIFrameSrc: "+e); 
	}
}

/*
dojo.io.sampleTranport = new function(){
	this.canHandle = function(kwArgs){
		// canHandle just tells dojo.io.bind() if this is a good transport to
		// use for the particular type of request.
		if(	
			(
				(kwArgs["mimetype"] == "text/plain") ||
				(kwArgs["mimetype"] == "text/html") ||
				(kwArgs["mimetype"] == "text/javascript")
			)&&(
				(kwArgs["method"] == "get") ||
				( (kwArgs["method"] == "post") && (!kwArgs["formNode"]) )
			)
		){
			return true;
		}

		return false;
	}

	this.bind = function(kwArgs){
		var hdlrObj = {};

		// set up a handler object
		for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
			var fn = dojo.io.hdlrFuncNames[x];
			if(typeof kwArgs.handler == "object"){
				if(typeof kwArgs.handler[fn] == "function"){
					hdlrObj[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"];
				}
			}else if(typeof kwArgs[fn] == "function"){
				hdlrObj[fn] = kwArgs[fn];
			}else{
				hdlrObj[fn] = kwArgs["handle"]||function(){};
			}
		}

		// build a handler function that calls back to the handler obj
		var hdlrFunc = function(evt){
			if(evt.type == "onload"){
				hdlrObj.load("load", evt.data, evt);
			}else if(evt.type == "onerr"){
				var errObj = new dojo.io.Error("sampleTransport Error: "+evt.msg);
				hdlrObj.error("error", errObj);
			}
		}

		// the sample transport would attach the hdlrFunc() when sending the
		// request down the pipe at this point
		var tgtURL = kwArgs.url+"?"+dojo.io.argsFromMap(kwArgs.content);
		// sampleTransport.sendRequest(tgtURL, hdlrFunc);
	}

	dojo.io.transports.addTransport("sampleTranport");
}
*/

dojo.provide("dojo.lang.array");

dojo.require("dojo.lang.common");

// FIXME: Is this worthless since you can do: if(name in obj)
// is this the right place for this?
dojo.lang.has = function(obj, name){
	try{
		return (typeof obj[name] != "undefined");
	}catch(e){ return false; }
}

dojo.lang.isEmpty = function(obj) {
	if(dojo.lang.isObject(obj)) {
		var tmp = {};
		var count = 0;
		for(var x in obj){
			if(obj[x] && (!tmp[x])){
				count++;
				break;
			} 
		}
		return (count == 0);
	} else if(dojo.lang.isArrayLike(obj) || dojo.lang.isString(obj)) {
		return obj.length == 0;
	}
}

dojo.lang.map = function(arr, obj, unary_func){
	var isString = dojo.lang.isString(arr);
	if(isString){
		arr = arr.split("");
	}
	if(dojo.lang.isFunction(obj)&&(!unary_func)){
		unary_func = obj;
		obj = dj_global;
	}else if(dojo.lang.isFunction(obj) && unary_func){
		// ff 1.5 compat
		var tmpObj = obj;
		obj = unary_func;
		unary_func = tmpObj;
	}
	if(Array.map){
	 	var outArr = Array.map(arr, unary_func, obj);
	}else{
		var outArr = [];
		for(var i=0;i<arr.length;++i){
			outArr.push(unary_func.call(obj, arr[i]));
		}
	}
	if(isString) {
		return outArr.join("");
	} else {
		return outArr;
	}
}

// http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach
dojo.lang.forEach = function(anArray /* Array */, callback /* Function */, thisObject /* Object */){
	if(dojo.lang.isString(anArray)){ 
		anArray = anArray.split(""); 
	}
	if(Array.forEach){
		Array.forEach(anArray, callback, thisObject);
	}else{
		// FIXME: there are several ways of handilng thisObject. Is dj_global always the default context?
		if(!thisObject){
			thisObject=dj_global;
		}
		for(var i=0,l=anArray.length; i<l; i++){ 
			callback.call(thisObject, anArray[i], i, anArray);
		}
	}
}

dojo.lang._everyOrSome = function(every, arr, callback, thisObject){
	if(dojo.lang.isString(arr)){ 
		arr = arr.split(""); 
	}
	if(Array.every){
		return Array[ (every) ? "every" : "some" ](arr, callback, thisObject);
	}else{
		if(!thisObject){
			thisObject = dj_global;
		}

IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.7.\..	y..."!Q.....
...p...G		for(var i=0,l=arr.length; i<l; i++){
			var result = callback.call(thisObject, arr[i], i, arr);
			if((every)&&(!result)){
				return false;
			}else if((!every)&&(result)){
				return true;
			}
		}
		return (every) ? true : false;
	}
}

dojo.lang.every = function(arr, callback, thisObject){
	return this._everyOrSome(true, arr, callback, thisObject);
}

dojo.lang.some = function(arr, callback, thisObject){
	return this._everyOrSome(false, arr, callback, thisObject);
}

dojo.lang.filter = function(arr, callback, thisObject) {
	var isString = dojo.lang.isString(arr);
	if(isString) { arr = arr.split(""); }
	if(Array.filter) {
		var outArr = Array.filter(arr, callback, thisObject);
	} else {
		if(!thisObject) {
			if(arguments.length >= 3) { dojo.raise("thisObject doesn't exist!"); }
			thisObject = dj_global;
		}

		var outArr = [];
		for(var i = 0; i < arr.length; i++) {
			if(callback.call(thisObject, arr[i], i, arr)) {
				outArr.push(arr[i]);
			}
		}
	}
	if(isString) {
		return outArr.join("");
	} else {
		return outArr;
	}
}

/**
 * Creates a 1-D array out of all the arguments passed,
 * unravelling any array-like objects in the process
 *
 * Ex:
 * unnest(1, 2, 3) ==> [1, 2, 3]
 * unnest(1, [2, [3], [[[4]]]]) ==> [1, 2, 3, 4]
 */
dojo.lang.unnest = function(/* ... */) {
	var out = [];
	for(var i = 0; i < arguments.length; i++) {
		if(dojo.lang.isArrayLike(arguments[i])) {
			var add = dojo.lang.unnest.apply(this, arguments[i]);
			out = out.concat(add);
		} else {
			out.push(arguments[i]);
		}
	}
	return out;
}

/**
 * Converts an array-like object (i.e. arguments, DOMCollection)
 * to an array
**/
dojo.lang.toArray = function(arrayLike, startOffset) {
	var array = [];
	for(var i = startOffset||0; i < arrayLike.length; i++) {
		array.push(arrayLike[i]);
	}
	return array;
}

dojo.provide("dojo.lang.func");

dojo.require("dojo.lang.common");

/**
 * Runs a function in a given scope (thisObject), can
 * also be used to preserve scope.
 *
 * hitch(foo, "bar"); // runs foo.bar() in the scope of foo
 * hitch(foo, myFunction); // runs myFunction in the scope of foo
 */
dojo.lang.hitch = function(thisObject, method) {
	if(dojo.lang.isString(method)) {
		var fcn = thisObject[method];
	} else {
		var fcn = method;
	}

	return function() {
		return fcn.apply(thisObject, arguments);
	}
}

dojo.lang.anonCtr = 0;
dojo.lang.anon = {};
dojo.lang.nameAnonFunc = function(anonFuncPtr, namespaceObj){
	var nso = (namespaceObj || dojo.lang.anon);
	if((dj_global["djConfig"])&&(djConfig["slowAnonFuncLookups"] == true)){
		for(var x in nso){
			if(nso[x] === anonFuncPtr){
				return x;
			}
		}
	}
	var ret = "__"+dojo.lang.anonCtr++;
	while(typeof nso[ret] != "undefined"){
		ret = "__"+dojo.lang.anonCtr++;
	}
	nso[ret] = anonFuncPtr;
	return ret;
}

dojo.lang.forward = function(funcName){
	// Returns a function that forwards a method call to this.func(...)
	return function(){
		return this[funcName].apply(this, arguments);
	};
}

dojo.lang.curry = function(ns, func /* args ... */){
	var outerArgs = [];
	ns = ns||dj_global;
	if(dojo.lang.isString(func)){
		func = ns[func];
	}
	for(var x=2; x<arguments.length; x++){
		outerArgs.push(arguments[x]);
	}
	// since the event system replaces the original function with a new
	// join-point runner with an arity of 0, we check to see if it's left us
	// any clues about the original arity in lieu of the function's actual
	// length property
	var ecount = (func["__preJoinArity"]||func.length) - outerArgs.length;
	// borrowed from svend tofte
	function gather(nextArgs, innerArgs, expected){
		var texpected = expected;
		var totalArgs = innerArgs.slice(0); // copy
		for(var x=0; x<nextArgs.length; x++){
			totalArgs.push(nextArgs[x]);
		}
		// check the list of provided nextArgs to see if it, plus the
		// number of innerArgs already supplied, meets the total
		// expected.
		expected = expected-nextArgs.length;
		if(expected<=0){
			var res = func.apply(ns, totalArgs);
			expected = texpected;
			return res;
		}else{
			return function(){
				return gather(arguments,// check to see if we've been run
										// with enough args
							totalArgs,	// a copy
							expected);	// how many more do we need to run?;
			}
		}
	}
	return gather([], outerArgs, ecount);
}

dojo.lang.curryArguments = function(ns, func, args, offset){
	var targs = [];
	var x = offset||0;
	for(x=offset; x<args.length; x++){
		targs.push(args[x]); // ensure that it's an arr
	}
	return dojo.lang.curry.apply(dojo.lang, [ns, func].concat(targs));
}

dojo.lang.tryThese = function(){
	for(var x=0; x<arguments.length; x++){
		try{
			if(typeof arguments[x] == "function"){
				var ret = (arguments[x]());
				if(ret){
					return ret;
				}
			}
		}catch(e){
			dojo.debug(e);
		}
	}
}

dojo.lang.delayThese = function(farr, cb, delay, onend){
	/**
	 * alternate: (array funcArray, function callback, function onend)
	 * alternate: (array funcArray, function callback)
	 * alternate: (array funcArray)
	 */
	if(!farr.length){ 
		if(typeof onend == "function"){
			onend();
		}
		return;
	}
	if((typeof delay == "undefined")&&(typeof cb == "number")){
		delay = cb;
		cb = function(){};
	}else if(!cb){
		cb = function(){};
		if(!delay){ delay = 0; }
	}
	setTimeout(function(){
		(farr.shift())();
		cb();
		dojo.lang.delayThese(farr, cb, delay, onend);
	}, delay);
}

dojo.provide("dojo.string.extras");

dojo.require("dojo.string.common");
dojo.require("dojo.lang");

/**
 * Parameterized string function
 * str - formatted string with %{values} to be replaces
 * pairs - object of name: "value" value pairs
 * killExtra - remove all remaining %{values} after pairs are inserted
 */
dojo.string.paramString = function(str, pairs, killExtra) {
	for(var name in pairs) {
		var re = new RegExp("\\%\\{" + name + "\\}", "g");
		str = str.replace(re, pairs[name]);
	}

	if(killExtra) { str = str.replace(/%\{([^\}\s]+)\}/g, ""); }
	return str;
}

/** Uppercases the first letter of each word */
dojo.string.capitalize = function (str) {
	if (!dojo.lang.isString(str)) { return ""; }
	if (arguments.length == 0) { str = this; }
	var words = str.split(' ');
	var retval = "";
	var len = words.length;
	for (var i=0; i<len; i++) {
		var word = words[i];
		word = word.charAt(0).toUpperCase() + word.substring(1, word.length);
		retval += word;
		if (i < len-1)
			retval += " ";
	}
	
	return new String(retval);
}

/**
 * Return true if the entire string is whitespace characters
 */
dojo.string.isBlank = function (str) {
	if(!dojo.lang.isString(str)) { return true; }
	return (dojo.string.trim(str).length == 0);
}

dojo.string.encodeAscii = function(str) {
	if(!dojo.lang.isString(str)) { return str; }
	var ret = "";
	var value = escape(str);
	var match, re = /%u([0-9A-F]{4})/i;
	while((match = value.match(re))) {
		var num = Number("0x"+match[1]);
		var newVal = escape("&#" + num + ";");
		ret += value.substring(0, match.index) + newVal;
		value = value.substring(match.index+match[0].length);
	}
	ret += value.replace(/\+/g, "%2B");
	return ret;
}

dojo.string.escape = function(type, str) {
	var args = [];
	for(var i = 1; i < arguments.length; i++) { args.push(arguments[i]); }
	switch(type.toLowerCase()) {
		case "xml":
		case "html":
		case "xhtml":
			return dojo.string.escapeXml.apply(this, args);
		case "sql":
			return dojo.string.escapeSql.apply(this, args);
		case "regexp":
		case "regex":
			return dojo.string.escapeRegExp.apply(this, args);
		case "javascript":
		case "jscript":
		case "js":
			return dojo.string.escapeJavaScript.apply(this, args);
		case "ascii":
			// so it's encode, but it seems useful
			return dojo.string.encodeAscii.apply(this, args);
		default:
			return str;
	}
}

dojo.string.escapeXml = function(str, noSingleQuotes) {
	str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
		.replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
	if(!noSingleQuotes) { str = str.replace(/'/gm, "&#39;"); }
	return str;
}

dojo.string.escapeSql = function(str) {
	return str.replace(/'/gm, "''");
}

dojo.string.escapeRegExp = function(str) {
	return str.replace(/\\/gm, "\\\\").replace(/([\f\b\n\t\r[\^$|?*+(){}])/gm, "\\$1");
}

dojo.string.escapeJavaScript = function(str) {
	return str.replace(/(["'\f\b\n\t\r])/gm, "\\$1");
}

dojo.string.escapeString = function(str){ 
	return ('"' + str.replace(/(["\\])/g, '\\$1') + '"'
		).replace(/[\f]/g, "\\f"
		).replace(/[\b]/g, "\\b"
		).replace(/[\n]/g, "\\n"
		).replace(/[\t]/g, "\\t"
		).replace(/[\r]/g, "\\r");
}

// TODO: make an HTML version
dojo.string.summary = function(str, len) {
	if(!len || str.length <= len) {
		return str;
	} else {
		return str.substring(0, len).replace(/\.+$/, "") + "...";
	}
}

/**
 * Returns true if 'str' ends with 'end'
 */
dojo.string.endsWith = function(str, end, ignoreCase) {
	if(ignoreCase) {
		str = str.toLowerCase();
		end = end.toLowerCase();
	}
	if((str.length - end.length) < 0){
		return false;
	}
	return str.lastIndexOf(end) == str.length - end.length;
}

/**
 * Returns true if 'str' ends wit
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.7/...	y..."!Q.....
.......ph any of the arguments[2 -> n]
 */
dojo.string.endsWithAny = function(str /* , ... */) {
	for(var i = 1; i < arguments.length; i++) {
		if(dojo.string.endsWith(str, arguments[i])) {
			return true;
		}
	}
	return false;
}

/**
 * Returns true if 'str' starts with 'start'
 */
dojo.string.startsWith = function(str, start, ignoreCase) {
	if(ignoreCase) {
		str = str.toLowerCase();
		start = start.toLowerCase();
	}
	return str.indexOf(start) == 0;
}

/**
 * Returns true if 'str' starts with any of the arguments[2 -> n]
 */
dojo.string.startsWithAny = function(str /* , ... */) {
	for(var i = 1; i < arguments.length; i++) {
		if(dojo.string.startsWith(str, arguments[i])) {
			return true;
		}
	}
	return false;
}

/**
 * Returns true if 'str' starts with any of the arguments 2 -> n
 */
dojo.string.has = function(str /* , ... */) {
	for(var i = 1; i < arguments.length; i++) {
		if(str.indexOf(arguments[i]) > -1){
			return true;
		}
	}
	return false;
}

dojo.string.normalizeNewlines = function (text,newlineChar) {
	if (newlineChar == "\n") {
		text = text.replace(/\r\n/g, "\n");
		text = text.replace(/\r/g, "\n");
	} else if (newlineChar == "\r") {
		text = text.replace(/\r\n/g, "\r");
		text = text.replace(/\n/g, "\r");
	} else {
		text = text.replace(/([^\r])\n/g, "$1\r\n");
		text = text.replace(/\r([^\n])/g, "\r\n$1");
	}
	return text;
}

dojo.string.splitEscaped = function (str,charac) {
	var components = [];
	for (var i = 0, prevcomma = 0; i < str.length; i++) {
		if (str.charAt(i) == '\\') { i++; continue; }
		if (str.charAt(i) == charac) {
			components.push(str.substring(prevcomma, i));
			prevcomma = i + 1;
		}
	}
	components.push(str.substr(prevcomma));
	return components;
}

dojo.provide("dojo.dom");
dojo.require("dojo.lang.array");

dojo.dom.ELEMENT_NODE                  = 1;
dojo.dom.ATTRIBUTE_NODE                = 2;
dojo.dom.TEXT_NODE                     = 3;
dojo.dom.CDATA_SECTION_NODE            = 4;
dojo.dom.ENTITY_REFERENCE_NODE         = 5;
dojo.dom.ENTITY_NODE                   = 6;
dojo.dom.PROCESSING_INSTRUCTION_NODE   = 7;
dojo.dom.COMMENT_NODE                  = 8;
dojo.dom.DOCUMENT_NODE                 = 9;
dojo.dom.DOCUMENT_TYPE_NODE            = 10;
dojo.dom.DOCUMENT_FRAGMENT_NODE        = 11;
dojo.dom.NOTATION_NODE                 = 12;
	
dojo.dom.dojoml = "http://www.dojotoolkit.org/2004/dojoml";

/**
 *	comprehensive list of XML namespaces
**/
dojo.dom.xmlns = {
	svg : "http://www.w3.org/2000/svg",
	smil : "http://www.w3.org/2001/SMIL20/",
	mml : "http://www.w3.org/1998/Math/MathML",
	cml : "http://www.xml-cml.org",
	xlink : "http://www.w3.org/1999/xlink",
	xhtml : "http://www.w3.org/1999/xhtml",
	xul : "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
	xbl : "http://www.mozilla.org/xbl",
	fo : "http://www.w3.org/1999/XSL/Format",
	xsl : "http://www.w3.org/1999/XSL/Transform",
	xslt : "http://www.w3.org/1999/XSL/Transform",
	xi : "http://www.w3.org/2001/XInclude",
	xforms : "http://www.w3.org/2002/01/xforms",
	saxon : "http://icl.com/saxon",
	xalan : "http://xml.apache.org/xslt",
	xsd : "http://www.w3.org/2001/XMLSchema",
	dt: "http://www.w3.org/2001/XMLSchema-datatypes",
	xsi : "http://www.w3.org/2001/XMLSchema-instance",
	rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
	rdfs : "http://www.w3.org/2000/01/rdf-schema#",
	dc : "http://purl.org/dc/elements/1.1/",
	dcq: "http://purl.org/dc/qualifiers/1.0",
	"soap-env" : "http://schemas.xmlsoap.org/soap/envelope/",
	wsdl : "http://schemas.xmlsoap.org/wsdl/",
	AdobeExtensions : "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
};

dojo.dom.isNode = function(wh){
	if(typeof Element == "object") {
		try {
			return wh instanceof Element;
		} catch(E) {}
	} else {
		// best-guess
		return wh && !isNaN(wh.nodeType);
	}
}

dojo.dom.getTagName = function(node){
	dojo.deprecated("dojo.dom.getTagName", "use node.tagName instead", "0.4");

	var tagName = node.tagName;
	if(tagName.substr(0,5).toLowerCase()!="dojo:"){
		
		if(tagName.substr(0,4).toLowerCase()=="dojo"){
			// FIXME: this assuumes tag names are always lower case
			return "dojo:" + tagName.substring(4).toLowerCase();
		}

		// allow lower-casing
		var djt = node.getAttribute("dojoType")||node.getAttribute("dojotype");
		if(djt){
			return "dojo:"+djt.toLowerCase();
		}
		
		if((node.getAttributeNS)&&(node.getAttributeNS(this.dojoml,"type"))){
			return "dojo:" + node.getAttributeNS(this.dojoml,"type").toLowerCase();
		}
		try{
			// FIXME: IE really really doesn't like this, so we squelch
			// errors for it
			djt = node.getAttribute("dojo:type");
		}catch(e){ /* FIXME: log? */ }
		if(djt){
			return "dojo:"+djt.toLowerCase();
		}

		if((!dj_global["djConfig"])||(!djConfig["ignoreClassNames"])){
			// FIXME: should we make this optionally enabled via djConfig?
			var classes = node.className||node.getAttribute("class");
			// FIXME: following line, without check for existence of classes.indexOf
			// breaks firefox 1.5's svg widgets
			if((classes)&&(classes.indexOf)&&(classes.indexOf("dojo-") != -1)){
				var aclasses = classes.split(" ");
				for(var x=0; x<aclasses.length; x++){
					if((aclasses[x].length>5)&&(aclasses[x].indexOf("dojo-")>=0)){
						return "dojo:"+aclasses[x].substr(5).toLowerCase();
					}
				}
			}
		}

	}
	return tagName.toLowerCase();
}

dojo.dom.getUniqueId = function(){
	do {
		var id = "dj_unique_" + (++arguments.callee._idIncrement);
	}while(document.getElementById(id));
	return id;
}
dojo.dom.getUniqueId._idIncrement = 0;

dojo.dom.firstElement = dojo.dom.getFirstChildElement = function(parentNode, tagName){
	var node = parentNode.firstChild;
	while(node && node.nodeType != dojo.dom.ELEMENT_NODE){
		node = node.nextSibling;
	}
	if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
		node = dojo.dom.nextElement(node, tagName);
	}
	return node;
}

dojo.dom.lastElement = dojo.dom.getLastChildElement = function(parentNode, tagName){
	var node = parentNode.lastChild;
	while(node && node.nodeType != dojo.dom.ELEMENT_NODE) {
		node = node.previousSibling;
	}
	if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
		node = dojo.dom.prevElement(node, tagName);
	}
	return node;
}

dojo.dom.nextElement = dojo.dom.getNextSiblingElement = function(node, tagName){
	if(!node) { return null; }
	do {
		node = node.nextSibling;
	} while(node && node.nodeType != dojo.dom.ELEMENT_NODE);

	if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
		return dojo.dom.nextElement(node, tagName);
	}
	return node;
}

dojo.dom.prevElement = dojo.dom.getPreviousSiblingElement = function(node, tagName){
	if(!node) { return null; }
	if(tagName) { tagName = tagName.toLowerCase(); }
	do {
		node = node.previousSibling;
	} while(node && node.nodeType != dojo.dom.ELEMENT_NODE);

	if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
		return dojo.dom.prevElement(node, tagName);
	}
	return node;
}

// TODO: hmph
/*this.forEachChildTag = function(node, unaryFunc) {
	var child = this.getFirstChildTag(node);
	while(child) {
		if(unaryFunc(child) == "break") { break; }
		child = this.getNextSiblingTag(child);
	}
}*/

dojo.dom.moveChildren = function(srcNode, destNode, trim){
	var count = 0;
	if(trim) {
		while(srcNode.hasChildNodes() &&
			srcNode.firstChild.nodeType == dojo.dom.TEXT_NODE) {
			srcNode.removeChild(srcNode.firstChild);
		}
		while(srcNode.hasChildNodes() &&
			srcNode.lastChild.nodeType == dojo.dom.TEXT_NODE) {
			srcNode.removeChild(srcNode.lastChild);
		}
	}
	while(srcNode.hasChildNodes()){
		destNode.appendChild(srcNode.firstChild);
		count++;
	}
	return count;
}

dojo.dom.copyChildren = function(srcNode, destNode, trim){
	var clonedNode = srcNode.cloneNode(true);
	return this.moveChildren(clonedNode, destNode, trim);
}

dojo.dom.removeChildren = function(node){
	var count = node.childNodes.length;
	while(node.hasChildNodes()){ node.removeChild(node.firstChild); }
	return count;
}

dojo.dom.replaceChildren = function(node, newChild){
	// FIXME: what if newChild is an array-like object?
	dojo.dom.removeChildren(node);
	node.appendChild(newChild);
}

dojo.dom.removeNode = function(node){
	if(node && node.parentNode){
		// return a ref to the removed child
		return node.parentNode.removeChild(node);
	}
}

dojo.dom.getAncestors = function(node, filterFunction, returnFirstHit) {
	var ancestors = [];
	var isFunction = dojo.lang.isFunction(filterFunction);
	while(node) {
		if (!isFunction || filterFunction(node)) {
			ancestors.push(node);
		}
		if (returnFirstHit && ancestors.length > 0) { return ancestors[0]; }
		
		node = node.parentNode;
	}
	if (returnFirstHit) { return null; }
	return ancestors;
}

dojo.dom.getAncestorsByTag = function(node, tag, returnFirstHit) {
	tag = tag.toLowerCase();
	return dojo.dom.getAncestors(node, function(el){
		return ((el.tagName)&&(el.tagName.toLowerCase() == tag));
	}, returnFirstHit);
}

dojo.dom.getFirstAncestorByTag = func
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.7R...	y..."!Q.....
........tion(node, tag) {
	return dojo.dom.getAncestorsByTag(node, tag, true);
}

dojo.dom.isDescendantOf = function(node, ancestor, guaranteeDescendant){
	// guaranteeDescendant allows us to be a "true" isDescendantOf function
	if(guaranteeDescendant && node) { node = node.parentNode; }
	while(node) {
		if(node == ancestor){ return true; }
		node = node.parentNode;
	}
	return false;
}

dojo.dom.innerXML = function(node){
	if(node.innerXML){
		return node.innerXML;
	}else if(typeof XMLSerializer != "undefined"){
		return (new XMLSerializer()).serializeToString(node);
	}
}

dojo.dom.createDocumentFromText = function(str, mimetype){
	if(!mimetype) { mimetype = "text/xml"; }
	if(typeof DOMParser != "undefined") {
		var parser = new DOMParser();
		return parser.parseFromString(str, mimetype);
	}else if(typeof ActiveXObject != "undefined"){
		var domDoc = new ActiveXObject("Microsoft.XMLDOM");
		if(domDoc) {
			domDoc.async = false;
			domDoc.loadXML(str);
			return domDoc;
		}else{
			dojo.debug("toXml didn't work?");
		}
	/*
	}else if((dojo.render.html.capable)&&(dojo.render.html.safari)){
		// FIXME: this doesn't appear to work!
		// from: http://web-graphics.com/mtarchive/001606.php
		// var xml = '<?xml version="1.0"?>'+str;
		var mtype = "text/xml";
		var xml = '<?xml version="1.0"?>'+str;
		var url = "data:"+mtype+";charset=utf-8,"+encodeURIComponent(xml);
		var req = new XMLHttpRequest();
		req.open("GET", url, false);
		req.overrideMimeType(mtype);
		req.send(null);
		return req.responseXML;
	*/
	}else if(document.createElement){
		// FIXME: this may change all tags to uppercase!
		var tmp = document.createElement("xml");
		tmp.innerHTML = str;
		if(document.implementation && document.implementation.createDocument) {
			var xmlDoc = document.implementation.createDocument("foo", "", null);
			for(var i = 0; i < tmp.childNodes.length; i++) {
				xmlDoc.importNode(tmp.childNodes.item(i), true);
			}
			return xmlDoc;
		}
		// FIXME: probably not a good idea to have to return an HTML fragment
		// FIXME: the tmp.doc.firstChild is as tested from IE, so it may not
		// work that way across the board
		return tmp.document && tmp.document.firstChild ?
			tmp.document.firstChild : tmp;
	}
	return null;
}

dojo.dom.prependChild = function(node, parent) {
	if(parent.firstChild) {
		parent.insertBefore(node, parent.firstChild);
	} else {
		parent.appendChild(node);
	}
	return true;
}

dojo.dom.insertBefore = function(node, ref, force){
	if (force != true &&
		(node === ref || node.nextSibling === ref)){ return false; }
	var parent = ref.parentNode;
	parent.insertBefore(node, ref);
	return true;
}

dojo.dom.insertAfter = function(node, ref, force){
	var pn = ref.parentNode;
	if(ref == pn.lastChild){
		if((force != true)&&(node === ref)){
			return false;
		}
		pn.appendChild(node);
	}else{
		return this.insertBefore(node, ref.nextSibling, force);
	}
	return true;
}

dojo.dom.insertAtPosition = function(node, ref, position){
	if((!node)||(!ref)||(!position)){ return false; }
	switch(position.toLowerCase()){
		case "before":
			return dojo.dom.insertBefore(node, ref);
		case "after":
			return dojo.dom.insertAfter(node, ref);
		case "first":
			if(ref.firstChild){
				return dojo.dom.insertBefore(node, ref.firstChild);
			}else{
				ref.appendChild(node);
				return true;
			}
			break;
		default: // aka: last
			ref.appendChild(node);
			return true;
	}
}

dojo.dom.insertAtIndex = function(node, containingNode, insertionIndex){
	var siblingNodes = containingNode.childNodes;

	// if there aren't any kids yet, just add it to the beginning

	if (!siblingNodes.length){
		containingNode.appendChild(node);
		return true;
	}

	// otherwise we need to walk the childNodes
	// and find our spot

	var after = null;

	for(var i=0; i<siblingNodes.length; i++){

		var sibling_index = siblingNodes.item(i)["getAttribute"] ? parseInt(siblingNodes.item(i).getAttribute("dojoinsertionindex")) : -1;

		if (sibling_index < insertionIndex){
			after = siblingNodes.item(i);
		}
	}

	if (after){
		// add it after the node in {after}

		return dojo.dom.insertAfter(node, after);
	}else{
		// add it to the start

		return dojo.dom.insertBefore(node, siblingNodes.item(0));
	}
}
	
/**
 * implementation of the DOM Level 3 attribute.
 * 
 * @param node The node to scan for text
 * @param text Optional, set the text to this value.
 */
dojo.dom.textContent = function(node, text){
	if (text) {
		dojo.dom.replaceChildren(node, document.createTextNode(text));
		return text;
	} else {
		var _result = "";
		if (node == null) { return _result; }
		for (var i = 0; i < node.childNodes.length; i++) {
			switch (node.childNodes[i].nodeType) {
				case 1: // ELEMENT_NODE
				case 5: // ENTITY_REFERENCE_NODE
					_result += dojo.dom.textContent(node.childNodes[i]);
					break;
				case 3: // TEXT_NODE
				case 2: // ATTRIBUTE_NODE
				case 4: // CDATA_SECTION_NODE
					_result += node.childNodes[i].nodeValue;
					break;
				default:
					break;
			}
		}
		return _result;
	}
}

dojo.dom.collectionToArray = function(collection){
	dojo.deprecated("dojo.dom.collectionToArray", "use dojo.lang.toArray instead", "0.4");
	return dojo.lang.toArray(collection);
}

dojo.dom.hasParent = function (node) {
	return node && node.parentNode && dojo.dom.isNode(node.parentNode);
}

/**
 * Determines if node has any of the provided tag names and
 * returns the tag name that matches, empty string otherwise.
 *
 * Examples:
 *
 * myFooNode = <foo />
 * isTag(myFooNode, "foo"); // returns "foo"
 * isTag(myFooNode, "bar"); // returns ""
 * isTag(myFooNode, "FOO"); // returns ""
 * isTag(myFooNode, "hey", "foo", "bar"); // returns "foo"
**/
dojo.dom.isTag = function(node /* ... */) {
	if(node && node.tagName) {
		var arr = dojo.lang.toArray(arguments, 1);
		return arr[ dojo.lang.find(node.tagName, arr) ] || "";
	}
	return "";
}

dojo.provide("dojo.undo.browser");
dojo.require("dojo.io");

try{
	if((!djConfig["preventBackButtonFix"])&&(!dojo.hostenv.post_load_)){
		document.write("<iframe style='border: 0px; width: 1px; height: 1px; position: absolute; bottom: 0px; right: 0px; visibility: visible;' name='djhistory' id='djhistory' src='"+(dojo.hostenv.getBaseScriptUri()+'iframe_history.html')+"'></iframe>");
	}
}catch(e){/* squelch */}

/* NOTES:
 *  Safari 1.2: 
 *	back button "works" fine, however it's not possible to actually
 *	DETECT that you've moved backwards by inspecting window.location.
 *	Unless there is some other means of locating.
 *	FIXME: perhaps we can poll on history.length?
 *  Safari 2.0.3+ (and probably 1.3.2+):
 *	works fine, except when changeUrl is used. When changeUrl is used,
 *	Safari jumps all the way back to whatever page was shown before
 *	the page that uses dojo.undo.browser support.
 *  IE 5.5 SP2:
 *	back button behavior is macro. It does not move back to the
 *	previous hash value, but to the last full page load. This suggests
 *	that the iframe is the correct way to capture the back button in
 *	these cases.
 *	Don't test this page using local disk for MSIE. MSIE will not create 
 *	a history list for iframe_history.html if served from a file: URL. 
 *	The XML served back from the XHR tests will also not be properly 
 *	created if served from local disk. Serve the test pages from a web 
 *	server to test in that browser.
 *  IE 6.0:
 *	same behavior as IE 5.5 SP2
 * Firefox 1.0:
 *	the back button will return us to the previous hash on the same
 *	page, thereby not requiring an iframe hack, although we do then
 *	need to run a timer to detect inter-page movement.
 */
dojo.undo.browser = {
	initialHref: window.location.href,
	initialHash: window.location.hash,

	moveForward: false,
	historyStack: [],
	forwardStack: [],
	historyIframe: null,
	bookmarkAnchor: null,
	locationTimer: null,

	/**
	 * setInitialState sets the state object and back callback for the very first page that is loaded.
	 * It is recommended that you call this method as part of an event listener that is registered via
	 * dojo.addOnLoad().
	 */
	setInitialState: function(args){
		this.initialState = {"url": this.initialHref, "kwArgs": args, "urlHash": this.initialHash};
	},

	//FIXME: Would like to support arbitrary back/forward jumps. Have to rework iframeLoaded among other things.
	//FIXME: is there a slight race condition in moz using change URL with the timer check and when
	//       the hash gets set? I think I have seen a back/forward call in quick succession, but not consistent.
	/**
	 * addToHistory takes one argument, and it is an object that defines the following functions:
	 * - To support getting back button notifications, the object argument should implement a
	 *   function called either "back", "backButton", or "handle". The string "back" will be
	 *   passed as the first and only argument to this callback.
	 * - To support getting forward button notifications, the object argument should implement a
	 *   function called either "forward", "forwardButton", or "handle". The string "forward" will be
	
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.7u...	y..."!Q.....
........ *   passed as the first and only argument to this callback.
	 * - If you want the browser location string to change, define "changeUrl" on the object. If the
	 *   value of "changeUrl" is true, then a unique number will be appended to the URL as a fragment
	 *   identifier (http://some.domain.com/path#uniquenumber). If it is any other value that does
	 *   not evaluate to false, that value will be used as the fragment identifier. For example,
	 *   if changeUrl: 'page1', then the URL will look like: http://some.domain.com/path#page1
	 *   
	 * Full example:
	 * 
	 * dojo.undo.browser.addToHistory({
	 *   back: function() { alert('back pressed'); },
	 *   forward: function() { alert('forward pressed'); },
	 *   changeUrl: true
	 * });
	 */
	addToHistory: function(args){
		var hash = null;
		if(!this.historyIframe){
			this.historyIframe = window.frames["djhistory"];
		}
		if(!this.bookmarkAnchor){
			this.bookmarkAnchor = document.createElement("a");
			(document.body||document.getElementsByTagName("body")[0]).appendChild(this.bookmarkAnchor);
			this.bookmarkAnchor.style.display = "none";
		}
		if((!args["changeUrl"])||(dojo.render.html.ie)){
			var url = dojo.hostenv.getBaseScriptUri()+"iframe_history.html?"+(new Date()).getTime();
			this.moveForward = true;
			dojo.io.setIFrameSrc(this.historyIframe, url, false);
		}
		if(args["changeUrl"]){
			this.changingUrl = true;
			hash = "#"+ ((args["changeUrl"]!==true) ? args["changeUrl"] : (new Date()).getTime());
			setTimeout("window.location.href = '"+hash+"'; dojo.undo.browser.changingUrl = false;", 1);
			this.bookmarkAnchor.href = hash;
			
			if(dojo.render.html.ie){
				var oldCB = args["back"]||args["backButton"]||args["handle"];

				//The function takes handleName as a parameter, in case the
				//callback we are overriding was "handle". In that case,
				//we will need to pass the handle name to handle.
				var tcb = function(handleName){
					if(window.location.hash != ""){
						setTimeout("window.location.href = '"+hash+"';", 1);
					}
					//Use apply to set "this" to args, and to try to avoid memory leaks.
					oldCB.apply(this, [handleName]);
				}
		
				//Set interceptor function in the right place.
				if(args["back"]){
					args.back = tcb;
				}else if(args["backButton"]){
					args.backButton = tcb;
				}else if(args["handle"]){
					args.handle = tcb;
				}
		
				//If addToHistory is called, then that means we prune the
				//forward stack -- the user went back, then wanted to
				//start a new forward path.
				this.forwardStack = []; 
				var oldFW = args["forward"]||args["forwardButton"]||args["handle"];
		
				//The function takes handleName as a parameter, in case the
				//callback we are overriding was "handle". In that case,
				//we will need to pass the handle name to handle.
				var tfw = function(handleName){
					if(window.location.hash != ""){
						window.location.href = hash;
					}
					if(oldFW){ // we might not actually have one
						//Use apply to set "this" to args, and to try to avoid memory leaks.
						oldFW.apply(this, [handleName]);
					}
				}

				//Set interceptor function in the right place.
				if(args["forward"]){
					args.forward = tfw;
				}else if(args["forwardButton"]){
					args.forwardButton = tfw;
				}else if(args["handle"]){
					args.handle = tfw;
				}

			}else if(dojo.render.html.moz){
				// start the timer
				if(!this.locationTimer){
					this.locationTimer = setInterval("dojo.undo.browser.checkLocation();", 200);
				}
			}
		}

		this.historyStack.push({"url": url, "kwArgs": args, "urlHash": hash});
	},

	checkLocation: function(){
		if (!this.changingUrl){
			var hsl = this.historyStack.length;
	
			if((window.location.hash == this.initialHash)||(window.location.href == this.initialHref)&&(hsl == 1)){
				// FIXME: could this ever be a forward button?
				// we can't clear it because we still need to check for forwards. Ugg.
				// clearInterval(this.locationTimer);
				this.handleBackButton();
				return;
			}
			// first check to see if we could have gone forward. We always halt on
			// a no-hash item.
			if(this.forwardStack.length > 0){
				if(this.forwardStack[this.forwardStack.length-1].urlHash == window.location.hash){
					this.handleForwardButton();
					return;
				}
			}
	
			// ok, that didn't work, try someplace back in the history stack
			if((hsl >= 2)&&(this.historyStack[hsl-2])){
				if(this.historyStack[hsl-2].urlHash==window.location.hash){
					this.handleBackButton();
					return;
				}
			}
		}
	},

	iframeLoaded: function(evt, ifrLoc){
		var query = this._getUrlQuery(ifrLoc.href);
		if(query == null){ 
			// alert("iframeLoaded");
			// we hit the end of the history, so we should go back
			if(this.historyStack.length == 1){
				this.handleBackButton();
			}
			return;
		}
		if(this.moveForward){
			// we were expecting it, so it's not either a forward or backward movement
			this.moveForward = false;
			return;
		}

		//Check the back stack first, since it is more likely.
		//Note that only one step back or forward is supported.
		if(this.historyStack.length >= 2 && query == this._getUrlQuery(this.historyStack[this.historyStack.length-2].url)){
			this.handleBackButton();
		}
		else if(this.forwardStack.length > 0 && query == this._getUrlQuery(this.forwardStack[this.forwardStack.length-1].url)){
			this.handleForwardButton();
		}
	},

	handleBackButton: function(){
		//The "current" page is always at the top of the history stack.
		var current = this.historyStack.pop();
		if(!current){ return; }
		var last = this.historyStack[this.historyStack.length-1];
		if(!last && this.historyStack.length == 0){
			last = this.initialState;
		}
		if (last){
			if(last.kwArgs["back"]){
				last.kwArgs["back"]();
			}else if(last.kwArgs["backButton"]){
				last.kwArgs["backButton"]();
			}else if(last.kwArgs["handle"]){
				last.kwArgs.handle("back");
			}
		}
		this.forwardStack.push(current);
	},

	handleForwardButton: function(){
		var last = this.forwardStack.pop();
		if(!last){ return; }
		if(last.kwArgs["forward"]){
			last.kwArgs.forward();
		}else if(last.kwArgs["forwardButton"]){
			last.kwArgs.forwardButton();
		}else if(last.kwArgs["handle"]){
			last.kwArgs.handle("forward");
		}
		this.historyStack.push(last);
	},

	_getUrlQuery: function(url){
		var segments = url.split("?");
		if (segments.length < 2){
			return null;
		}
		else{
			return segments[1];
		}
	}
}

dojo.provide("dojo.io.BrowserIO");

dojo.require("dojo.io");
dojo.require("dojo.lang.array");
dojo.require("dojo.lang.func");
dojo.require("dojo.string.extras");
dojo.require("dojo.dom");
dojo.require("dojo.undo.browser");

dojo.io.checkChildrenForFile = function(node){
	var hasFile = false;
	var inputs = node.getElementsByTagName("input");
	dojo.lang.forEach(inputs, function(input){
		if(hasFile){ return; }
		if(input.getAttribute("type")=="file"){
			hasFile = true;
		}
	});
	return hasFile;
}

dojo.io.formHasFile = function(formNode){
	return dojo.io.checkChildrenForFile(formNode);
}

dojo.io.updateNode = function(node, urlOrArgs){
	node = dojo.byId(node);
	var args = urlOrArgs;
	if(dojo.lang.isString(urlOrArgs)){
		args = { url: urlOrArgs };
	}
	args.mimetype = "text/html";
	args.load = function(t, d, e){
		while(node.firstChild){
			if(dojo["event"]){
				try{
					dojo.event.browser.clean(node.firstChild);
				}catch(e){}
			}
			node.removeChild(node.firstChild);
		}
		node.innerHTML = d;
	};
	dojo.io.bind(args);
}

dojo.io.formFilter = function(node) {
	var type = (node.type||"").toLowerCase();
	return !node.disabled && node.name
		&& !dojo.lang.inArray(type, ["file", "submit", "image", "reset", "button"]);
}

// TODO: Move to htmlUtils
dojo.io.encodeForm = function(formNode, encoding, formFilter){
	if((!formNode)||(!formNode.tagName)||(!formNode.tagName.toLowerCase() == "form")){
		dojo.raise("Attempted to encode a non-form element.");
	}
	if(!formFilter) { formFilter = dojo.io.formFilter; }
	var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
	var values = [];

	for(var i = 0; i < formNode.elements.length; i++){
		var elm = formNode.elements[i];
		if(!elm || elm.tagName.toLowerCase() == "fieldset" || !formFilter(elm)) { continue; }
		var name = enc(elm.name);
		var type = elm.type.toLowerCase();

		if(type == "select-multiple"){
			for(var j = 0; j < elm.options.length; j++){
				if(elm.options[j].selected) {
					values.push(name + "=" + enc(elm.options[j].value));
				}
			}
		}else if(dojo.lang.inArray(type, ["radio", "checkbox"])){
			if(elm.checked){
				values.push(name + "=" + enc(elm.value));
			}
		}else{
			values.push(name + "=" + enc(elm.value));
		}
	}

	// now collect input type="image", which doesn't show up in the elements array
	var inputs = formNode.getElementsByTagName("input");
	for(var i = 0; i < inputs.length; i++) {
		var input = inputs[i];
		if(input.type.toLowerCase() == "image" && input.form == formNode
			&& formFilter(input)) {
			var name = enc(inpu
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.7....	y..."!Q.....
...)....t.name);
			values.push(name + "=" + enc(input.value));
			values.push(name + ".x=0");
			values.push(name + ".y=0");
		}
	}
	return values.join("&") + "&";
}

dojo.io.FormBind = function(args) {
	this.bindArgs = {};

	if(args && args.formNode) {
		this.init(args);
	} else if(args) {
		this.init({formNode: args});
	}
}
dojo.lang.extend(dojo.io.FormBind, {
	form: null,

	bindArgs: null,

	clickedButton: null,

	init: function(args) {
		var form = dojo.byId(args.formNode);

		if(!form || !form.tagName || form.tagName.toLowerCase() != "form") {
			throw new Error("FormBind: Couldn't apply, invalid form");
		} else if(this.form == form) {
			return;
		} else if(this.form) {
			throw new Error("FormBind: Already applied to a form");
		}

		dojo.lang.mixin(this.bindArgs, args);
		this.form = form;

		this.connect(form, "onsubmit", "submit");

		for(var i = 0; i < form.elements.length; i++) {
			var node = form.elements[i];
			if(node && node.type && dojo.lang.inArray(node.type.toLowerCase(), ["submit", "button"])) {
				this.connect(node, "onclick", "click");
			}
		}

		var inputs = form.getElementsByTagName("input");
		for(var i = 0; i < inputs.length; i++) {
			var input = inputs[i];
			if(input.type.toLowerCase() == "image" && input.form == form) {
				this.connect(input, "onclick", "click");
			}
		}
	},

	onSubmit: function(form) {
		return true;
	},

	submit: function(e) {
		e.preventDefault();
		if(this.onSubmit(this.form)) {
			dojo.io.bind(dojo.lang.mixin(this.bindArgs, {
				formFilter: dojo.lang.hitch(this, "formFilter")
			}));
		}
	},

	click: function(e) {
		var node = e.currentTarget;
		if(node.disabled) { return; }
		this.clickedButton = node;
	},

	formFilter: function(node) {
		var type = (node.type||"").toLowerCase();
		var accept = false;
		if(node.disabled || !node.name) {
			accept = false;
		} else if(dojo.lang.inArray(type, ["submit", "button", "image"])) {
			if(!this.clickedButton) { this.clickedButton = node; }
			accept = node == this.clickedButton;
		} else {
			accept = !dojo.lang.inArray(type, ["file", "submit", "reset", "button"]);
		}
		return accept;
	},

	// in case you don't have dojo.event.* pulled in
	connect: function(srcObj, srcFcn, targetFcn) {
		if(dojo.evalObjPath("dojo.event.connect")) {
			dojo.event.connect(srcObj, srcFcn, this, targetFcn);
		} else {
			var fcn = dojo.lang.hitch(this, targetFcn);
			srcObj[srcFcn] = function(e) {
				if(!e) { e = window.event; }
				if(!e.currentTarget) { e.currentTarget = e.srcElement; }
				if(!e.preventDefault) { e.preventDefault = function() { window.event.returnValue = false; } }
				fcn(e);
			}
		}
	}
});

dojo.io.XMLHTTPTransport = new function(){
	var _this = this;

	var _cache = {}; // FIXME: make this public? do we even need to?
	this.useCache = false; // if this is true, we'll cache unless kwArgs.useCache = false
	this.preventCache = false; // if this is true, we'll always force GET requests to cache

	// FIXME: Should this even be a function? or do we just hard code it in the next 2 functions?
	function getCacheKey(url, query, method) {
		return url + "|" + query + "|" + method.toLowerCase();
	}

	function addToCache(url, query, method, http) {
		_cache[getCacheKey(url, query, method)] = http;
	}

	function getFromCache(url, query, method) {
		return _cache[getCacheKey(url, query, method)];
	}

	this.clearCache = function() {
		_cache = {};
	}

	// moved successful load stuff here
	function doLoad(kwArgs, http, url, query, useCache) {
		if((http.status==200)||(http.status==304)||(http.status==204)||
			(location.protocol=="file:" && (http.status==0 || http.status==undefined))||
			(location.protocol=="chrome:" && (http.status==0 || http.status==undefined))
		){
			var ret;
			if(kwArgs.method.toLowerCase() == "head"){
				var headers = http.getAllResponseHeaders();
				ret = {};
				ret.toString = function(){ return headers; }
				var values = headers.split(/[\r\n]+/g);
				for(var i = 0; i < values.length; i++) {
					var pair = values[i].match(/^([^:]+)\s*:\s*(.+)$/i);
					if(pair) {
						ret[pair[1]] = pair[2];
					}
				}
			}else if(kwArgs.mimetype == "text/javascript"){
				try{
					ret = dj_eval(http.responseText);
				}catch(e){
					dojo.debug(e);
					dojo.debug(http.responseText);
					ret = null;
				}
			}else if(kwArgs.mimetype == "text/json"){
				try{
					ret = dj_eval("("+http.responseText+")");
				}catch(e){
					dojo.debug(e);
					dojo.debug(http.responseText);
					ret = false;
				}
			}else if((kwArgs.mimetype == "application/xml")||
						(kwArgs.mimetype == "text/xml")){
				ret = http.responseXML;
				if(!ret || typeof ret == "string") {
					ret = dojo.dom.createDocumentFromText(http.responseText);
				}
			}else{
				ret = http.responseText;
			}

			if(useCache){ // only cache successful responses
				addToCache(url, query, kwArgs.method, http);
			}
			kwArgs[(typeof kwArgs.load == "function") ? "load" : "handle"]("load", ret, http, kwArgs);
		}else{
			var errObj = new dojo.io.Error("XMLHttpTransport Error: "+http.status+" "+http.statusText);
			kwArgs[(typeof kwArgs.error == "function") ? "error" : "handle"]("error", errObj, http, kwArgs);
		}
	}

	// set headers (note: Content-Type will get overriden if kwArgs.contentType is set)
	function setHeaders(http, kwArgs){
		if(kwArgs["headers"]) {
			for(var header in kwArgs["headers"]) {
				if(header.toLowerCase() == "content-type" && !kwArgs["contentType"]) {
					kwArgs["contentType"] = kwArgs["headers"][header];
				} else {
					http.setRequestHeader(header, kwArgs["headers"][header]);
				}
			}
		}
	}

	this.inFlight = [];
	this.inFlightTimer = null;

	this.startWatchingInFlight = function(){
		if(!this.inFlightTimer){
			this.inFlightTimer = setInterval("dojo.io.XMLHTTPTransport.watchInFlight();", 10);
		}
	}

	this.watchInFlight = function(){
		var now = null;
		for(var x=this.inFlight.length-1; x>=0; x--){
			var tif = this.inFlight[x];
			if(!tif){ this.inFlight.splice(x, 1); continue; }
			if(4==tif.http.readyState){
				// remove it so we can clean refs
				this.inFlight.splice(x, 1);
				doLoad(tif.req, tif.http, tif.url, tif.query, tif.useCache);
			}else if (tif.startTime){
				//See if this is a timeout case.
				if(!now){
					now = (new Date()).getTime();
				}
				if(tif.startTime + (tif.req.timeoutSeconds * 1000) < now){
					//Stop the request.
					if(typeof tif.http.abort == "function"){
						tif.http.abort();
					}

					// remove it so we can clean refs
					this.inFlight.splice(x, 1);
					tif.req[(typeof tif.req.timeout == "function") ? "timeout" : "handle"]("timeout", null, tif.http, tif.req);
				}
			}
		}

		if(this.inFlight.length == 0){
			clearInterval(this.inFlightTimer);
			this.inFlightTimer = null;
		}
	}

	var hasXmlHttp = dojo.hostenv.getXmlhttpObject() ? true : false;
	this.canHandle = function(kwArgs){
		// canHandle just tells dojo.io.bind() if this is a good transport to
		// use for the particular type of request.

		// FIXME: we need to determine when form values need to be
		// multi-part mime encoded and avoid using this transport for those
		// requests.
		return hasXmlHttp
			&& dojo.lang.inArray((kwArgs["mimetype"].toLowerCase()||""), ["text/plain", "text/html", "application/xml", "text/xml", "text/javascript", "text/json"])
			&& !( kwArgs["formNode"] && dojo.io.formHasFile(kwArgs["formNode"]) );
	}

	this.multipartBoundary = "45309FFF-BD65-4d50-99C9-36986896A96F";	// unique guid as a boundary value for multipart posts

	this.bind = function(kwArgs){
		if(!kwArgs["url"]){
			// are we performing a history action?
			if( !kwArgs["formNode"]
				&& (kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"] || kwArgs["watchForURL"])
				&& (!djConfig.preventBackButtonFix)) {
        dojo.deprecated("Using dojo.io.XMLHTTPTransport.bind() to add to browser history without doing an IO request is deprecated. Use dojo.undo.browser.addToHistory() instead.");
				dojo.undo.browser.addToHistory(kwArgs);
				return true;
			}
		}

		// build this first for cache purposes
		var url = kwArgs.url;
		var query = "";
		if(kwArgs["formNode"]){
			var ta = kwArgs.formNode.getAttribute("action");
			if((ta)&&(!kwArgs["url"])){ url = ta; }
			var tp = kwArgs.formNode.getAttribute("method");
			if((tp)&&(!kwArgs["method"])){ kwArgs.method = tp; }
			query += dojo.io.encodeForm(kwArgs.formNode, kwArgs.encoding, kwArgs["formFilter"]);
		}

		if(url.indexOf("#") > -1) {
			dojo.debug("Warning: dojo.io.bind: stripping hash values from url:", url);
			url = url.split("#")[0];
		}

		if(kwArgs["file"]){
			// force post for file transfer
			kwArgs.method = "post";
		}

		if(!kwArgs["method"]){
			kwArgs.method = "get";
		}

		// guess the multipart value		
		if(kwArgs.method.toLowerCase() == "get"){
			// GET cannot use multipart
			kwArgs.multipart = false;
		}else{
			if(kwArgs["file"]){
				// enforce multipart when sending files
				kwArgs.multipart = true;
			}else if(!kwArgs["multipart"]){
				// default 
				kwArgs.multip
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.7.$..	y..."!Q.....
...[...)art = false;
			}
		}

		if(kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"]){
			dojo.undo.browser.addToHistory(kwArgs);
		}

		var content = kwArgs["content"] || {};

		if(kwArgs.sendTransport) {
			content["dojo.transport"] = "xmlhttp";
		}

		do { // break-block
			if(kwArgs.postContent){
				query = kwArgs.postContent;
				break;
			}

			if(content) {
				query += dojo.io.argsFromMap(content, kwArgs.encoding);
			}
			
			if(kwArgs.method.toLowerCase() == "get" || !kwArgs.multipart){
				break;
			}

			var	t = [];
			if(query.length){
				var q = query.split("&");
				for(var i = 0; i < q.length; ++i){
					if(q[i].length){
						var p = q[i].split("=");
						t.push(	"--" + this.multipartBoundary,
								"Content-Disposition: form-data; name=\"" + p[0] + "\"", 
								"",
								p[1]);
					}
				}
			}

			if(kwArgs.file){
				if(dojo.lang.isArray(kwArgs.file)){
					for(var i = 0; i < kwArgs.file.length; ++i){
						var o = kwArgs.file[i];
						t.push(	"--" + this.multipartBoundary,
								"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
								"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
								"",
								o.content);
					}
				}else{
					var o = kwArgs.file;
					t.push(	"--" + this.multipartBoundary,
							"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
							"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
							"",
							o.content);
				}
			}

			if(t.length){
				t.push("--"+this.multipartBoundary+"--", "");
				query = t.join("\r\n");
			}
		}while(false);

		// kwArgs.Connection = "close";

		var async = kwArgs["sync"] ? false : true;

		var preventCache = kwArgs["preventCache"] ||
			(this.preventCache == true && kwArgs["preventCache"] != false);
		var useCache = kwArgs["useCache"] == true ||
			(this.useCache == true && kwArgs["useCache"] != false );

		// preventCache is browser-level (add query string junk), useCache
		// is for the local cache. If we say preventCache, then don't attempt
		// to look in the cache, but if useCache is true, we still want to cache
		// the response
		if(!preventCache && useCache){
			var cachedHttp = getFromCache(url, query, kwArgs.method);
			if(cachedHttp){
				doLoad(kwArgs, cachedHttp, url, query, false);
				return;
			}
		}

		// much of this is from getText, but reproduced here because we need
		// more flexibility
		var http = dojo.hostenv.getXmlhttpObject(kwArgs);	
		var received = false;

		// build a handler function that calls back to the handler obj
		if(async){
			var startTime = 
			// FIXME: setting up this callback handler leaks on IE!!!
			this.inFlight.push({
				"req":		kwArgs,
				"http":		http,
				"url":	 	url,
				"query":	query,
				"useCache":	useCache,
				"startTime": kwArgs.timeoutSeconds ? (new Date()).getTime() : 0
			});
			this.startWatchingInFlight();
		}

		if(kwArgs.method.toLowerCase() == "post"){
			// FIXME: need to hack in more flexible Content-Type setting here!
			http.open("POST", url, async);
			setHeaders(http, kwArgs);
			http.setRequestHeader("Content-Type", kwArgs.multipart ? ("multipart/form-data; boundary=" + this.multipartBoundary) : 
				(kwArgs.contentType || "application/x-www-form-urlencoded"));
			try{
				http.send(query);
			}catch(e){
				if(typeof http.abort == "function"){
					http.abort();
				}
				doLoad(kwArgs, {status: 404}, url, query, useCache);
			}
		}else{
			var tmpUrl = url;
			if(query != "") {
				tmpUrl += (tmpUrl.indexOf("?") > -1 ? "&" : "?") + query;
			}
			if(preventCache) {
				tmpUrl += (dojo.string.endsWithAny(tmpUrl, "?", "&")
					? "" : (tmpUrl.indexOf("?") > -1 ? "&" : "?")) + "dojo.preventCache=" + new Date().valueOf();
			}
			http.open(kwArgs.method.toUpperCase(), tmpUrl, async);
			setHeaders(http, kwArgs);
			try {
				http.send(null);
			}catch(e)	{
				if(typeof http.abort == "function"){
					http.abort();
				}
				doLoad(kwArgs, {status: 404}, url, query, useCache);
			}
		}

		if( !async ) {
			doLoad(kwArgs, http, url, query, useCache);
		}

		kwArgs.abort = function(){
			return http.abort();
		}

		return;
	}
	dojo.io.transports.addTransport("XMLHTTPTransport");
}

dojo.provide("dojo.event");

dojo.require("dojo.lang.array");
dojo.require("dojo.lang.extras");
dojo.require("dojo.lang.func");

dojo.event = new function(){
	this.canTimeout = dojo.lang.isFunction(dj_global["setTimeout"])||dojo.lang.isAlien(dj_global["setTimeout"]);

	// FIXME: where should we put this method (not here!)?
	function interpolateArgs(args){
		var dl = dojo.lang;
		var ao = {
			srcObj: dj_global,
			srcFunc: null,
			adviceObj: dj_global,
			adviceFunc: null,
			aroundObj: null,
			aroundFunc: null,
			adviceType: (args.length>2) ? args[0] : "after",
			precedence: "last",
			once: false,
			delay: null,
			rate: 0,
			adviceMsg: false
		};

		switch(args.length){
			case 0: return;
			case 1: return;
			case 2:
				ao.srcFunc = args[0];
				ao.adviceFunc = args[1];
				break;
			case 3:
				if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isString(args[2]))){
					ao.adviceType = "after";
					ao.srcObj = args[0];
					ao.srcFunc = args[1];
					ao.adviceFunc = args[2];
				}else if((dl.isString(args[1]))&&(dl.isString(args[2]))){
					ao.srcFunc = args[1];
					ao.adviceFunc = args[2];
				}else if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isFunction(args[2]))){
					ao.adviceType = "after";
					ao.srcObj = args[0];
					ao.srcFunc = args[1];
					var tmpName  = dojo.lang.nameAnonFunc(args[2], ao.adviceObj);
					ao.adviceFunc = tmpName;
				}else if((dl.isFunction(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))){
					ao.adviceType = "after";
					ao.srcObj = dj_global;
					var tmpName  = dojo.lang.nameAnonFunc(args[0], ao.srcObj);
					ao.srcFunc = tmpName;
					ao.adviceObj = args[1];
					ao.adviceFunc = args[2];
				}
				break;
			case 4:
				if((dl.isObject(args[0]))&&(dl.isObject(args[2]))){
					// we can assume that we've got an old-style "connect" from
					// the sigslot school of event attachment. We therefore
					// assume after-advice.
					ao.adviceType = "after";
					ao.srcObj = args[0];
					ao.srcFunc = args[1];
					ao.adviceObj = args[2];
					ao.adviceFunc = args[3];
				}else if((dl.isString(args[0]))&&(dl.isString(args[1]))&&(dl.isObject(args[2]))){
					ao.adviceType = args[0];
					ao.srcObj = dj_global;
					ao.srcFunc = args[1];
					ao.adviceObj = args[2];
					ao.adviceFunc = args[3];
				}else if((dl.isString(args[0]))&&(dl.isFunction(args[1]))&&(dl.isObject(args[2]))){
					ao.adviceType = args[0];
					ao.srcObj = dj_global;
					var tmpName  = dojo.lang.nameAnonFunc(args[1], dj_global);
					ao.srcFunc = tmpName;
					ao.adviceObj = args[2];
					ao.adviceFunc = args[3];
				}else if((dl.isString(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))&&(dl.isFunction(args[3]))){
					ao.srcObj = args[1];
					ao.srcFunc = args[2];
					var tmpName  = dojo.lang.nameAnonFunc(args[3], dj_global);
					ao.adviceObj = dj_global;
					ao.adviceFunc = tmpName;
				}else if(dl.isObject(args[1])){
					ao.srcObj = args[1];
					ao.srcFunc = args[2];
					ao.adviceObj = dj_global;
					ao.adviceFunc = args[3];
				}else if(dl.isObject(args[2])){
					ao.srcObj = dj_global;
					ao.srcFunc = args[1];
					ao.adviceObj = args[2];
					ao.adviceFunc = args[3];
				}else{
					ao.srcObj = ao.adviceObj = ao.aroundObj = dj_global;
					ao.srcFunc = args[1];
					ao.adviceFunc = args[2];
					ao.aroundFunc = args[3];
				}
				break;
			case 6:
				ao.srcObj = args[1];
				ao.srcFunc = args[2];
				ao.adviceObj = args[3]
				ao.adviceFunc = args[4];
				ao.aroundFunc = args[5];
				ao.aroundObj = dj_global;
				break;
			default:
				ao.srcObj = args[1];
				ao.srcFunc = args[2];
				ao.adviceObj = args[3]
				ao.adviceFunc = args[4];
				ao.aroundObj = args[5];
				ao.aroundFunc = args[6];
				ao.once = args[7];
				ao.delay = args[8];
				ao.rate = args[9];
				ao.adviceMsg = args[10];
				break;
		}

		if(dl.isFunction(ao.aroundFunc)){
			var tmpName  = dojo.lang.nameAnonFunc(ao.aroundFunc, ao.aroundObj);
			ao.aroundFunc = tmpName;
		}

		if(!dl.isString(ao.srcFunc)){
			ao.srcFunc = dojo.lang.getNameInObj(ao.srcObj, ao.srcFunc);
		}

		if(!dl.isString(ao.adviceFunc)){
			ao.adviceFunc = dojo.lang.getNameInObj(ao.adviceObj, ao.adviceFunc);
		}

		if((ao.aroundObj)&&(!dl.isString(ao.aroundFunc))){
			ao.aroundFunc = dojo.lang.getNameInObj(ao.aroundObj, ao.aroundFunc);
		}

		if(!ao.srcObj){
			dojo.raise("bad srcObj for srcFunc: "+ao.srcFunc);
		}
		if(!ao.adviceObj){
			dojo.raise("bad adviceObj for adviceFunc: "+ao.adviceFunc);
		}
		return ao;
	}

	this.connect = function(){
		if(arguments.length == 1){
			var ao = arguments[0];
		}else{
			var ao = interpolateArgs(arguments);
		}

		if(dojo.lang.i
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.7.L..	y..."!Q.....
.......[sArray(ao.srcObj) && ao.srcObj!=""){
			var tmpAO = {};
			for(var x in ao){
				tmpAO[x] = ao[x];
			}
			var mjps = [];
			dojo.lang.forEach(ao.srcObj, function(src){
				if((dojo.render.html.capable)&&(dojo.lang.isString(src))){
					src = dojo.byId(src);
					// dojo.debug(src);
				}
				tmpAO.srcObj = src;
				// dojo.debug(tmpAO.srcObj, tmpAO.srcFunc);
				// dojo.debug(tmpAO.adviceObj, tmpAO.adviceFunc);
				mjps.push(dojo.event.connect.call(dojo.event, tmpAO));
			});
			return mjps;
		}

		// FIXME: just doing a "getForMethod()" seems to be enough to put this into infinite recursion!!
		var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc);
		if(ao.adviceFunc){
			var mjp2 = dojo.event.MethodJoinPoint.getForMethod(ao.adviceObj, ao.adviceFunc);
		}

		mjp.kwAddAdvice(ao);

		return mjp;	// advanced users might want to fsck w/ the join point
					// manually
	}

	this.log = function(a1, a2){
		var kwArgs;
		if((arguments.length == 1)&&(typeof a1 == "object")){
			kwArgs = a1;
		}else{
			kwArgs = {
				srcObj: a1,
				srcFunc: a2
			};
		}
		kwArgs.adviceFunc = function(){
			var argsStr = [];
			for(var x=0; x<arguments.length; x++){
				argsStr.push(arguments[x]);
			}
			dojo.debug("("+kwArgs.srcObj+")."+kwArgs.srcFunc, ":", argsStr.join(", "));
		}
		this.kwConnect(kwArgs);
	}

	this.connectBefore = function(){
		var args = ["before"];
		for(var i = 0; i < arguments.length; i++) { args.push(arguments[i]); }
		return this.connect.apply(this, args);
	}

	this.connectAround = function(){
		var args = ["around"];
		for(var i = 0; i < arguments.length; i++) { args.push(arguments[i]); }
		return this.connect.apply(this, args);
	}

	this.connectOnce = function(){
		var ao = interpolateArgs(arguments);
		ao.once = true;
		return this.connect(ao);
	}

	this._kwConnectImpl = function(kwArgs, disconnect){
		var fn = (disconnect) ? "disconnect" : "connect";
		if(typeof kwArgs["srcFunc"] == "function"){
			kwArgs.srcObj = kwArgs["srcObj"]||dj_global;
			var tmpName  = dojo.lang.nameAnonFunc(kwArgs.srcFunc, kwArgs.srcObj);
			kwArgs.srcFunc = tmpName;
		}
		if(typeof kwArgs["adviceFunc"] == "function"){
			kwArgs.adviceObj = kwArgs["adviceObj"]||dj_global;
			var tmpName  = dojo.lang.nameAnonFunc(kwArgs.adviceFunc, kwArgs.adviceObj);
			kwArgs.adviceFunc = tmpName;
		}
		return dojo.event[fn](	(kwArgs["type"]||kwArgs["adviceType"]||"after"),
									kwArgs["srcObj"]||dj_global,
									kwArgs["srcFunc"],
									kwArgs["adviceObj"]||kwArgs["targetObj"]||dj_global,
									kwArgs["adviceFunc"]||kwArgs["targetFunc"],
									kwArgs["aroundObj"],
									kwArgs["aroundFunc"],
									kwArgs["once"],
									kwArgs["delay"],
									kwArgs["rate"],
									kwArgs["adviceMsg"]||false );
	}

	this.kwConnect = function(kwArgs){
		return this._kwConnectImpl(kwArgs, false);

	}

	this.disconnect = function(){
		var ao = interpolateArgs(arguments);
		if(!ao.adviceFunc){ return; } // nothing to disconnect
		var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc);
		return mjp.removeAdvice(ao.adviceObj, ao.adviceFunc, ao.adviceType, ao.once);
	}

	this.kwDisconnect = function(kwArgs){
		return this._kwConnectImpl(kwArgs, true);
	}
}

// exactly one of these is created whenever a method with a joint point is run,
// if there is at least one 'around' advice.
dojo.event.MethodInvocation = function(join_point, obj, args) {
	this.jp_ = join_point;
	this.object = obj;
	this.args = [];
	for(var x=0; x<args.length; x++){
		this.args[x] = args[x];
	}
	// the index of the 'around' that is currently being executed.
	this.around_index = -1;
}

dojo.event.MethodInvocation.prototype.proceed = function() {
	this.around_index++;
	if(this.around_index >= this.jp_.around.length){
		return this.jp_.object[this.jp_.methodname].apply(this.jp_.object, this.args);
		// return this.jp_.run_before_after(this.object, this.args);
	}else{
		var ti = this.jp_.around[this.around_index];
		var mobj = ti[0]||dj_global;
		var meth = ti[1];
		return mobj[meth].call(mobj, this);
	}
} 


dojo.event.MethodJoinPoint = function(obj, methname){
	this.object = obj||dj_global;
	this.methodname = methname;
	this.methodfunc = this.object[methname];
	this.before = [];
	this.after = [];
	this.around = [];
}

dojo.event.MethodJoinPoint.getForMethod = function(obj, methname) {
	// if(!(methname in obj)){
	if(!obj){ obj = dj_global; }
	if(!obj[methname]){
		// supply a do-nothing method implementation
		obj[methname] = function(){};
	}else if((!dojo.lang.isFunction(obj[methname]))&&(!dojo.lang.isAlien(obj[methname]))){
		return null; // FIXME: should we throw an exception here instead?
	}
	// we hide our joinpoint instance in obj[methname + '$joinpoint']
	var jpname = methname + "$joinpoint";
	var jpfuncname = methname + "$joinpoint$method";
	var joinpoint = obj[jpname];
	if(!joinpoint){
		var isNode = false;
		if(dojo.event["browser"]){
			if( (obj["attachEvent"])||
				(obj["nodeType"])||
				(obj["addEventListener"]) ){
				isNode = true;
				dojo.event.browser.addClobberNodeAttrs(obj, [jpname, jpfuncname, methname]);
			}
		}
		var origArity = obj[methname].length;
		obj[jpfuncname] = obj[methname];
		// joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, methname);
		joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, jpfuncname);
		obj[methname] = function(){ 
			var args = [];

			if((isNode)&&(!arguments.length)){
				var evt = null;
				try{
					if(obj.ownerDocument){
						evt = obj.ownerDocument.parentWindow.event;
					}else if(obj.documentElement){
						evt = obj.documentElement.ownerDocument.parentWindow.event;
					}else{
						evt = window.event;
					}
				}catch(e){
					evt = window.event;
				}

				if(evt){
					args.push(dojo.event.browser.fixEvent(evt, this));
				}
			}else{
				for(var x=0; x<arguments.length; x++){
					if((x==0)&&(isNode)&&(dojo.event.browser.isEvent(arguments[x]))){
						args.push(dojo.event.browser.fixEvent(arguments[x], this));
					}else{
						args.push(arguments[x]);
					}
				}
			}
			// return joinpoint.run.apply(joinpoint, arguments); 
			return joinpoint.run.apply(joinpoint, args); 
		}
		obj[methname].__preJoinArity = origArity;
	}
	return joinpoint;
}

dojo.lang.extend(dojo.event.MethodJoinPoint, {
	unintercept: function(){
		this.object[this.methodname] = this.methodfunc;
		this.before = [];
		this.after = [];
		this.around = [];
	},

	disconnect: dojo.lang.forward("unintercept"),

	run: function() {
		var obj = this.object||dj_global;
		var args = arguments;

		// optimization. We only compute once the array version of the arguments
		// pseudo-arr in order to prevent building it each time advice is unrolled.
		var aargs = [];
		for(var x=0; x<args.length; x++){
			aargs[x] = args[x];
		}

		var unrollAdvice  = function(marr){ 
			if(!marr){
				dojo.debug("Null argument to unrollAdvice()");
				return;
			}
		  
			var callObj = marr[0]||dj_global;
			var callFunc = marr[1];
			
			if(!callObj[callFunc]){
				dojo.raise("function \"" + callFunc + "\" does not exist on \"" + callObj + "\"");
			}
			
			var aroundObj = marr[2]||dj_global;
			var aroundFunc = marr[3];
			var msg = marr[6];
			var undef;

			var to = {
				args: [],
				jp_: this,
				object: obj,
				proceed: function(){
					return callObj[callFunc].apply(callObj, to.args);
				}
			};
			to.args = aargs;

			var delay = parseInt(marr[4]);
			var hasDelay = ((!isNaN(delay))&&(marr[4]!==null)&&(typeof marr[4] != "undefined"));
			if(marr[5]){
				var rate = parseInt(marr[5]);
				var cur = new Date();
				var timerSet = false;
				if((marr["last"])&&((cur-marr.last)<=rate)){
					if(dojo.event.canTimeout){
						if(marr["delayTimer"]){
							clearTimeout(marr.delayTimer);
						}
						var tod = parseInt(rate*2); // is rate*2 naive?
						var mcpy = dojo.lang.shallowCopy(marr);
						marr.delayTimer = setTimeout(function(){
							// FIXME: on IE at least, event objects from the
							// browser can go out of scope. How (or should?) we
							// deal with it?
							mcpy[5] = 0;
							unrollAdvice(mcpy);
						}, tod);
					}
					return;
				}else{
					marr.last = cur;
				}
			}

			// FIXME: need to enforce rates for a connection here!

			if(aroundFunc){
				// NOTE: around advice can't delay since we might otherwise depend
				// on execution order!
				aroundObj[aroundFunc].call(aroundObj, to);
			}else{
				// var tmjp = dojo.event.MethodJoinPoint.getForMethod(obj, methname);
				if((hasDelay)&&((dojo.render.html)||(dojo.render.svg))){  // FIXME: the render checks are grotty!
					dj_global["setTimeout"](function(){
						if(msg){
							callObj[callFunc].call(callObj, to); 
						}else{
							callObj[callFunc].apply(callObj, args); 
						}
					}, delay);
				}else{ // many environments can't support delay!
					if(msg){
						callObj[callFunc].call(callObj, to); 
					}else{
						callObj[callFunc].apply(callObj, args); 
					}
				}
			}
		}

		if(this.before.length>0){
			dojo.lang
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.8.t..	y..."!Q.....
.........forEach(this.before, unrollAdvice);
		}

		var result;
		if(this.around.length>0){
			var mi = new dojo.event.MethodInvocation(this, obj, args);
			result = mi.proceed();
		}else if(this.methodfunc){
			result = this.object[this.methodname].apply(this.object, args);
		}

		if(this.after.length>0){
			dojo.lang.forEach(this.after, unrollAdvice);
		}

		return (this.methodfunc) ? result : null;
	},

	getArr: function(kind){
		var arr = this.after;
		// FIXME: we should be able to do this through props or Array.in()
		if((typeof kind == "string")&&(kind.indexOf("before")!=-1)){
			arr = this.before;
		}else if(kind=="around"){
			arr = this.around;
		}
		return arr;
	},

	kwAddAdvice: function(args){
		this.addAdvice(	args["adviceObj"], args["adviceFunc"], 
						args["aroundObj"], args["aroundFunc"], 
						args["adviceType"], args["precedence"], 
						args["once"], args["delay"], args["rate"], 
						args["adviceMsg"]);
	},

	addAdvice: function(	thisAdviceObj, thisAdvice, 
							thisAroundObj, thisAround, 
							advice_kind, precedence, 
							once, delay, rate, asMessage){
		var arr = this.getArr(advice_kind);
		if(!arr){
			dojo.raise("bad this: " + this);
		}

		var ao = [thisAdviceObj, thisAdvice, thisAroundObj, thisAround, delay, rate, asMessage];
		
		if(once){
			if(this.hasAdvice(thisAdviceObj, thisAdvice, advice_kind, arr) >= 0){
				return;
			}
		}

		if(precedence == "first"){
			arr.unshift(ao);
		}else{
			arr.push(ao);
		}
	},

	hasAdvice: function(thisAdviceObj, thisAdvice, advice_kind, arr){
		if(!arr){ arr = this.getArr(advice_kind); }
		var ind = -1;
		for(var x=0; x<arr.length; x++){
			if((arr[x][0] == thisAdviceObj)&&(arr[x][1] == thisAdvice)){
				ind = x;
			}
		}
		return ind;
	},

	removeAdvice: function(thisAdviceObj, thisAdvice, advice_kind, once){
		var arr = this.getArr(advice_kind);
		var ind = this.hasAdvice(thisAdviceObj, thisAdvice, advice_kind, arr);
		if(ind == -1){
			return false;
		}
		while(ind != -1){
			arr.splice(ind, 1);
			if(once){ break; }
			ind = this.hasAdvice(thisAdviceObj, thisAdvice, advice_kind, arr);
		}
		return true;
	}
});

dojo.require("dojo.event");
dojo.provide("dojo.event.topic");

dojo.event.topic = new function(){
	this.topics = {};

	this.getTopic = function(topicName){
		if(!this.topics[topicName]){
			this.topics[topicName] = new this.TopicImpl(topicName);
		}
		return this.topics[topicName];
	}

	this.registerPublisher = function(topic, obj, funcName){
		var topic = this.getTopic(topic);
		topic.registerPublisher(obj, funcName);
	}

	this.subscribe = function(topic, obj, funcName){
		var topic = this.getTopic(topic);
		topic.subscribe(obj, funcName);
	}

	this.unsubscribe = function(topic, obj, funcName){
		var topic = this.getTopic(topic);
		topic.unsubscribe(obj, funcName);
	}

	this.destroy = function(topic){
		this.getTopic(topic).destroy();
		delete this.topics[topic];
	}

	this.publish = function(topic, message){
		var topic = this.getTopic(topic);
		// if message is an array, we treat it as a set of arguments,
		// otherwise, we just pass on the arguments passed in as-is
		var args = [];
		if(arguments.length == 2 && (dojo.lang.isArray(message) || message.callee)){
			args = message;
		}else{
			var args = [];
			for(var x=1; x<arguments.length; x++){
				args.push(arguments[x]);
			}
		}
		topic.sendMessage.apply(topic, args);
	}
}

dojo.event.topic.TopicImpl = function(topicName){
	this.topicName = topicName;

	this.subscribe = function(listenerObject, listenerMethod){
		var tf = listenerMethod||listenerObject;
		var to = (!listenerMethod) ? dj_global : listenerObject;
		dojo.event.kwConnect({
			srcObj:		this, 
			srcFunc:	"sendMessage", 
			adviceObj:	to,
			adviceFunc: tf
		});
	}

	this.unsubscribe = function(listenerObject, listenerMethod){
		var tf = (!listenerMethod) ? listenerObject : listenerMethod;
		var to = (!listenerMethod) ? null : listenerObject;
		dojo.event.kwDisconnect({
			srcObj:		this, 
			srcFunc:	"sendMessage", 
			adviceObj:	to,
			adviceFunc: tf
		});
	}

	this.destroy = function(){
		dojo.event.MethodJoinPoint.getForMethod(this, "sendMessage").disconnect();
	}

	this.registerPublisher = function(publisherObject, publisherMethod){
		dojo.event.connect(publisherObject, publisherMethod, this, "sendMessage");
	}

	this.sendMessage = function(message){
		// The message has been propagated
	}
}


dojo.provide("dojo.event.browser");
dojo.require("dojo.event");

dojo_ie_clobber = new function(){
	this.clobberNodes = [];

	function nukeProp(node, prop){
		// try{ node.removeAttribute(prop); 	}catch(e){ /* squelch */ }
		try{ node[prop] = null; 			}catch(e){ /* squelch */ }
		try{ delete node[prop]; 			}catch(e){ /* squelch */ }
		// FIXME: JotLive needs this, but I'm not sure if it's too slow or not
		try{ node.removeAttribute(prop);	}catch(e){ /* squelch */ }
	}

	this.clobber = function(nodeRef){
		var na;
		var tna;
		if(nodeRef){
			tna = nodeRef.all || nodeRef.getElementsByTagName("*");
			na = [nodeRef];
			for(var x=0; x<tna.length; x++){
				// if we're gonna be clobbering the thing, at least make sure
				// we aren't trying to do it twice
				if(tna[x]["__doClobber__"]){
					na.push(tna[x]);
				}
			}
		}else{
			try{ window.onload = null; }catch(e){}
			na = (this.clobberNodes.length) ? this.clobberNodes : document.all;
		}
		tna = null;
		var basis = {};
		for(var i = na.length-1; i>=0; i=i-1){
			var el = na[i];
			if(el["__clobberAttrs__"]){
				for(var j=0; j<el.__clobberAttrs__.length; j++){
					nukeProp(el, el.__clobberAttrs__[j]);
				}
				nukeProp(el, "__clobberAttrs__");
				nukeProp(el, "__doClobber__");
			}
		}
		na = null;
	}
}

if(dojo.render.html.ie){
	window.onunload = function(){
		dojo_ie_clobber.clobber();
		try{
			if((dojo["widget"])&&(dojo.widget["manager"])){
				dojo.widget.manager.destroyAll();
			}
		}catch(e){}
		try{ window.onload = null; }catch(e){}
		try{ window.onunload = null; }catch(e){}
		dojo_ie_clobber.clobberNodes = [];
		// CollectGarbage();
	}
}

dojo.event.browser = new function(){

	var clobberIdx = 0;

	this.clean = function(node){
		if(dojo.render.html.ie){ 
			dojo_ie_clobber.clobber(node);
		}
	}

	this.addClobberNode = function(node){
		if(!node["__doClobber__"]){
			node.__doClobber__ = true;
			dojo_ie_clobber.clobberNodes.push(node);
			// this might not be the most efficient thing to do, but it's
			// much less error prone than other approaches which were
			// previously tried and failed
			node.__clobberAttrs__ = [];
		}
	}

	this.addClobberNodeAttrs = function(node, props){
		this.addClobberNode(node);
		for(var x=0; x<props.length; x++){
			node.__clobberAttrs__.push(props[x]);
		}
	}

	this.removeListener = function(node, evtName, fp, capture){
		if(!capture){ var capture = false; }
		evtName = evtName.toLowerCase();
		if(evtName.substr(0,2)=="on"){ evtName = evtName.substr(2); }
		// FIXME: this is mostly a punt, we aren't actually doing anything on IE
		if(node.removeEventListener){
			node.removeEventListener(evtName, fp, capture);
		}
	}

	this.addListener = function(node, evtName, fp, capture, dontFix){
		if(!node){ return; } // FIXME: log and/or bail?
		if(!capture){ var capture = false; }
		evtName = evtName.toLowerCase();
		if(evtName.substr(0,2)!="on"){ evtName = "on"+evtName; }

		if(!dontFix){
			// build yet another closure around fp in order to inject fixEvent
			// around the resulting event
			var newfp = function(evt){
				if(!evt){ evt = window.event; }
				var ret = fp(dojo.event.browser.fixEvent(evt, this));
				if(capture){
					dojo.event.browser.stopEvent(evt);
				}
				return ret;
			}
		}else{
			newfp = fp;
		}

		if(node.addEventListener){ 
			node.addEventListener(evtName.substr(2), newfp, capture);
			return newfp;
		}else{
			if(typeof node[evtName] == "function" ){
				var oldEvt = node[evtName];
				node[evtName] = function(e){
					oldEvt(e);
					return newfp(e);
				}
			}else{
				node[evtName]=newfp;
			}
			if(dojo.render.html.ie){
				this.addClobberNodeAttrs(node, [evtName]);
			}
			return newfp;
		}
	}

	this.isEvent = function(obj){
		// FIXME: event detection hack ... could test for additional attributes
		// if necessary
		return (typeof obj != "undefined")&&(typeof Event != "undefined")&&(obj.eventPhase);
		// Event does not support instanceof in Opera, otherwise:
		//return (typeof Event != "undefined")&&(obj instanceof Event);
	}

	this.currentEvent = null;
	
	this.callListener = function(listener, curTarget){
		if(typeof listener != 'function'){
			dojo.raise("listener not a function: " + listener);
		}
		dojo.event.browser.currentEvent.currentTarget = curTarget;
		return listener.call(curTarget, dojo.event.browser.currentEvent);
	}

	this.stopPropagation = function(){
		dojo.event.browser.currentEvent.cancelBubble = true;
	}

	this.preventDefault = function(){
	  dojo.event.browser.currentEvent.returnValue = false;
	}

	this.keys = {
		KEY_BACKSPACE: 8,
		KEY_TAB: 9,
		KEY_ENTER: 13,
		KEY_SHIFT: 16,
		KEY_CTRL
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 2460
E.	...@.@.>............`.8%...	y...".......
........: 17,
		KEY_ALT: 18,
		KEY_PAUSE: 19,
		KEY_CAPS_LOCK: 20,
		KEY_ESCAPE: 27,
		KEY_SPACE: 32,
		KEY_PAGE_UP: 33,
		KEY_PAGE_DOWN: 34,
		KEY_END: 35,
		KEY_HOME: 36,
		KEY_LEFT_ARROW: 37,
		KEY_UP_ARROW: 38,
		KEY_RIGHT_ARROW: 39,
		KEY_DOWN_ARROW: 40,
		KEY_INSERT: 45,
		KEY_DELETE: 46,
		KEY_LEFT_WINDOW: 91,
		KEY_RIGHT_WINDOW: 92,
		KEY_SELECT: 93,
		KEY_F1: 112,
		KEY_F2: 113,
		KEY_F3: 114,
		KEY_F4: 115,
		KEY_F5: 116,
		KEY_F6: 117,
		KEY_F7: 118,
		KEY_F8: 119,
		KEY_F9: 120,
		KEY_F10: 121,
		KEY_F11: 122,
		KEY_F12: 123,
		KEY_NUM_LOCK: 144,
		KEY_SCROLL_LOCK: 145
	};

	// reverse lookup
	this.revKeys = [];
	for(var key in this.keys){
		this.revKeys[this.keys[key]] = key;
	}

	this.fixEvent = function(evt, sender){
		if((!evt)&&(window["event"])){
			var evt = window.event;
		}
		
		if((evt["type"])&&(evt["type"].indexOf("key") == 0)){ // key events
			evt.keys = this.revKeys;
			// FIXME: how can we eliminate this iteration?
			for(var key in this.keys) {
				evt[key] = this.keys[key];
			}
			if((dojo.render.html.ie)&&(evt["type"] == "keypress")){
				evt.charCode = evt.keyCode;
			}
		}
	
		if(dojo.render.html.ie){
			if(!evt.target){ evt.target = evt.srcElement; }
			if(!evt.currentTarget){ evt.currentTarget = (sender ? sender : evt.srcElement); }
			if(!evt.layerX){ evt.layerX = evt.offsetX; }
			if(!evt.layerY){ evt.layerY = evt.offsetY; }
			// FIXME: scroll position query is duped from dojo.html to avoid dependency on that entire module
			if(!evt.pageX){ evt.pageX = evt.clientX + (window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0) }
			if(!evt.pageY){ evt.pageY = evt.clientY + (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0) }
			// mouseover
			if(evt.type == "mouseover"){ evt.relatedTarget = evt.fromElement; }
			// mouseout
			if(evt.type == "mouseout"){ evt.relatedTarget = evt.toElement; }
			this.currentEvent = evt;
			evt.callListener = this.callListener;
			evt.stopPropagation = this.stopPropagation;
			evt.preventDefault = this.preventDefault;
		}
		return evt;
	}

	this.stopEvent = function(ev) {
		if(window.event){
			ev.returnValue = false;
			ev.cancelBubble = true;
		}else{
			ev.preventDefault();
			ev.stopPropagation();
		}
	}
}

dojo.kwCompoundRequire({
	common: ["dojo.event", "dojo.event.topic"],
	browser: ["dojo.event.browser"],
	dashboard: ["dojo.event.browser"]
});
dojo.provide("dojo.event.*");


IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 437
E....0@.@............_....]..'.....+.......
...=....GET /scooby/js/lib/dojo-event_and_io/src/debug.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 2486
E.	.N.@.@.............._.'...._....6.......
...S...=HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"2274-1147389474000"
Last-Modified: Thu, 11 May 2006 23:17:54 GMT
Content-Type: text/javascript
Content-Length: 2274
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

/**
 * Produce a line of debug output. 
 * Does nothing unless djConfig.isDebug is true.
 * varargs, joined with ''.
 * Caller should not supply a trailing "\n".
 */
dojo.debug = function(){
	if (!djConfig.isDebug) { return; }
	var args = arguments;
	if(dj_undef("println", dojo.hostenv)){
		dojo.raise("dojo.debug not available (yet?)");
	}
	var isJUM = dj_global["jum"] && !dj_global["jum"].isBrowser;
	var s = [(isJUM ? "": "DEBUG: ")];
	for(var i=0;i<args.length;++i){
		if(!false && args[i] instanceof Error){
			var msg = "[" + args[i].name + ": " + dojo.errorToString(args[i]) +
				(args[i].fileName ? ", file: " + args[i].fileName : "") +
				(args[i].lineNumber ? ", line: " + args[i].lineNumber : "") + "]";
		} else {
			try {
				var msg = String(args[i]);
			} catch(e) {
				if(dojo.render.html.ie) {
					var msg = "[ActiveXObject]";
				} else {
					var msg = "[unknown]";
				}
			}
		}
		s.push(msg);
	}
	if(isJUM){ // this seems to be the only way to get JUM to "play nice"
		jum.debug(s.join(" "));
	}else{
		dojo.hostenv.println(s.join(" "));
	}
}

/**
 * this is really hacky for now - just 
 * display the properties of the object
**/

dojo.debugShallow = function(obj){
	if (!djConfig.isDebug) { return; }
	dojo.debug('------------------------------------------------------------');
	dojo.debug('Object: '+obj);
	var props = [];
	for(var prop in obj){
		try {
			props.push(prop + ': ' + obj[prop]);
		} catch(E) {
			props.push(prop + ': ERROR - ' + E.message);
		}
	}
	props.sort();
	for(var i = 0; i < props.length; i++) {
		dojo.debug(props[i]);
	}
	dojo.debug('------------------------------------------------------------');
}

dojo.debugDeep = function(obj){
	if (!djConfig.isDebug) { return; }
	if (!window.open){ return dojo.debug('Deep debugging is only supported in host environments with window.open'); }
	var win = window.open(dojo.uri.dojoUri("src/debug/deep.html"), '_blank', 'width=600, height=400, resizable=yes, scrollbars=yes, status=yes');
	win.debugVar = obj;
}

IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 497
E..%$.@.@..?.........[...~....$............
...o....GET /scooby/js/lib/dojo-event_and_io/src/widget/__package__.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 818
E..f6g@.@..)...........[..$..~.......[.....
.......oHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"608-1147389476000"
Last-Modified: Thu, 11 May 2006 23:17:56 GMT
Content-Type: text/javascript
Content-Length: 608
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.kwCompoundRequire({
	common: ["dojo.xml.Parse", 
			 "dojo.widget.Widget", 
			 "dojo.widget.Parse", 
			 "dojo.widget.Manager"],
	browser: ["dojo.widget.DomWidget",
			  "dojo.widget.HtmlWidget"],
	dashboard: ["dojo.widget.DomWidget",
			  "dojo.widget.HtmlWidget"],
	svg: 	 ["dojo.widget.SvgWidget"]
});
dojo.provide("dojo.widget.*");

IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 488
E....l@.@.Qm.........^...//;...<...........
........GET /scooby/js/lib/dojo-event_and_io/src/xml/Parse.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 6682
E..Nu.@.@..............^...<./1#...4.C.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"6470-1147389476000"
Last-Modified: Thu, 11 May 2006 23:17:56 GMT
Content-Type: text/javascript
Content-Length: 6470
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.xml.Parse");

dojo.require("dojo.dom");

//TODO: determine dependencies
// currently has dependency on dojo.xml.DomUtil nodeTypes constants...

/* generic method for taking a node and parsing it into an object

TODO: WARNING: This comment is wrong!

For example, the following xml fragment

<foo bar="bar">
	<baz xyzzy="xyzzy"/>
</foo>

can be described as:

dojo.???.foo = {}
dojo.???.foo.bar = {}
dojo.???.foo.bar.value = "bar";
dojo.???.foo.baz = {}
dojo.???.foo.baz.xyzzy = {}
dojo.???.foo.baz.xyzzy.value = "xyzzy"

*/
// using documentFragment nomenclature to generalize in case we don't want to require passing a collection of nodes with a single parent
dojo.xml.Parse = function(){

	function getDojoTagName (node) {
		var tagName = node.tagName;
		if (tagName.substr(0,5).toLowerCase() != "dojo:") {
			
			if (tagName.substr(0,4).toLowerCase() == "dojo") {
				// FIXME: this assuumes tag names are always lower case
				return "dojo:" + tagName.substring(4).toLowerCase();
			}
		
			// allow lower-casing
			var djt = node.getAttribute("dojoType") || node.getAttribute("dojotype");
			if (djt) { return "dojo:" + djt.toLowerCase(); }
			
			if (node.getAttributeNS && node.getAttributeNS(dojo.dom.dojoml,"type")) {
				return "dojo:" + node.getAttributeNS(dojo.dom.dojoml,"type").toLowerCase();
			}
			try {
				// FIXME: IE really really doesn't like this, so we squelch
				// errors for it
				djt = node.getAttribute("dojo:type");
			} catch (e) { /* FIXME: log? */ }

			if (djt) { return "dojo:"+djt.toLowerCase(); }
		
			if (!dj_global["djConfig"] || !djConfig["ignoreClassNames"]) {
				// FIXME: should we make this optionally enabled via djConfig?
				var classes = node.className||node.getAttribute("class");
				// FIXME: following line, without check for existence of classes.indexOf
				// breaks firefox 1.5's svg widgets
				if (classes && classes.indexOf && classes.indexOf("dojo-") != -1) {
					var aclasses = classes.split(" ");
					for(var x=0; x<aclasses.length; x++){
						if (aclasses[x].length > 5 && aclasses[x].indexOf("dojo-") >= 0) {
							return "dojo:"+aclasses[x].substr(5).toLowerCase();
						}
					}
				}
			}
		
		}
		return tagName.toLowerCase();
	}

	this.parseElement = function(node, hasParentNodeSet, optimizeForDojoML, thisIdx){

        // if parseWidgets="false" don't search inside this node for widgets
        if (node.getAttribute("parseWidgets") == "false") {
            return {};
        }

		// TODO: make this namespace aware
		var parsedNodeSet = {};

		var tagName = getDojoTagName(node);
		parsedNodeSet[tagName] = [];
		if((!optimizeForDojoML)||(tagName.substr(0,4).toLowerCase()=="dojo")){
			var attributeSet = parseAttributes(node);
			for(var attr in attributeSet){
				if((!parsedNodeSet[tagName][attr])||(typeof parsedNodeSet[tagName][attr] != "array")){
					parsedNodeSet[tagName][attr] = [];
				}
				parsedNodeSet[tagName][attr].push(attributeSet[attr]);
			}
	
			// FIXME: we might want to make this optional or provide cloning instead of
			// referencing, but for now, we include a node reference to allow
			// instantiated components to figure out their "roots"
			parsedNodeSet[tagName].nodeRef = node;
			parsedNodeSet.tagName = tagName;
			parsedNodeSet.index = thisIdx||0;
		}
	
		var count = 0;
		var tcn, i = 0, nodes = node.childNodes;
		while(tcn = nodes[i++]){
			switch(tcn.nodeType){
				case  dojo.dom.ELEMENT_NODE: // element nodes, call this function recursively
					count++;
					var ctn = getDojoTagName(tcn);
					if(!parsedNodeSet[ctn]){
						parsedNodeSet[ctn] = [];
					}
					parsedNodeSet[ctn].push(this.parseElement(tcn, true, optimizeForDojoML, count));
					if(	(tcn.childNodes.length == 1)&&
						(tcn.childNodes.item(0).nodeType == dojo.dom.TEXT_NODE)){
						parsedNodeSet[ctn][parsedNodeSet[ctn].length-1].value = tcn.childNodes.item(0).nodeValue;
					}
					break;
				case  dojo.dom.TEXT_NODE: // if a single text node is the child, treat it as an attribute
					if(node.childNodes.length == 1) {
						parsedNodeSet[tagName].push({ value: node.childNodes.item(0).nodeValue });
					}
					break;
				default: break;
				/*
				case  dojo.dom.ATTRIBUTE_NODE: // attribute node... not meaningful here
					break;
				case  dojo.dom.CDATA_SECTION_NODE: // cdata section... not sure if this would ever be meaningful... might be...
					break;
				case  dojo.dom.ENTITY_REFERENCE_NODE: // entity reference node... not meaningful here
					break;
				case  dojo.dom.ENTITY_NODE: // entity node... not sure if this would ever be meaningful
					break;
				case  dojo.dom.PROCESSING_INSTRUCTION_NODE: // processing instruction node... not meaningful here
					break;
				case  dojo.dom.COMMENT_NODE: // comment node... not not sure if this would ever be meaningful 
					break;
				case  dojo.dom.DOCUMENT_NODE: // document node... not sure if this would ever be meaningful
					break;
				case  dojo.dom.DOCUMENT_TYPE_NODE: // document type node... not meaningful here
					break;
				case  dojo.dom.DOCUMENT_FRAGMENT_NODE: // document fragment node... not meaningful here
					break;
				case  dojo.dom.NOTATION_NODE:// notation node... not meaningful here
					break;
				*/
			}
		}
		//return (hasParentNodeSet) ? parsedNodeSet[node.tagName] : parsedNodeSet;
		return parsedNodeSet;
	}

	/* parses a set of attributes on a node into an object tree */
	function parseAttributes(node) {
		// TODO: make this namespace aware
		var parsedAttributeSet = {};
		var atts = node.attributes;
		// TODO: should we allow for duplicate attributes at this point...
		// would any of the relevant dom implementations even allow this?
		var attnode, i=0;
		while(attnode=atts[i++]) {
			if((dojo.render.html.capable)&&(dojo.render.html.ie)){
				if(!attnode){ continue; }
				if(	(typeof attnode == "object")&&
					(typeof attnode.nodeValue == 'undefined')||
					(attnode.nodeValue == null)||
					(attnode.nodeValue == '')){ 
					continue; 
				}
			}
			var nn = (attnode.nodeName.indexOf("dojo:") == -1) ? attnode.nodeName : attnode.nodeName.split("dojo:")[1];
			parsedAttributeSet[nn] = { 
				value: attnode.nodeValue 
			};
		}
		return parsedAttributeSet;
	}
}

IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 492
E.. ..@.@.s<.........`....	y.8/8...........
........GET /scooby/js/lib/dojo-event_and_io/src/widget/Widget.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.8/8...e...3!Q.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"17098-1147389476000"
Last-Modified: Thu, 11 May 2006 23:17:56 GMT
Content-Type: text/javascript
Content-Length: 17098
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.widget.Widget");
dojo.provide("dojo.widget.tags");

dojo.require("dojo.lang.func");
dojo.require("dojo.lang.array");
dojo.require("dojo.lang.extras");
dojo.require("dojo.lang.declare");
dojo.require("dojo.widget.Manager");
dojo.require("dojo.event.*");

dojo.declare("dojo.widget.Widget", null, {
	initializer: function() {								 
		// these properties aren't primitives and need to be created on a per-item
		// basis.
		this.children = [];
		// this.selection = new dojo.widget.Selection();
		// FIXME: need to replace this with context menu stuff
		this.extraArgs = {};
	},
	// FIXME: need to be able to disambiguate what our rendering context is
	//        here!
	//
	// needs to be a string with the end classname. Every subclass MUST
	// over-ride.
	//
	// base widget properties
	parent: null,
	// obviously, top-level and modal widgets should set these appropriately
	isTopLevel:  false,
	isModal: false,

	isEnabled: true,
	isHidden: false,
	isContainer: false, // can we contain other widgets?
	widgetId: "",
	widgetType: "Widget", // used for building generic widgets

	toString: function() {
		return '[Widget ' + this.widgetType + ', ' + (this.widgetId || 'NO ID') + ']';
	},

	repr: function(){
		return this.toString();
	},

	enable: function(){
		// should be over-ridden
		this.isEnabled = true;
	},

	disable: function(){
		// should be over-ridden
		this.isEnabled = false;
	},

	hide: function(){
		// should be over-ridden
		this.isHidden = true;
	},

	show: function(){
		// should be over-ridden
		this.isHidden = false;
	},

	onResized: function(){
		// Clients should override this function to do special processing,
		// then call this.notifyChildrenOfResize() to notify children of resize
		this.notifyChildrenOfResize();
	},
	
	notifyChildrenOfResize: function(){
		for(var i=0; i<this.children.length; i++){
			var child = this.children[i];
			//dojo.debug(this.widgetId + " resizing child " + child.widgetId);
			if( child.onResized ){
				child.onResized();
			}
		}
	},

	create: function(args, fragment, parentComp){
		// dojo.debug(this.widgetType, "create");
		this.satisfyPropertySets(args, fragment, parentComp);
		// dojo.debug(this.widgetType, "-> mixInProperties");
		this.mixInProperties(args, fragment, parentComp);
		// dojo.debug(this.widgetType, "-> postMixInProperties");
		this.postMixInProperties(args, fragment, parentComp);
		// dojo.debug(this.widgetType, "-> dojo.widget.manager.add");
		dojo.widget.manager.add(this);
		// dojo.debug(this.widgetType, "-> buildRendering");
		this.buildRendering(args, fragment, parentComp);
		// dojo.debug(this.widgetType, "-> initialize");
		this.initialize(args, fragment, parentComp);
		// dojo.debug(this.widgetType, "-> postInitialize");
		this.postInitialize(args, fragment, parentComp);
		// dojo.debug(this.widgetType, "-> postCreate");
		this.postCreate(args, fragment, parentComp);
		// dojo.debug(this.widgetType, "done!");
		return this;
	},

	// Destroy this widget and it's descendants
	destroy: function(finalize){
		// FIXME: this is woefully incomplete
		this.destroyChildren();
		this.uninitialize();
		this.destroyRendering(finalize);
		dojo.widget.manager.removeById(this.widgetId);
	},

	// Destroy the children of this widget, and their descendents
	destroyChildren: function(){
		while(this.children.length > 0){
			var tc = this.children[0];
			this.removeChild(tc);
			tc.destroy();
		}
	},

	getChildrenOfType: function(type, recurse){
		var ret = [];
		var isFunc = dojo.lang.isFunction(type);
		if(!isFunc){
			type = type.toLowerCase();
		}
		for(var x=0; x<this.children.length; x++){
			if(isFunc){
				if(this.children[x] instanceof type){
					ret.push(this.children[x]);
				}
			}else{
				if(this.children[x].widgetType.toLowerCase() == type){
					ret.push(this.children[x]);
				}
			}
			if(recurse){
				ret = ret.concat(this.children[x].getChildrenOfType(type, recurse));
			}
		}
		return ret;
	},

	getDescendants: function(){
		var result = [];
		var stack = [this];
		var elem;
		while (elem = stack.pop()){
			result.push(elem);
			dojo.lang.forEach(elem.children, function(elem) { stack.push(elem); });
		}
		return result;
	},

	satisfyPropertySets: function(args){
		// dojo.profile.start("satisfyPropertySets");
		// get the default propsets for our component type
		/*
		var typePropSets = []; // FIXME: need to pull these from somewhere!
		var localPropSets = []; // pull out propsets from the parser's return structure

		// for(var x=0; x<args.length; x++){
		// }

		for(var x=0; x<typePropSets.length; x++){
		}

		for(var x=0; x<localPropSets.length; x++){
		}
		*/
		// dojo.profile.end("satisfyPropertySets");
		
		return args;
	},

	mixInProperties: function(args, frag){
		if((args["fastMixIn"])||(frag["fastMixIn"])){
			// dojo.profile.start("mixInProperties_fastMixIn");
			// fast mix in assumes case sensitivity, no type casting, etc...
			// dojo.lang.mixin(this, args);
			for(var x in args){
				this[x] = args[x];
			}
			// dojo.profile.end("mixInProperties_fastMixIn");
			return;
		}
		// dojo.profile.start("mixInProperties");
		/*
		 * the actual mix-in code attempts to do some type-assignment based on
		 * PRE-EXISTING properties of the "this" object. When a named property
		 * of a propset is located, it is first tested to make sure that the
		 * current object already "has one". Properties which are undefined in
		 * the base widget are NOT settable here. The next step is to try to
		 * determine type of the pre-existing property. If it's a string, the
		 * property value is simply assigned. If a function, the property is
		 * replaced with a "new Function()" declaration. If an Array, the
		 * system attempts to split the string value on ";" chars, and no
		 * further processing is attempted (conversion of array elements to a
		 * integers, for instance). If the property value is an Object
		 * (testObj.constructor === Object), the property is split first on ";"
		 * chars, secondly on ":" chars, and the resulting key/value pairs are
		 * assigned to an object in a map style. The onus is on the property
		 * user to ensure that all property values are converted to the
		 * expected type before usage.
		 */

		var undef;

		// NOTE: we cannot assume that the passed properties are case-correct
		// (esp due to some browser bugs). Therefore, we attempt to locate
		// properties for assignment regardless of case. This may cause
		// problematic assignments and bugs in the future and will need to be
		// documented with big bright neon lights.

		// FIXME: fails miserably if a mixin property has a default value of null in 
		// a widget

		// NOTE: caching lower-cased args in the prototype is only 
		// acceptable if the properties are invariant.
		// if we have a name-cache, get it
		var lcArgs = dojo.widget.lcArgsCache[this.widgetType];
		if ( lcArgs == null ){
			// build a lower-case property name cache if we don't have one
			lcArgs = {};
			for(var y in this){
				lcArgs[((new String(y)).toLowerCase())] = y;
			}
			dojo.widget.lcArgsCache[this.widgetType] = lcArgs;
		}
		var visited = {};
		for(var x in args){
			if(!this[x]){ // check the cache for properties
				var y = lcArgs[(new String(x)).toLowerCase()];
				if(y){
					args[y] = args[x];
					x = y; 
				}
			}
			if(visited[x]){ continue; }
			visited[x] = true;
			if((typeof this[x]) != (typeof undef)){
				if(typeof args[x] != "string"){
					this[x] = args[x];
				}else{
					if(dojo.lang.isString(this[x])){
						this[x] = args[x];
					}else if(dojo.lang.isNumber(this[x])){
						this[x] = new Number(args[x]); // FIXME: what if NaN is the result?
					}else if(dojo.lang.isBoolean(this[x])){
						this[x] = (args[x].toLowerCase()=="false") ? false : true;
					}else if(dojo.lang.isFunction(this[x])){

						// FIXME: need to determine if always over-writing instead
						// of attaching here is appropriate. I suspect that we
						// might want to only allow attaching w/ action items.
						
						// RAR, 1/19/05: I'm going to attach instead of
						// over-write here. Perhaps function objects could have
						// some sort of flag set on them? Or mixed-into objects
						// could have some list of non-mutable properties
						// (although I'm not sure how that would alleviate this
						// particular problem)? 

						// this[x] = new Function(args[x]);

						// after an IRC discussion last week, it was decided
						// that these event handlers should execute in the
						// context of the widget, so 
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 8312
E. ...@.@.'............`.8R`...e...3.......
........that the "this" pointer
						// takes correctly.
						
						// argument that contains no punctuation other than . is 
						// considered a function spec, not code
						if(args[x].search(/[^\w\.]+/i) == -1){
							this[x] = dojo.evalObjPath(args[x], false);
						}else{
							var tn = dojo.lang.nameAnonFunc(new Function(args[x]), this);
							dojo.event.connect(this, x, this, tn);
						}
					}else if(dojo.lang.isArray(this[x])){ // typeof [] == "object"
						this[x] = args[x].split(";");
					} else if (this[x] instanceof Date) {
						this[x] = new Date(Number(args[x])); // assume timestamp
					}else if(typeof this[x] == "object"){ 
						// FIXME: should we be allowing extension here to handle
						// other object types intelligently?

						// if we defined a URI, we probablt want to allow plain strings
						// to override it
						if (this[x] instanceof dojo.uri.Uri){

							this[x] = args[x];
						}else{

							// FIXME: unlike all other types, we do not replace the
							// object with a new one here. Should we change that?
							var pairs = args[x].split(";");
							for(var y=0; y<pairs.length; y++){
								var si = pairs[y].indexOf(":");
								if((si != -1)&&(pairs[y].length>si)){
									this[x][pairs[y].substr(0, si).replace(/^\s+|\s+$/g, "")] = pairs[y].substr(si+1);
								}
							}
						}
					}else{
						// the default is straight-up string assignment. When would
						// we ever hit this?
						this[x] = args[x];
					}
				}
			}else{
				// collect any extra 'non mixed in' args
				this.extraArgs[x.toLowerCase()] = args[x];
			}
		}
		// dojo.profile.end("mixInProperties");
	},
	
	postMixInProperties: function(){
	},

	initialize: function(args, frag){
		// dojo.unimplemented("dojo.widget.Widget.initialize");
		return false;
	},

	postInitialize: function(args, frag){
		return false;
	},

	postCreate: function(args, frag){
		return false;
	},

	uninitialize: function(){
		// dojo.unimplemented("dojo.widget.Widget.uninitialize");
		return false;
	},

	buildRendering: function(){
		// SUBCLASSES MUST IMPLEMENT
		dojo.unimplemented("dojo.widget.Widget.buildRendering, on "+this.toString()+", ");
		return false;
	},

	destroyRendering: function(){
		// SUBCLASSES MUST IMPLEMENT
		dojo.unimplemented("dojo.widget.Widget.destroyRendering");
		return false;
	},

	cleanUp: function(){
		// SUBCLASSES MUST IMPLEMENT
		dojo.unimplemented("dojo.widget.Widget.cleanUp");
		return false;
	},

	addedTo: function(parent){
		// this is just a signal that can be caught
	},

	addChild: function(child){
		// SUBCLASSES MUST IMPLEMENT
		dojo.unimplemented("dojo.widget.Widget.addChild");
		return false;
	},

	// Detach the given child widget from me, but don't destroy it
	removeChild: function(widget){
		for(var x=0; x<this.children.length; x++){
			if(this.children[x] === widget){
				this.children.splice(x, 1);
				break;
			}
		}
		return widget;
	},

	resize: function(width, height){
		// both width and height may be set as percentages. The setWidth and
		// setHeight  functions attempt to determine if the passed param is
		// specified in percentage or native units. Integers without a
		// measurement are assumed to be in the native unit of measure.
		this.setWidth(width);
		this.setHeight(height);
	},

	setWidth: function(width){
		if((typeof width == "string")&&(width.substr(-1) == "%")){
			this.setPercentageWidth(width);
		}else{
			this.setNativeWidth(width);
		}
	},

	setHeight: function(height){
		if((typeof height == "string")&&(height.substr(-1) == "%")){
			this.setPercentageHeight(height);
		}else{
			this.setNativeHeight(height);
		}
	},

	setPercentageHeight: function(height){
		// SUBCLASSES MUST IMPLEMENT
		return false;
	},

	setNativeHeight: function(height){
		// SUBCLASSES MUST IMPLEMENT
		return false;
	},

	setPercentageWidth: function(width){
		// SUBCLASSES MUST IMPLEMENT
		return false;
	},

	setNativeWidth: function(width){
		// SUBCLASSES MUST IMPLEMENT
		return false;
	},

	getPreviousSibling: function() {
		var idx = this.getParentIndex();
 
		 // first node is idx=0 not found is idx<0
		if (idx<=0) return null;
 
		return this.getSiblings()[idx-1];
	},
 
	getSiblings: function() {
		return this.parent.children;
	},
 
	getParentIndex: function() {
		return dojo.lang.indexOf( this.getSiblings(), this, true);
	},
 
	getNextSibling: function() {
 
		var idx = this.getParentIndex();
 
		if (idx == this.getSiblings().length-1) return null; // last node
		if (idx < 0) return null; // not found
 
		return this.getSiblings()[idx+1];
 
	}
});

// Lower case name cache: listing of the lower case elements in each widget.
// We can't store the lcArgs in the widget itself because if B subclasses A,
// then B.prototype.lcArgs might return A.prototype.lcArgs, which is not what we
// want
dojo.widget.lcArgsCache = {};

// TODO: should have a more general way to add tags or tag libraries?
// TODO: need a default tags class to inherit from for things like getting propertySets
// TODO: parse properties/propertySets into component attributes
// TODO: parse subcomponents
// TODO: copy/clone raw markup fragments/nodes as appropriate
dojo.widget.tags = {};
dojo.widget.tags.addParseTreeHandler = function(type){
	var ltype = type.toLowerCase();
	this[ltype] = function(fragment, widgetParser, parentComp, insertionIndex, localProps){ 
		return dojo.widget.buildWidgetFromParseTree(ltype, fragment, widgetParser, parentComp, insertionIndex, localProps);
	}
}
dojo.widget.tags.addParseTreeHandler("dojo:widget");

dojo.widget.tags["dojo:propertyset"] = function(fragment, widgetParser, parentComp){
	// FIXME: Is this needed?
	// FIXME: Not sure that this parses into the structure that I want it to parse into...
	// FIXME: add support for nested propertySets
	var properties = widgetParser.parseProperties(fragment["dojo:propertyset"]);
}

// FIXME: need to add the <dojo:connect />
dojo.widget.tags["dojo:connect"] = function(fragment, widgetParser, parentComp){
	var properties = widgetParser.parseProperties(fragment["dojo:connect"]);
}

// FIXME: if we know the insertion point (to a reasonable location), why then do we:
//	- create a template node
//	- clone the template node
//	- render the clone and set properties
//	- remove the clone from the render tree
//	- place the clone
// this is quite dumb
dojo.widget.buildWidgetFromParseTree = function(type, frag, 
												parser, parentComp, 
												insertionIndex, localProps){
	var stype = type.split(":");
	stype = (stype.length == 2) ? stype[1] : type;
	// FIXME: we don't seem to be doing anything with this!
	// var propertySets = parser.getPropertySets(frag);
	var localProperties = localProps || parser.parseProperties(frag["dojo:"+stype]);
	// var tic = new Date();
	var twidget = dojo.widget.manager.getImplementation(stype);
	if(!twidget){
		throw new Error("cannot find \"" + stype + "\" widget");
	}else if (!twidget.create){
		throw new Error("\"" + stype + "\" widget object does not appear to implement *Widget");
	}
	localProperties["dojoinsertionindex"] = insertionIndex;
	// FIXME: we loose no less than 5ms in construction!
	var ret = twidget.create(localProperties, frag, parentComp);
	// dojo.debug(new Date() - tic);
	return ret;
}

/*
 * it would be best to be able to call defineWidget for any widget namespace
 */
dojo.widget.defineWidget = function(widgetClass /*string*/, superclass /*function*/, props /*object*/, renderer /*string*/, ctor /*function*/){
	// widgetClass takes the form foo.bar.baz<.renderer>.WidgetName (e.g. foo.bar.baz.WidgetName or foo.bar.baz.html.WidgetName)
	var namespace = widgetClass.split(".");
	var type = namespace.pop(); // type <= WidgetName, namespace <= foo.bar.baz<.renderer>
	if(renderer){
		// FIXME: could just be namespace.pop(), unless there can be foo.bar.baz.html.zot.WidgetName
		while ((namespace.length)&&(namespace.pop() != renderer)); // namespace <= foo.bar.baz
	}
	namespace = namespace.join(".");

	dojo.widget.manager.registerWidgetPackage(namespace);
	dojo.widget.tags.addParseTreeHandler("dojo:"+type.toLowerCase());

	if(!props){ props = {}; }
	props.widgetType = type;

	if((!ctor)&&(props["classConstructor"])){
		ctor = props.classConstructor;
		delete props.classConstructor;
	}
	dojo.declare(widgetClass, superclass, props, ctor);
}

IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 491
E....2@.@............_...._..'.>...y.......
.......SGET /scooby/js/lib/dojo-event_and_io/src/lang/declare.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 3804
E...N.@.@.............._.'.>..ao...H
......
...:....HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"3592-1147389474000"
Last-Modified: Thu, 11 May 2006 23:17:54 GMT
Content-Type: text/javascript
Content-Length: 3592
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.lang.declare");

dojo.require("dojo.lang.common");
dojo.require("dojo.lang.extras");

/*
 * Creates a constructor: inherit and extend
 *
 * - inherits from "superclass" (via dojo.inherits, null is ok)
 * - "props" are mixed-in to the prototype (via dojo.lang.extend)
 * - can have an initializer function that fires when the class is created. 
 * - name of the class ("className" argument) is stored in "clasName" property
 * 
 * The initializer function works just like a constructor, except it has the following benefits:
 * - it doesn't fire at inheritance time (when prototyping)
 * - properties set in the initializer do not become part of subclass prototypes
 *
 * The initializer can be specified in the "init" argument, or by including a function called
 * "initializer" in "props".
 *
 * Superclass methods (inherited methods) can be invoked using "inherited" method:
 * this.inherited(<method name>[, <argument array>]);
 * - inherited will continue up the prototype chain until it finds an implementation of method
 * - nested calls to inherited are supported (i.e. inherited method "A" can succesfully call inherited("A"), and so on)
 *
 * Aliased as "dojo.declare"
 *
 * Usage:
 *
 * dojo.declare("my.classes.bar", my.classes.foo, {
 *	initializer: function() {
 *		this.myComplicatedObject = new ReallyComplicatedObject(); 
 *	},
 *	someValue: 2,
 *	aMethod: function() { doStuff(); }
 * });
 *
 */
dojo.lang.declare = function(className /*string*/, superclass /*function*/ , props /*object*/, init /*function*/){
	var ctor = function(){ 
		// get the generational context (which object [or prototype] should be constructed)
		var self = this._getPropContext();
		var s = self.constructor.superclass;
		if((s)&&(s.constructor)){
			// if this constructor is invoked directly by some constructor (my.ancestor.call(this))
			if(s.constructor==arguments.callee){
				this.inherited("constructor", arguments);
			}else{
				this._inherited(s, "constructor", arguments);
			}
		}
		if((!this.prototyping)&&(self.initializer)){
			self.initializer.apply(this, arguments);
		}
	}
	var scp = (superclass ? superclass.prototype : null);
	if(scp){
		scp.prototyping = true;
		ctor.prototype = new superclass();
		scp.prototyping = false; 
	}
	ctor.prototype.constructor = ctor;
	ctor.superclass = scp;
	dojo.lang.extend(ctor, dojo.lang.declare.base);
	props=(props||{});
	props.initializer = (props.initializer)||(init)||(function(){ });
	props.className = className;
	dojo.lang.extend(ctor, props);
	dojo.lang.setObjPathValue(className, ctor, null, true);
}
dojo.lang.declare.base = {
	_getPropContext: function() { return (this.___proto||this); },
	// cache ptype context and call method on it
	_inherited: function(ptype, method, args){
		var stack = this.___proto;
		this.___proto = ptype;
		var result = ptype[method].apply(this, (args||[]));
		this.___proto = stack;
		return result;
	},
	// searches backward thru prototype chain to find nearest ancestral iplementation of method
	inherited: function(prop, args){
		var p = this._getPropContext();
		do{
			if((!p.constructor)||(!p.constructor.superclass)){return;}
			p = p.constructor.superclass;
		}while(!(prop in p));
		return (typeof p[prop] == 'function' ? this._inherited(p, prop, args) : p[prop]);
	}
}
dojo.declare = dojo.lang.declare;

IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 493
E..!$.@.@..A.........[...~....'5...........
...G....GET /scooby/js/lib/dojo-event_and_io/src/widget/Manager.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 8910
E.#.6h@.@..............[..'5.~...... ......
...V...GHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"8698-1147389476000"
Last-Modified: Thu, 11 May 2006 23:17:56 GMT
Content-Type: text/javascript
Content-Length: 8698
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.widget.Manager");
dojo.require("dojo.lang.array");
dojo.require("dojo.lang.func");
dojo.require("dojo.event.*");

// Manager class
dojo.widget.manager = new function(){
	this.widgets = [];
	this.widgetIds = [];
	
	// map of widgetId-->widget for widgets without parents (top level widgets)
	this.topWidgets = {};

	var widgetTypeCtr = {};
	var renderPrefixCache = [];

	this.getUniqueId = function (widgetType) {
		return widgetType + "_" + (widgetTypeCtr[widgetType] != undefined ?
			++widgetTypeCtr[widgetType] : widgetTypeCtr[widgetType] = 0);
	}

	this.add = function(widget){
		dojo.profile.start("dojo.widget.manager.add");
		this.widgets.push(widget);
		// Opera9 uses ID (caps)
		if(!widget.extraArgs["id"]){
			widget.extraArgs["id"] = widget.extraArgs["ID"];
		}
		// FIXME: the rest of this method is very slow!
		if(widget.widgetId == ""){
			if(widget["id"]){
				widget.widgetId = widget["id"];
			}else if(widget.extraArgs["id"]){
				widget.widgetId = widget.extraArgs["id"];
			}else{
				widget.widgetId = this.getUniqueId(widget.widgetType);
			}
		}
		if(this.widgetIds[widget.widgetId]){
			dojo.debug("widget ID collision on ID: "+widget.widgetId);
		}
		this.widgetIds[widget.widgetId] = widget;
		// Widget.destroy already calls removeById(), so we don't need to
		// connect() it here
		dojo.profile.end("dojo.widget.manager.add");
	}

	this.destroyAll = function(){
		for(var x=this.widgets.length-1; x>=0; x--){
			try{
				// this.widgets[x].destroyChildren();
				this.widgets[x].destroy(true);
				delete this.widgets[x];
			}catch(e){ }
		}
	}

	// FIXME: we should never allow removal of the root widget until all others
	// are removed!
	this.remove = function(widgetIndex){
		var tw = this.widgets[widgetIndex].widgetId;
		delete this.widgetIds[tw];
		this.widgets.splice(widgetIndex, 1);
	}
	
	// FIXME: suboptimal performance
	this.removeById = function(id) {
		for (var i=0; i<this.widgets.length; i++){
			if(this.widgets[i].widgetId == id){
				this.remove(i);
				break;
			}
		}
	}

	this.getWidgetById = function(id){
		return this.widgetIds[id];
	}

	this.getWidgetsByType = function(type){
		var lt = type.toLowerCase();
		var ret = [];
		dojo.lang.forEach(this.widgets, function(x){
			if(x.widgetType.toLowerCase() == lt){
				ret.push(x);
			}
		});
		return ret;
	}

	this.getWidgetsOfType = function (id) {
		dojo.deprecated("getWidgetsOfType is depecrecated, use getWidgetsByType");
		return dojo.widget.manager.getWidgetsByType(id);
	}

	this.getWidgetsByFilter = function(unaryFunc, onlyOne){
		var ret = [];
		dojo.lang.every(this.widgets, function(x){
			if(unaryFunc(x)){
				ret.push(x);
				if(onlyOne){return false;}
			}
			return true;
		});
		return (onlyOne ? ret[0] : ret);
	}

	this.getAllWidgets = function() {
		return this.widgets.concat();
	}

	//	added, trt 2006-01-20
	this.getWidgetByNode = function(/* DOMNode */ node){
		var w=this.getAllWidgets();
		for (var i=0; i<w.length; i++){
			if (w[i].domNode==node){
				return w[i];
			}
		}
		return null;
	}

	// shortcuts, baby
	this.byId = this.getWidgetById;
	this.byType = this.getWidgetsByType;
	this.byFilter = this.getWidgetsByFilter;
	this.byNode = this.getWidgetByNode;

	// map of previousally discovered implementation names to constructors
	var knownWidgetImplementations = {};

	// support manually registered widget packages
	var widgetPackages = ["dojo.widget"];
	for (var i=0; i<widgetPackages.length; i++) {
		// convenience for checking if a package exists (reverse lookup)
		widgetPackages[widgetPackages[i]] = true;
	}

	this.registerWidgetPackage = function(pname) {
		if(!widgetPackages[pname]){
			widgetPackages[pname] = true;
			widgetPackages.push(pname);
		}
	}
	
	this.getWidgetPackageList = function() {
		return dojo.lang.map(widgetPackages, function(elt) { return(elt!==true ? elt : undefined); });
	}
	
	this.getImplementation = function(widgetName, ctorObject, mixins){
		// try and find a name for the widget
		var impl = this.getImplementationName(widgetName);
		if(impl){ 
			// var tic = new Date();
			var ret = new impl(ctorObject);
			// dojo.debug(new Date() - tic);
			return ret;
		}
	}

	this.getImplementationName = function(widgetName){
		/*
		 * This is the overly-simplistic implemention of getImplementation (har
		 * har). In the future, we are going to want something that allows more
		 * freedom of expression WRT to specifying different specializations of
		 * a widget.
		 *
		 * Additionally, this implementation treats widget names as case
		 * insensitive, which does not necessarialy mesh with the markup which
		 * can construct a widget.
		 */

		var lowerCaseWidgetName = widgetName.toLowerCase();

		var impl = knownWidgetImplementations[lowerCaseWidgetName];
		if(impl){
			return impl;
		}

		// first store a list of the render prefixes we are capable of rendering
		if(!renderPrefixCache.length){
			for(var renderer in dojo.render){
				if(dojo.render[renderer]["capable"] === true){
					var prefixes = dojo.render[renderer].prefixes;
					for(var i = 0; i < prefixes.length; i++){
						renderPrefixCache.push(prefixes[i].toLowerCase());
					}
				}
			}
			// make sure we don't HAVE to prefix widget implementation names
			// with anything to get them to render
			renderPrefixCache.push("");
		}

		// look for a rendering-context specific version of our widget name
		for(var i = 0; i < widgetPackages.length; i++){
			var widgetPackage = dojo.evalObjPath(widgetPackages[i]);
			if(!widgetPackage) { continue; }

			for (var j = 0; j < renderPrefixCache.length; j++) {
				if (!widgetPackage[renderPrefixCache[j]]) { continue; }
				for (var widgetClass in widgetPackage[renderPrefixCache[j]]) {
					if (widgetClass.toLowerCase() != lowerCaseWidgetName) { continue; }
					knownWidgetImplementations[lowerCaseWidgetName] =
						widgetPackage[renderPrefixCache[j]][widgetClass];
					return knownWidgetImplementations[lowerCaseWidgetName];
				}
			}

			for (var j = 0; j < renderPrefixCache.length; j++) {
				for (var widgetClass in widgetPackage) {
					if (widgetClass.toLowerCase() !=
						(renderPrefixCache[j] + lowerCaseWidgetName)) { continue; }
	
					knownWidgetImplementations[lowerCaseWidgetName] =
						widgetPackage[widgetClass];
					return knownWidgetImplementations[lowerCaseWidgetName];
				}
			}
		}
		
		throw new Error('Could not locate "' + widgetName + '" class');
	}

	// FIXME: does it even belong in this name space?
	// NOTE: this method is implemented by DomWidget.js since not all
	// hostenv's would have an implementation.
	/*this.getWidgetFromPrimitive = function(baseRenderType){
		dojo.unimplemented("dojo.widget.manager.getWidgetFromPrimitive");
	}

	this.getWidgetFromEvent = function(nativeEvt){
		dojo.unimplemented("dojo.widget.manager.getWidgetFromEvent");
	}*/

	// Catch window resize events and notify top level widgets
	this.resizing=false;
	this.onWindowResized = function(){
		if(this.resizing){
			return;	// duplicate event
		}
		try{
			this.resizing=true;
			for(var id in this.topWidgets){
				var child = this.topWidgets[id];
				if(child.onParentResized ){
					child.onParentResized();
				}
			};
		}catch(e){
		}finally{
			this.resizing=false;
		}
	}
	if(typeof window != "undefined") {
		dojo.addOnLoad(this, 'onWindowResized');							// initial sizing
		dojo.event.connect(window, 'onresize', this, 'onWindowResized');	// window resize
	}

	// FIXME: what else?
};

(function(){
	var dw = dojo.widget;
	var dwm = dw.manager;
	var h = dojo.lang.curry(dojo.lang, "hitch", dwm);
	var g = function(oldName, newName){
		dw[(newName||oldName)] = h(oldName);
	}
	// copy the methods from the default manager (this) to the widget namespace
	g("add", "addWidget");
	g("destroyAll", "destroyAllWidgets");
	g("remove", "removeWidget");
	g("removeById", "removeWidgetById");
	g("getWidgetById");
	g("getWidgetById", "byId");
	g("getWidgetsByType");
	g("getWidgetsByFilter");
	g("getWidgetsByType", "byType");
	g("getWidgetsByFilter", "byFilter");
	g("getWidgetByNode", "byNode");
	dw.all = function(n){
		var widgets = dwm.getAllWidgets.apply(dwm, arguments);
		if(arguments.length > 0) {
			return widgets[n];
		}
		return widgets;
	}
	g("registerWidgetPackage");
	g("getImplementation", "getWidgetImplementation");
	g("getImplementationName", "getWidgetImplementationName");

	dw.widgets = dwm.widgets;
	dw.widgetIds = dwm.widgetIds;
	dw.root = dwm.root;
})();

IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 491
E....n@.@.Qh.........^.../1#...V...........
...b....GET /scooby/js/lib/dojo-event_and_io/src/widget/Parse.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 9000
E.#\u.@.@..............^...V./3....E!Q.....
...c...bHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"10192-1147389476000"
Last-Modified: Thu, 11 May 2006 23:17:56 GMT
Content-Type: text/javascript
Content-Length: 10192
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.widget.Parse");

dojo.require("dojo.widget.Manager");
dojo.require("dojo.dom");

dojo.widget.Parse = function(fragment) {
	this.propertySetsList = [];
	this.fragment = fragment;
	
	this.createComponents = function(frag, parentComp){
		var comps = [ ];
		var built = false;
		// if we have items to parse/create at this level, do it!
		try{
			if((frag)&&(frag["tagName"])&&(frag!=frag["nodeRef"])){
				var djTags = dojo.widget.tags;
				// we split so that you can declare multiple
				// non-destructive widgets from the same ctor node
				var tna = String(frag["tagName"]).split(";");
				for(var x=0; x<tna.length; x++){
					var ltn = (tna[x].replace(/^\s+|\s+$/g, "")).toLowerCase();
					if(djTags[ltn]){
						built = true;
						frag.tagName = ltn;
						var ret = djTags[ltn](frag, this, parentComp, frag["index"]);
						comps.push(ret);
					}else{
						if((dojo.lang.isString(ltn))&&(ltn.substr(0, 5)=="dojo:")){
							dojo.debug("no tag handler registed for type: ", ltn);
						}
					}
				}
			}
		}catch(e){
			dojo.debug("dojo.widget.Parse: error:", e);
			// throw(e);
			// IE is such a bitch sometimes
		}
		// if there's a sub-frag, build widgets from that too
		if(!built){
			comps = comps.concat(this.createSubComponents(frag, parentComp));
		}
		return comps;
	}

	/*	createSubComponents recurses over a raw JavaScript object structure,
			and calls the corresponding handler for its normalized tagName if it exists
	*/
	this.createSubComponents = function(fragment, parentComp){
		var frag, comps = [];
		for(var item in fragment){
			frag = fragment[item];
			if ((frag)&&(typeof frag == "object")&&(frag!=fragment.nodeRef)&&(frag!=fragment["tagName"])){
				comps = comps.concat(this.createComponents(frag, parentComp));
			}
		}
		return comps;
	}

	/*  parsePropertySets checks the top level of a raw JavaScript object
			structure for any propertySets.  It stores an array of references to 
			propertySets that it finds.
	*/
	this.parsePropertySets = function(fragment) {
		return [];
		var propertySets = [];
		for(var item in fragment){
			if(	(fragment[item]["tagName"] == "dojo:propertyset") ) {
				propertySets.push(fragment[item]);
			}
		}
		// FIXME: should we store these propertySets somewhere for later retrieval
		this.propertySetsList.push(propertySets);
		return propertySets;
	}
	
	/*  parseProperties checks a raw JavaScript object structure for
			properties, and returns an array of properties that it finds.
	*/
	this.parseProperties = function(fragment) {
		var properties = {};
		for(var item in fragment){
			// FIXME: need to check for undefined?
			// case: its a tagName or nodeRef
			if((fragment[item] == fragment["tagName"])||
				(fragment[item] == fragment.nodeRef)){
				// do nothing
			}else{
				if((fragment[item]["tagName"])&&
					(dojo.widget.tags[fragment[item].tagName.toLowerCase()])){
					// TODO: it isn't a property or property set, it's a fragment, 
					// so do something else
					// FIXME: needs to be a better/stricter check
					// TODO: handle xlink:href for external property sets
				}else if((fragment[item][0])&&(fragment[item][0].value!="")&&(fragment[item][0].value!=null)){
					try{
						// FIXME: need to allow more than one provider
						if(item.toLowerCase() == "dataprovider") {
							var _this = this;
							this.getDataProvider(_this, fragment[item][0].value);
							properties.dataProvider = this.dataProvider;
						}
						properties[item] = fragment[item][0].value;
						var nestedProperties = this.parseProperties(fragment[item]);
						// FIXME: this kind of copying is expensive and inefficient!
						for(var property in nestedProperties){
							properties[property] = nestedProperties[property];
						}
					}catch(e){ dojo.debug(e); }
				}
			}
		}
		return properties;
	}

	/* getPropertySetById returns the propertySet that matches the provided id
	*/
	
	this.getDataProvider = function(objRef, dataUrl) {
		// FIXME: this is currently sync.  To make this async, we made need to move 
		//this step into the widget ctor, so that it is loaded when it is needed 
		// to populate the widget
		dojo.io.bind({
			url: dataUrl,
			load: function(type, evaldObj){
				if(type=="load"){
					objRef.dataProvider = evaldObj;
				}
			},
			mimetype: "text/javascript",
			sync: true
		});
	}

	
	this.getPropertySetById = function(propertySetId){
		for(var x = 0; x < this.propertySetsList.length; x++){
			if(propertySetId == this.propertySetsList[x]["id"][0].value){
				return this.propertySetsList[x];
			}
		}
		return "";
	}
	
	/* getPropertySetsByType returns the propertySet(s) that match(es) the
	 * provided componentClass
	 */
	this.getPropertySetsByType = function(componentType){
		var propertySets = [];
		for(var x=0; x < this.propertySetsList.length; x++){
			var cpl = this.propertySetsList[x];
			var cpcc = cpl["componentClass"]||cpl["componentType"]||null;
			if((cpcc)&&(propertySetId == cpcc[0].value)){
				propertySets.push(cpl);
			}
		}
		return propertySets;
	}
	
	/* getPropertySets returns the propertySet for a given component fragment
	*/
	this.getPropertySets = function(fragment){
		var ppl = "dojo:propertyproviderlist";
		var propertySets = [];
		var tagname = fragment["tagName"];
		if(fragment[ppl]){ 
			var propertyProviderIds = fragment[ppl].value.split(" ");
			// FIXME: should the propertyProviderList attribute contain #
			// 		  syntax for reference to ids or not?
			// FIXME: need a better test to see if this is local or external
			// FIXME: doesn't handle nested propertySets, or propertySets that
			// 		  just contain information about css documents, etc.
			for(propertySetId in propertyProviderIds){
				if((propertySetId.indexOf("..")==-1)&&(propertySetId.indexOf("://")==-1)){
					// get a reference to a propertySet within the current parsed structure
					var propertySet = this.getPropertySetById(propertySetId);
					if(propertySet != ""){
						propertySets.push(propertySet);
					}
				}else{
					// FIXME: add code to parse and return a propertySet from
					// another document
					// alex: is this even necessaray? Do we care? If so, why?
				}
			}
		}
		// we put the typed ones first so that the parsed ones override when
		// iteration happens.
		return (this.getPropertySetsByType(tagname)).concat(propertySets);
	}
	
	/* 
		nodeRef is the node to be replaced... in the future, we might want to add 
		an alternative way to specify an insertion point

		componentName is the expected dojo widget name, i.e. Button of ContextMenu

		properties is an object of name value pairs
	*/
	this.createComponentFromScript = function(nodeRef, componentName, properties){
		var ltn = "dojo:" + componentName.toLowerCase();
		if(dojo.widget.tags[ltn]){
			properties.fastMixIn = true;
			return [dojo.widget.tags[ltn](properties, this, null, null, properties)];
		}else{
			if(ltn.substr(0, 5)=="dojo:"){
				dojo.debug("no tag handler registed for type: ", ltn);
			}
		}
	}
}


dojo.widget._parser_collection = {"dojo": new dojo.widget.Parse() };
dojo.widget.getParser = function(name){
	if(!name){ name = "dojo"; }
	if(!this._parser_collection[name]){
		this._parser_collection[name] = new dojo.widget.Parse();
	}
	return this._parser_collection[name];
}

/**
 * Creates widget.
 *
 * @param name     The name of the widget to create
 * @param props    Key-Value pairs of properties of the widget
 * @param refNode  If the last argument is specified this node is used as
 *                 a reference for inserting this node into a DOM tree else
 *                 it beomces the domNode
 * @param position The position to insert this widget's node relative to the
 *                 refNode argument
 * @return The new Widget object
 */
 
dojo.widget.createWidget = function(name, props, refNode, position){
	var lowerCaseName = name.toLowerCase();
	var namespacedName = "dojo:" + lowerCaseName;
	var isNode = ( dojo.byId(name) && (!dojo.widget.tags[namespacedName]) );

	// if we got a node or an unambiguious ID, build a widget out of it
	if(	(arguments.length==1) && ((typeof name != "string")||(isNode)) ){
		// we got a DOM node
		var xp = new dojo.xml.Parse();
		// FIXME: we should try to find the parent!
		var tn = (isNode) ? dojo.byId(name) : name;
		return dojo.widget.getParser().createComponents(xp.parseElement(tn, null, true))[0];
	}

	function fromScript (placeKeeperNode, name, props) {
		props[namespacedName] = { 
			dojotype: [{value: lowerCaseName}],
			nodeRef: placeKeeperNode,
			fastMixIn: t
IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 1406
E...u.@.@..............^..
~./3....E.......
...c...crue
		};
		return dojo.widget.getParser().createComponentFromScript(
			placeKeeperNode, name, props, true);
	}

	if (typeof name != "string" && typeof props == "string") {
		dojo.deprecated("dojo.widget.createWidget", 
			"argument order is now of the form " +
			"dojo.widget.createWidget(NAME, [PROPERTIES, [REFERENCENODE, [POSITION]]])");
		return fromScript(name, props, refNode);
	}
	
	props = props||{};
	var notRef = false;
	var tn = null;
	var h = dojo.render.html.capable;
	if(h){
		tn = document.createElement("span");
	}
	if(!refNode){
		notRef = true;
		refNode = tn;
		if(h){
			document.body.appendChild(refNode);
		}
	}else if(position){
		dojo.dom.insertAtPosition(tn, refNode, position);
	}else{ // otherwise don't replace, but build in-place
		tn = refNode;
	}
	var widgetArray = fromScript(tn, name, props);
	if (!widgetArray[0] || typeof widgetArray[0].widgetType == "undefined") {
		throw new Error("createWidget: Creation of \"" + name + "\" widget failed.");
	}
	if (notRef) {
		if (widgetArray[0].domNode.parentNode) {
			widgetArray[0].domNode.parentNode.removeChild(widgetArray[0].domNode);
		}
	}
	return widgetArray[0]; // just return the widget
}
 
dojo.widget.fromScript = function(name, props, refNode, position){
	dojo.deprecated("dojo.widget.fromScript", " use " +
		"dojo.widget.createWidget instead");
	return dojo.widget.createWidget(name, props, refNode, position);
}

IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 495
E..#..@.@.s6.........`.....e.8r............
...o....GET /scooby/js/lib/dojo-event_and_io/src/widget/DomWidget.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%	...........`.8r...
T...C!Q.....
...z...oHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"18139-1147389476000"
Last-Modified: Thu, 11 May 2006 23:17:56 GMT
Content-Type: text/javascript
Content-Length: 18139
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.widget.DomWidget");

dojo.require("dojo.event.*");
dojo.require("dojo.widget.Widget");
dojo.require("dojo.dom");
dojo.require("dojo.xml.Parse");
dojo.require("dojo.uri.*");
dojo.require("dojo.lang.func");

dojo.widget._cssFiles = {};
dojo.widget._cssStrings = {};
dojo.widget._templateCache = {};

dojo.widget.defaultStrings = {
	dojoRoot: dojo.hostenv.getBaseScriptUri(),
	baseScriptUri: dojo.hostenv.getBaseScriptUri()
};

dojo.widget.buildFromTemplate = function() {
	dojo.lang.forward("fillFromTemplateCache");
}

// static method to build from a template w/ or w/o a real widget in place
dojo.widget.fillFromTemplateCache = function(obj, templatePath, templateCssPath, templateString, avoidCache){
	// dojo.debug("avoidCache:", avoidCache);
	var tpath = templatePath || obj.templatePath;
	var cpath = templateCssPath || obj.templateCssPath;

	// DEPRECATED: use Uri objects, not strings
	if (tpath && !(tpath instanceof dojo.uri.Uri)) {
		tpath = dojo.uri.dojoUri(tpath);
		dojo.deprecated("templatePath should be of type dojo.uri.Uri");
	}
	if (cpath && !(cpath instanceof dojo.uri.Uri)) {
		cpath = dojo.uri.dojoUri(cpath);
		dojo.deprecated("templateCssPath should be of type dojo.uri.Uri");
	}
	
	var tmplts = dojo.widget._templateCache;
	if(!obj["widgetType"]) { // don't have a real template here
		do {
			var dummyName = "__dummyTemplate__" + dojo.widget._templateCache.dummyCount++;
		} while(tmplts[dummyName]);
		obj.widgetType = dummyName;
	}
	var wt = obj.widgetType;

	if((!obj.templateCssString)&&(cpath)&&(!dojo.widget._cssFiles[cpath])){
		obj.templateCssString = dojo.hostenv.getText(cpath);
		obj.templateCssPath = null;
		dojo.widget._cssFiles[cpath] = true;
	}
	if((obj["templateCssString"])&&(!obj.templateCssString["loaded"])){
		dojo.style.insertCssText(obj.templateCssString, null, cpath);
		if(!obj.templateCssString){ obj.templateCssString = ""; }
		obj.templateCssString.loaded = true;
	}

	var ts = tmplts[wt];
	if(!ts){
		tmplts[wt] = { "string": null, "node": null };
		if(avoidCache){
			ts = {};
		}else{
			ts = tmplts[wt];
		}
	}
	if(!obj.templateString){
		obj.templateString = templateString || ts["string"];
	}
	if(!obj.templateNode){
		obj.templateNode = ts["node"];
	}
	if((!obj.templateNode)&&(!obj.templateString)&&(tpath)){
		// fetch a text fragment and assign it to templateString
		// NOTE: we rely on blocking IO here!
		var tstring = dojo.hostenv.getText(tpath);
		if(tstring){
			var matches = tstring.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
			if(matches){
				tstring = matches[1];
			}
		}else{
			tstring = "";
		}
		obj.templateString = tstring;
		if(!avoidCache){
			tmplts[wt]["string"] = tstring;
		}
	}
	if((!ts["string"])&&(!avoidCache)){
		ts.string = obj.templateString;
	}
}
dojo.widget._templateCache.dummyCount = 0;

dojo.widget.attachProperties = ["dojoAttachPoint", "id"];
dojo.widget.eventAttachProperty = "dojoAttachEvent";
dojo.widget.onBuildProperty = "dojoOnBuild";

dojo.widget.attachTemplateNodes = function(rootNode, targetObj, events){
	// FIXME: this method is still taking WAAAY too long. We need ways of optimizing:
	//	a.) what we are looking for on each node
	//	b.) the nodes that are subject to interrogation (use xpath instead?)
	//	c.) how expensive event assignment is (less eval(), more connect())
	// var start = new Date();
	var elementNodeType = dojo.dom.ELEMENT_NODE;

	function trim(str){
		return str.replace(/^\s+|\s+$/g, "");
	}

	if(!rootNode){ 
		rootNode = targetObj.domNode;
	}

	if(rootNode.nodeType != elementNodeType){
		return;
	}
	// alert(events.length);

	var nodes = rootNode.all || rootNode.getElementsByTagName("*");
	var _this = targetObj;
	for(var x=-1; x<nodes.length; x++){
		var baseNode = (x == -1) ? rootNode : nodes[x];
		// FIXME: is this going to have capitalization problems?  Could use getAttribute(name, 0); to get attributes case-insensitve
		var attachPoint = [];
		for(var y=0; y<this.attachProperties.length; y++){
			var tmpAttachPoint = baseNode.getAttribute(this.attachProperties[y]);
			if(tmpAttachPoint){
				attachPoint = tmpAttachPoint.split(";");
				for(var z=0; z<attachPoint.length; z++){
					if(dojo.lang.isArray(targetObj[attachPoint[z]])){
						targetObj[attachPoint[z]].push(baseNode);
					}else{
						targetObj[attachPoint[z]]=baseNode;
					}
				}
				break;
			}
		}
		// continue;

		// FIXME: we need to put this into some kind of lookup structure
		// instead of direct assignment
		var tmpltPoint = baseNode.getAttribute(this.templateProperty);
		if(tmpltPoint){
			targetObj[tmpltPoint]=baseNode;
		}

		var attachEvent = baseNode.getAttribute(this.eventAttachProperty);
		if(attachEvent){
			// NOTE: we want to support attributes that have the form
			// "domEvent: nativeEvent; ..."
			var evts = attachEvent.split(";");
			for(var y=0; y<evts.length; y++){
				if((!evts[y])||(!evts[y].length)){ continue; }
				var thisFunc = null;
				var tevt = trim(evts[y]);
				if(evts[y].indexOf(":") >= 0){
					// oh, if only JS had tuple assignment
					var funcNameArr = tevt.split(":");
					tevt = trim(funcNameArr[0]);
					thisFunc = trim(funcNameArr[1]);
				}
				if(!thisFunc){
					thisFunc = tevt;
				}

				var tf = function(){ 
					var ntf = new String(thisFunc);
					return function(evt){
						if(_this[ntf]){
							_this[ntf](dojo.event.browser.fixEvent(evt, this));
						}
					};
				}();
				dojo.event.browser.addListener(baseNode, tevt, tf, false, true);
				// dojo.event.browser.addListener(baseNode, tevt, dojo.lang.hitch(_this, thisFunc));
			}
		}

		for(var y=0; y<events.length; y++){
			//alert(events[x]);
			var evtVal = baseNode.getAttribute(events[y]);
			if((evtVal)&&(evtVal.length)){
				var thisFunc = null;
				var domEvt = events[y].substr(4); // clober the "dojo" prefix
				thisFunc = trim(evtVal);
				var funcs = [thisFunc];
				if(thisFunc.indexOf(";")>=0){
					funcs = dojo.lang.map(thisFunc.split(";"), trim);
				}
				for(var z=0; z<funcs.length; z++){
					if(!funcs[z].length){ continue; }
					var tf = function(){ 
						var ntf = new String(funcs[z]);
						return function(evt){
							if(_this[ntf]){
								_this[ntf](dojo.event.browser.fixEvent(evt, this));
							}
						}
					}();
					dojo.event.browser.addListener(baseNode, domEvt, tf, false, true);
					// dojo.event.browser.addListener(baseNode, domEvt, dojo.lang.hitch(_this, funcs[z]));
				}
			}
		}

		var onBuild = baseNode.getAttribute(this.onBuildProperty);
		if(onBuild){
			eval("var node = baseNode; var widget = targetObj; "+onBuild);
		}
	}

}

dojo.widget.getDojoEventsFromStr = function(str){
	// var lstr = str.toLowerCase();
	var re = /(dojoOn([a-z]+)(\s?))=/gi;
	var evts = str ? str.match(re)||[] : [];
	var ret = [];
	var lem = {};
	for(var x=0; x<evts.length; x++){
		if(evts[x].legth < 1){ continue; }
		var cm = evts[x].replace(/\s/, "");
		cm = (cm.slice(0, cm.length-1));
		if(!lem[cm]){
			lem[cm] = true;
			ret.push(cm);
		}
	}
	return ret;
}

/*
dojo.widget.buildAndAttachTemplate = function(obj, templatePath, templateCssPath, templateString, targetObj) {
	this.buildFromTemplate(obj, templatePath, templateCssPath, templateString);
	var node = dojo.dom.createNodesFromText(obj.templateString, true)[0];
	this.attachTemplateNodes(node, targetObj||obj, dojo.widget.getDojoEventsFromStr(templateString));
	return node;
}
*/

dojo.declare("dojo.widget.DomWidget", dojo.widget.Widget, {
	initializer: function() {
		if((arguments.length>0)&&(typeof arguments[0] == "object")){
			this.create(arguments[0]);
		}
	},
								 
	templateNode: null,
	templateString: null,
	templateCssString: null,
	preventClobber: false,
	domNode: null, // this is our visible representation of the widget!
	containerNode: null, // holds child elements

	// Process the given child widget, inserting it's dom node as a child of our dom node
	// FIXME: should we support addition at an index in the children arr and
	// order the display accordingly? Right now we always append.
	addChild: function(widget, overrideContainerNode, pos, ref, insertIndex){
		if(!this.isContainer){ // we aren't allowed to contain other widgets, it seems
			dojo.debug("dojo.widget.DomWidget.addChild() attempted on non-container widget");
			return null;
		}else{
			this.addWidgetAsDirectChild(widget, overrideContainerNode, pos, ref, insertIndex);
			this.registerChild(widget, insertIndex);
		}
		return widget;
	},
	
	addWidgetAsDirectChild: function(widget, overrideContainerNode, pos, ref, insertIndex){
		if((!this.contai
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.8....
T...C!Q.....
...z...onerNode)&&(!overrideContainerNode)){
			this.containerNode = this.domNode;
		}
		var cn = (overrideContainerNode) ? overrideContainerNode : this.containerNode;
		if(!pos){ pos = "after"; }
		if(!ref){ 
			// if(!cn){ cn = document.body; }
			if(!cn){ cn = document.body; }
			ref = cn.lastChild; 
		}
		if(!insertIndex) { insertIndex = 0; }
		widget.domNode.setAttribute("dojoinsertionindex", insertIndex);

		// insert the child widget domNode directly underneath my domNode, in the
		// specified position (by default, append to end)
		if(!ref){
			cn.appendChild(widget.domNode);
		}else{
			// FIXME: was this meant to be the (ugly hack) way to support insert @ index?
			//dojo.dom[pos](widget.domNode, ref, insertIndex);

			// CAL: this appears to be the intended way to insert a node at a given position...
			if (pos == 'insertAtIndex'){
				// dojo.debug("idx:", insertIndex, "isLast:", ref === cn.lastChild);
				dojo.dom.insertAtIndex(widget.domNode, ref.parentNode, insertIndex);
			}else{
				// dojo.debug("pos:", pos, "isLast:", ref === cn.lastChild);
				if((pos == "after")&&(ref === cn.lastChild)){
					cn.appendChild(widget.domNode);
				}else{
					dojo.dom.insertAtPosition(widget.domNode, cn, pos);
				}
			}
		}
	},

	// Record that given widget descends from me
	registerChild: function(widget, insertionIndex){

		// we need to insert the child at the right point in the parent's 
		// 'children' array, based on the insertionIndex

		widget.dojoInsertionIndex = insertionIndex;

		var idx = -1;
		for(var i=0; i<this.children.length; i++){
			if (this.children[i].dojoInsertionIndex < insertionIndex){
				idx = i;
			}
		}

		this.children.splice(idx+1, 0, widget);

		widget.parent = this;
		widget.addedTo(this);
		
		// If this widget was created programatically, then it was erroneously added
		// to dojo.widget.manager.topWidgets.  Fix that here.
		delete dojo.widget.manager.topWidgets[widget.widgetId];
	},

	removeChild: function(widget){
		// detach child domNode from parent domNode
		dojo.dom.removeNode(widget.domNode);

		// remove child widget from parent widget
		return dojo.widget.DomWidget.superclass.removeChild.call(this, widget);
	},

	getFragNodeRef: function(frag){
		if( !frag || !frag["dojo:"+this.widgetType.toLowerCase()] ){
			dojo.raise("Error: no frag for widget type " + this.widgetType +
				", id " + this.widgetId + " (maybe a widget has set it's type incorrectly)");
		}
		return (frag ? frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"] : null);
	},
	
	// Replace source domNode with generated dom structure, and register
	// widget with parent.
	postInitialize: function(args, frag, parentComp){
		var sourceNodeRef = this.getFragNodeRef(frag);
		// Stick my generated dom into the output tree
		//alert(this.widgetId + ": replacing " + sourceNodeRef + " with " + this.domNode.innerHTML);
		if (parentComp && (parentComp.snarfChildDomOutput || !sourceNodeRef)){
			// Add my generated dom as a direct child of my parent widget
			// This is important for generated widgets, and also cases where I am generating an
			// <li> node that can't be inserted back into the original DOM tree
			parentComp.addWidgetAsDirectChild(this, "", "insertAtIndex", "",  args["dojoinsertionindex"], sourceNodeRef);
		} else if (sourceNodeRef){
			// Do in-place replacement of the my source node with my generated dom
			if(this.domNode && (this.domNode !== sourceNodeRef)){
				var oldNode = sourceNodeRef.parentNode.replaceChild(this.domNode, sourceNodeRef);
			}
		}

		// Register myself with my parent, or with the widget manager if
		// I have no parent
		// TODO: the code below erroneously adds all programatically generated widgets
		// to topWidgets (since we don't know who the parent is until after creation finishes)
		if ( parentComp ) {
			parentComp.registerChild(this, args.dojoinsertionindex);
		} else {
			dojo.widget.manager.topWidgets[this.widgetId]=this;
		}

		// Expand my children widgets
		if(this.isContainer){
			//alert("recurse from " + this.widgetId);
			// build any sub-components with us as the parent
			var fragParser = dojo.widget.getParser();
			fragParser.createSubComponents(frag, this);
		}
	},

	// method over-ride
	buildRendering: function(args, frag){
		// DOM widgets construct themselves from a template
		var ts = dojo.widget._templateCache[this.widgetType];
		if(	
			(!this.preventClobber)&&(
				(this.templatePath)||
				(this.templateNode)||
				(
					(this["templateString"])&&(this.templateString.length) 
				)||
				(
					(typeof ts != "undefined")&&( (ts["string"])||(ts["node"]) )
				)
			)
		){
			// if it looks like we can build the thing from a template, do it!
			this.buildFromTemplate(args, frag);
		}else{
			// otherwise, assign the DOM node that was the source of the widget
			// parsing to be the root node
			this.domNode = this.getFragNodeRef(frag);
		}
		this.fillInTemplate(args, frag); 	// this is where individual widgets
											// will handle population of data
											// from properties, remote data
											// sets, etc.
	},

	buildFromTemplate: function(args, frag){
		// var start = new Date();
		// copy template properties if they're already set in the templates object
		// dojo.debug("buildFromTemplate:", this);
		var avoidCache = false;
		if(args["templatecsspath"]){
			args["templateCssPath"] = args["templatecsspath"];
		}
		if(args["templatepath"]){
			avoidCache = true;
			args["templatePath"] = args["templatepath"];
		}
		dojo.widget.fillFromTemplateCache(	this, 
											args["templatePath"], 
											args["templateCssPath"],
											null,
											avoidCache);
		var ts = dojo.widget._templateCache[this.widgetType];
		if((ts)&&(!avoidCache)){
			if(!this.templateString.length){
				this.templateString = ts["string"];
			}
			if(!this.templateNode){
				this.templateNode = ts["node"];
			}
		}
		var matches = false;
		var node = null;
		// var tstr = new String(this.templateString); 
		var tstr = this.templateString; 
		// attempt to clone a template node, if there is one
		if((!this.templateNode)&&(this.templateString)){
			matches = this.templateString.match(/\$\{([^\}]+)\}/g);
			if(matches) {
				// if we do property replacement, don't create a templateNode
				// to clone from.
				var hash = this.strings || {};
				// FIXME: should this hash of default replacements be cached in
				// templateString?
				for(var key in dojo.widget.defaultStrings) {
					if(dojo.lang.isUndefined(hash[key])) {
						hash[key] = dojo.widget.defaultStrings[key];
					}
				}
				// FIXME: this is a lot of string munging. Can we make it faster?
				for(var i = 0; i < matches.length; i++) {
					var key = matches[i];
					key = key.substring(2, key.length-1);
					var kval = (key.substring(0, 5) == "this.") ? this[key.substring(5)] : hash[key];
					var value;
					if((kval)||(dojo.lang.isString(kval))){
						value = (dojo.lang.isFunction(kval)) ? kval.call(this, key, this.templateString) : kval;
						tstr = tstr.replace(matches[i], value);
					}
				}
			}else{
				// otherwise, we are required to instantiate a copy of the template
				// string if one is provided.
				
				// FIXME: need to be able to distinguish here what should be done
				// or provide a generic interface across all DOM implementations
				// FIMXE: this breaks if the template has whitespace as its first 
				// characters
				// node = this.createNodesFromText(this.templateString, true);
				// this.templateNode = node[0].cloneNode(true); // we're optimistic here
				this.templateNode = this.createNodesFromText(this.templateString, true)[0];
				ts.node = this.templateNode;
			}
		}
		if((!this.templateNode)&&(!matches)){ 
			dojo.debug("weren't able to create template!");
			return false;
		}else if(!matches){
			node = this.templateNode.cloneNode(true);
			if(!node){ return false; }
		}else{
			node = this.createNodesFromText(tstr, true)[0];
		}

		// recurse through the node, looking for, and attaching to, our
		// attachment points which should be defined on the template node.

		this.domNode = node;
		// dojo.profile.start("attachTemplateNodes");
		this.attachTemplateNodes(this.domNode, this);
		// dojo.profile.end("attachTemplateNodes");
		
		// relocate source contents to templated container node
		// this.containerNode must be able to receive children, or exceptions will be thrown
		if (this.isContainer && this.containerNode){
			var src = this.getFragNodeRef(frag);
			if (src){
				dojo.dom.moveChildren(src, this.containerNode);
			}
		}
	},

	attachTemplateNodes: function(baseNode, targetObj){
		if(!targetObj){ targetObj = this; }
		return dojo.widget.attachTemplateNodes(baseNode, targetObj, 
					dojo.widget.getDojoEventsFromStr(this.templateString));
	},

	fillInTemplate: function(){
		// dojo.unimplemented("dojo.widget.DomWidget.fillInTemplate");
	},
	
	// method over-ride
	destroyRendering: function(){
		try{
			delete this.domNode;
		}catch(e){ /* squelch! */ }
	},

	// FIXME: meth
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 353
E.....@.@.F............`.8.(..
T...C.......
...{...{od over-ride
	cleanUp: function(){},
	
	getContainerHeight: function(){
		dojo.unimplemented("dojo.widget.DomWidget.getContainerHeight");
	},

	getContainerWidth: function(){
		dojo.unimplemented("dojo.widget.DomWidget.getContainerWidth");
	},

	createNodesFromText: function(){
		dojo.unimplemented("dojo.widget.DomWidget.createNodesFromText");
	}
});

IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 494
E..".4@.@............_....ao.'.............
.......:GET /scooby/js/lib/dojo-event_and_io/src/uri/__package__.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 573
E..qN.@.@..f..........._.'....c]...Z.f.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"363-1147389474000"
Last-Modified: Thu, 11 May 2006 23:17:54 GMT
Content-Type: text/javascript
Content-Length: 363
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.kwCompoundRequire({
	common: ["dojo.uri.Uri", false, false]
});
dojo.provide("dojo.uri.*");

IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 486
E...$.@.@..F.........[...~....J............
.......VGET /scooby/js/lib/dojo-event_and_io/src/uri/Uri.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 3739
E...6i@.@..............[..J..~.............
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"3527-1147389474000"
Last-Modified: Thu, 11 May 2006 23:17:54 GMT
Content-Type: text/javascript
Content-Length: 3527
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.uri.Uri");

dojo.uri = new function() {
	this.joinPath = function() {
		// DEPRECATED: use the dojo.uri.Uri object instead
		var arr = [];
		for(var i = 0; i < arguments.length; i++) { arr.push(arguments[i]); }
		return arr.join("/").replace(/\/{2,}/g, "/").replace(/((https*|ftps*):)/i, "$1/");
	}
	
	this.dojoUri = function (uri) {
		// returns a Uri object resolved relative to the dojo root
		return new dojo.uri.Uri(dojo.hostenv.getBaseScriptUri(), uri);
	}
		
	this.Uri = function (/*uri1, uri2, [...]*/) {
		// An object representing a Uri.
		// Each argument is evaluated in order relative to the next until
		// a conanical uri is producued. To get an absolute Uri relative
		// to the current document use
		//      new dojo.uri.Uri(document.baseURI, uri)

		// TODO: support for IPv6, see RFC 2732

		// resolve uri components relative to each other
		var uri = arguments[0];
		for (var i = 1; i < arguments.length; i++) {
			if(!arguments[i]) { continue; }

			// Safari doesn't support this.constructor so we have to be explicit
			var relobj = new dojo.uri.Uri(arguments[i].toString());
			var uriobj = new dojo.uri.Uri(uri.toString());

			if (relobj.path == "" && relobj.scheme == null &&
				relobj.authority == null && relobj.query == null) {
				if (relobj.fragment != null) { uriobj.fragment = relobj.fragment; }
				relobj = uriobj;
			} else if (relobj.scheme == null) {
				relobj.scheme = uriobj.scheme;
			
				if (relobj.authority == null) {
					relobj.authority = uriobj.authority;
					
					if (relobj.path.charAt(0) != "/") {
						var path = uriobj.path.substring(0,
							uriobj.path.lastIndexOf("/") + 1) + relobj.path;

						var segs = path.split("/");
						for (var j = 0; j < segs.length; j++) {
							if (segs[j] == ".") {
								if (j == segs.length - 1) { segs[j] = ""; }
								else { segs.splice(j, 1); j--; }
							} else if (j > 0 && !(j == 1 && segs[0] == "") &&
								segs[j] == ".." && segs[j-1] != "..") {

								if (j == segs.length - 1) { segs.splice(j, 1); segs[j - 1] = ""; }
								else { segs.splice(j - 1, 2); j -= 2; }
							}
						}
						relobj.path = segs.join("/");
					}
				}
			}

			uri = "";
			if (relobj.scheme != null) { uri += relobj.scheme + ":"; }
			if (relobj.authority != null) { uri += "//" + relobj.authority; }
			uri += relobj.path;
			if (relobj.query != null) { uri += "?" + relobj.query; }
			if (relobj.fragment != null) { uri += "#" + relobj.fragment; }
		}

		this.uri = uri.toString();

		// break the uri into its main components
		var regexp = "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";
	    var r = this.uri.match(new RegExp(regexp));

		this.scheme = r[2] || (r[1] ? "" : null);
		this.authority = r[4] || (r[3] ? "" : null);
		this.path = r[5]; // can never be undefined
		this.query = r[7] || (r[6] ? "" : null);
		this.fragment  = r[9] || (r[8] ? "" : null);
		
		if (this.authority != null) {
			// server based naming authority
			regexp = "^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$";
			r = this.authority.match(new RegExp(regexp));
			
			this.user = r[3] || null;
			this.password = r[4] || null;
			this.host = r[5];
			this.port = r[7] || null;
		}
	
		this.toString = function(){ return this.uri; }
	}
};

IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 496
E..$.q@.@.Q`.........^.../3................
.......cGET /scooby/js/lib/dojo-event_and_io/src/widget/HtmlWidget.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 4761
E...u.@.@..f...........^...../4....V.......
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"4549-1147389476000"
Last-Modified: Thu, 11 May 2006 23:17:56 GMT
Content-Type: text/javascript
Content-Length: 4549
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.widget.HtmlWidget");
dojo.require("dojo.widget.DomWidget");
dojo.require("dojo.html");
dojo.require("dojo.html.extras");
dojo.require("dojo.lang.extras");
dojo.require("dojo.lang.func");
dojo.require("dojo.lfx.toggle");

dojo.declare("dojo.widget.HtmlWidget", dojo.widget.DomWidget, {								 
	widgetType: "HtmlWidget",

	templateCssPath: null,
	templatePath: null,

	// for displaying/hiding widget
	toggle: "plain",
	toggleDuration: 150,

	animationInProgress: false,

	initialize: function(args, frag){
	},

	postMixInProperties: function(args, frag){
		// now that we know the setting for toggle, get toggle object
		// (default to plain toggler if user specified toggler not present)
		this.toggleObj =
			dojo.lfx.toggle[this.toggle.toLowerCase()] || dojo.lfx.toggle.plain;
	},

	getContainerHeight: function(){
		// NOTE: container height must be returned as the INNER height
		dojo.unimplemented("dojo.widget.HtmlWidget.getContainerHeight");
	},

	getContainerWidth: function(){
		return this.parent.domNode.offsetWidth;
	},

	setNativeHeight: function(height){
		var ch = this.getContainerHeight();
	},

	createNodesFromText: function(txt, wrap){
		return dojo.html.createNodesFromText(txt, wrap);
	},

	destroyRendering: function(finalize){
		try{
			if(!finalize){
				dojo.event.browser.clean(this.domNode);
			}
			this.domNode.parentNode.removeChild(this.domNode);
			delete this.domNode;
		}catch(e){ /* squelch! */ }
	},

	/////////////////////////////////////////////////////////
	// Displaying/hiding the widget
	/////////////////////////////////////////////////////////
	isShowing: function(){
		return dojo.style.isShowing(this.domNode);
	},

	toggleShowing: function(){
		// dojo.style.toggleShowing(this.domNode);
		if(this.isHidden){
			this.show();
		}else{
			this.hide();
		}
	},

	show: function(){
		this.animationInProgress=true;
		this.isHidden = false;
		this.toggleObj.show(this.domNode, this.toggleDuration, null,
			dojo.lang.hitch(this, this.onShow), this.explodeSrc);
	},

	// called after the show() animation has completed
	onShow: function(){
		this.animationInProgress=false;
	},

	hide: function(){
		this.animationInProgress = true;
		this.isHidden = true;
		this.toggleObj.hide(this.domNode, this.toggleDuration, null,
			dojo.lang.hitch(this, this.onHide), this.explodeSrc);
	},

	// called after the hide() animation has completed
	onHide: function(){
		this.animationInProgress=false;
	},

	//////////////////////////////////////////////////////////////////////////////
	// Sizing related methods
	//  If the parent changes size then for each child it should call either
	//   - resizeTo(): size the child explicitly
	//   - or onParentResized(): notify the child the the parent has changed size
	//////////////////////////////////////////////////////////////////////////////

	// Test if my size has changed.
	// If width & height are specified then that's my new size; otherwise,
	// query outerWidth/outerHeight of my domNode
	_isResized: function(w, h){
		// If I'm not being displayed then disregard (show() must
		// check if the size has changed)
		if(!this.isShowing()){ return false; }

		// If my parent has been resized and I have style="height: 100%"
		// or something similar then my size has changed too.
		w=w||dojo.style.getOuterWidth(this.domNode);
		h=h||dojo.style.getOuterHeight(this.domNode);
		if(this.width == w && this.height == h){ return false; }

		this.width=w;
		this.height=h;
		return true;
	},

	// Called when my parent has changed size, but my parent won't call resizeTo().
	// This is useful if my size is height:100% or something similar
	onParentResized: function(){
		if(!this._isResized()){ return; }
		this.onResized();
	},

	// Explicitly set this widget's size (in pixels).
	resizeTo: function(w, h){
		if(!this._isResized(w,h)){ return; }
		dojo.style.setOuterWidth(this.domNode, w);
		dojo.style.setOuterHeight(this.domNode, h);
		this.onResized();
	},

	resizeSoon: function(){
		if(this.isShowing()){
			dojo.lang.setTimeout(this, this.onResized, 0);
		}
	},

	// Called when my size has changed.
	// Must notify children if their size has (possibly) changed
	onResized: function(){
		dojo.lang.forEach(this.children, function(child){ child.onParentResized(); });
	}
});

IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 483
E.....@.@.s?.........`....
T.8.............
.......{GET /scooby/js/lib/dojo-event_and_io/src/html.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.%............`.8.....7...T!Q.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"15660-1147389474000"
Last-Modified: Thu, 11 May 2006 23:17:54 GMT
Content-Type: text/javascript
Content-Length: 15660
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.html");

dojo.require("dojo.lang.func");
dojo.require("dojo.dom");
dojo.require("dojo.style");
dojo.require("dojo.string");

dojo.lang.mixin(dojo.html, dojo.dom);
dojo.lang.mixin(dojo.html, dojo.style);

// FIXME: we are going to assume that we can throw any and every rendering
// engine into the IE 5.x box model. In Mozilla, we do this w/ CSS.
// Need to investigate for KHTML and Opera

dojo.html.clearSelection = function(){
	try{
		if(window["getSelection"]){ 
			if(dojo.render.html.safari){
				// pulled from WebCore/ecma/kjs_window.cpp, line 2536
				window.getSelection().collapse();
			}else{
				window.getSelection().removeAllRanges();
			}
		}else if(document.selection){
			if(document.selection.empty){
				document.selection.empty();
			}else if(document.selection.clear){
				document.selection.clear();
			}
		}
		return true;
	}catch(e){
		dojo.debug(e);
		return false;
	}
}

dojo.html.disableSelection = function(element){
	element = dojo.byId(element)||document.body;
	var h = dojo.render.html;
	
	if(h.mozilla){
		element.style.MozUserSelect = "none";
	}else if(h.safari){
		element.style.KhtmlUserSelect = "none"; 
	}else if(h.ie){
		element.unselectable = "on";
	}else{
		return false;
	}
	return true;
}

dojo.html.enableSelection = function(element){
	element = dojo.byId(element)||document.body;
	
	var h = dojo.render.html;
	if(h.mozilla){ 
		element.style.MozUserSelect = ""; 
	}else if(h.safari){
		element.style.KhtmlUserSelect = "";
	}else if(h.ie){
		element.unselectable = "off";
	}else{
		return false;
	}
	return true;
}

dojo.html.selectElement = function(element){
	element = dojo.byId(element);
	if(document.selection && document.body.createTextRange){ // IE
		var range = document.body.createTextRange();
		range.moveToElementText(element);
		range.select();
	}else if(window["getSelection"]){
		var selection = window.getSelection();
		// FIXME: does this work on Safari?
		if(selection["selectAllChildren"]){ // Mozilla
			selection.selectAllChildren(element);
		}
	}
}

dojo.html.selectInputText = function(element){
	element = dojo.byId(element);
	if(document.selection && document.body.createTextRange){ // IE
		var range = element.createTextRange();
		range.moveStart("character", 0);
		range.moveEnd("character", element.value.length);
		range.select();
	}else if(window["getSelection"]){
		var selection = window.getSelection();
		// FIXME: does this work on Safari?
		element.setSelectionRange(0, element.value.length);
	}
	element.focus();
}


dojo.html.isSelectionCollapsed = function(){
	if(document["selection"]){ // IE
		return document.selection.createRange().text == "";
	}else if(window["getSelection"]){
		var selection = window.getSelection();
		if(dojo.lang.isString(selection)){ // Safari
			return selection == "";
		}else{ // Mozilla/W3
			return selection.isCollapsed;
		}
	}
}

dojo.html.getEventTarget = function(evt){
	if(!evt) { evt = window.event || {} };
	var t = (evt.srcElement ? evt.srcElement : (evt.target ? evt.target : null));
	while((t)&&(t.nodeType!=1)){ t = t.parentNode; }
	return t;
}

dojo.html.getDocumentWidth = function(){
	dojo.deprecated("dojo.html.getDocument* has been deprecated in favor of dojo.html.getViewport*");
	return dojo.html.getViewportWidth();
}

dojo.html.getDocumentHeight = function(){
	dojo.deprecated("dojo.html.getDocument* has been deprecated in favor of dojo.html.getViewport*");
	return dojo.html.getViewportHeight();
}

dojo.html.getDocumentSize = function(){
	dojo.deprecated("dojo.html.getDocument* has been deprecated in favor of dojo.html.getViewport*");
	return dojo.html.getViewportSize();
}

dojo.html.getViewportWidth = function(){
	var w = 0;

	if(window.innerWidth){
		w = window.innerWidth;
	}

	if(dojo.exists(document, "documentElement.clientWidth")){
		// IE6 Strict
		var w2 = document.documentElement.clientWidth;
		// this lets us account for scrollbars
		if(!w || w2 && w2 < w) {
			w = w2;
		}
		return w;
	}

	if(document.body){
		// IE
		return document.body.clientWidth;
	}

	return 0;
}

dojo.html.getViewportHeight = function(){
	if (window.innerHeight){
		return window.innerHeight;
	}

	if (dojo.exists(document, "documentElement.clientHeight")){
		// IE6 Strict
		return document.documentElement.clientHeight;
	}

	if (document.body){
		// IE
		return document.body.clientHeight;
	}

	return 0;
}

dojo.html.getViewportSize = function(){
	var ret = [dojo.html.getViewportWidth(), dojo.html.getViewportHeight()];
	ret.w = ret[0];
	ret.h = ret[1];
	return ret;
}

dojo.html.getScrollTop = function(){
	return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
}

dojo.html.getScrollLeft = function(){
	return window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
}

dojo.html.getScrollOffset = function(){
	var off = [dojo.html.getScrollLeft(), dojo.html.getScrollTop()];
	off.x = off[0];
	off.y = off[1];
	return off;
}

dojo.html.getParentOfType = function(node, type){
	dojo.deprecated("dojo.html.getParentOfType has been deprecated in favor of dojo.html.getParentByType*");
	return dojo.html.getParentByType(node, type);
}

dojo.html.getParentByType = function(node, type) {
	var parent = dojo.byId(node);
	type = type.toLowerCase();
	while((parent)&&(parent.nodeName.toLowerCase()!=type)){
		if(parent==(document["body"]||document["documentElement"])){
			return null;
		}
		parent = parent.parentNode;
	}
	return parent;
}

// RAR: this function comes from nwidgets and is more-or-less unmodified.
// We should probably look ant Burst and f(m)'s equivalents
dojo.html.getAttribute = function(node, attr){
	node = dojo.byId(node);
	// FIXME: need to add support for attr-specific accessors
	if((!node)||(!node.getAttribute)){
		// if(attr !== 'nwType'){
		//	alert("getAttr of '" + attr + "' with bad node"); 
		// }
		return null;
	}
	var ta = typeof attr == 'string' ? attr : new String(attr);

	// first try the approach most likely to succeed
	var v = node.getAttribute(ta.toUpperCase());
	if((v)&&(typeof v == 'string')&&(v!="")){ return v; }

	// try returning the attributes value, if we couldn't get it as a string
	if(v && v.value){ return v.value; }

	// this should work on Opera 7, but it's a little on the crashy side
	if((node.getAttributeNode)&&(node.getAttributeNode(ta))){
		return (node.getAttributeNode(ta)).value;
	}else if(node.getAttribute(ta)){
		return node.getAttribute(ta);
	}else if(node.getAttribute(ta.toLowerCase())){
		return node.getAttribute(ta.toLowerCase());
	}
	return null;
}
	
/**
 *	Determines whether or not the specified node carries a value for the
 *	attribute in question.
 */
dojo.html.hasAttribute = function(node, attr){
	node = dojo.byId(node);
	return dojo.html.getAttribute(node, attr) ? true : false;
}
	
/**
 * Returns the string value of the list of CSS classes currently assigned
 * directly to the node in question. Returns an empty string if no class attribute
 * is found;
 */
dojo.html.getClass = function(node){
	node = dojo.byId(node);
	if(!node){ return ""; }
	var cs = "";
	if(node.className){
		cs = node.className;
	}else if(dojo.html.hasAttribute(node, "class")){
		cs = dojo.html.getAttribute(node, "class");
	}
	return dojo.string.trim(cs);
}

/**
 * Returns an array of CSS classes currently assigned
 * directly to the node in question. Returns an empty array if no classes
 * are found;
 */
dojo.html.getClasses = function(node) {
	var c = dojo.html.getClass(node);
	return (c == "") ? [] : c.split(/\s+/g);
}

/**
 * Returns whether or not the specified classname is a portion of the
 * class list currently applied to the node. Does not cover cascaded
 * styles, only classes directly applied to the node.
 */
dojo.html.hasClass = function(node, classname){
	return dojo.lang.inArray(dojo.html.getClasses(node), classname);
}

/**
 * Adds the specified class to the beginning of the class list on the
 * passed node. This gives the specified class the highest precidence
 * when style cascading is calculated for the node. Returns true or
 * false; indicating success or failure of the operation, respectively.
 */
dojo.html.prependClass = function(node, classStr){
	classStr += " " + dojo.html.getClass(node);
	return dojo.html.setClass(node, classStr);
}

/**
 * Adds the specified class to the end of the class list on the
 *	passed &node;. Returns &true; or &false; indicating success or failure.
 */
dojo.html.addClass = function(node, classStr){
	if (dojo.html.hasClass(node, classStr)) {
	  return fa
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 6874
E.....@.@.-R...........`.8.....7...T.......
........lse;
	}
	classStr = dojo.string.trim(dojo.html.getClass(node) + " " + classStr);
	return dojo.html.setClass(node, classStr);
}

/**
 *	Clobbers the existing list of classes for the node, replacing it with
 *	the list given in the 2nd argument. Returns true or false
 *	indicating success or failure.
 */
dojo.html.setClass = function(node, classStr){
	node = dojo.byId(node);
	var cs = new String(classStr);
	try{
		if(typeof node.className == "string"){
			node.className = cs;
		}else if(node.setAttribute){
			node.setAttribute("class", classStr);
			node.className = cs;
		}else{
			return false;
		}
	}catch(e){
		dojo.debug("dojo.html.setClass() failed", e);
	}
	return true;
}

/**
 * Removes the className from the node;. Returns
 * true or false indicating success or failure.
 */ 
dojo.html.removeClass = function(node, classStr, allowPartialMatches){
	var classStr = dojo.string.trim(new String(classStr));

	try{
		var cs = dojo.html.getClasses(node);
		var nca	= [];
		if(allowPartialMatches){
			for(var i = 0; i<cs.length; i++){
				if(cs[i].indexOf(classStr) == -1){ 
					nca.push(cs[i]);
				}
			}
		}else{
			for(var i=0; i<cs.length; i++){
				if(cs[i] != classStr){ 
					nca.push(cs[i]);
				}
			}
		}
		dojo.html.setClass(node, nca.join(" "));
	}catch(e){
		dojo.debug("dojo.html.removeClass() failed", e);
	}

	return true;
}

/**
 * Replaces 'oldClass' and adds 'newClass' to node
 */
dojo.html.replaceClass = function(node, newClass, oldClass) {
	dojo.html.removeClass(node, oldClass);
	dojo.html.addClass(node, newClass);
}

// Enum type for getElementsByClass classMatchType arg:
dojo.html.classMatchType = {
	ContainsAll : 0, // all of the classes are part of the node's class (default)
	ContainsAny : 1, // any of the classes are part of the node's class
	IsOnly : 2 // only all of the classes are part of the node's class
}


/**
 * Returns an array of nodes for the given classStr, children of a
 * parent, and optionally of a certain nodeType
 */
dojo.html.getElementsByClass = function(classStr, parent, nodeType, classMatchType){
	parent = dojo.byId(parent) || document;
	var classes = classStr.split(/\s+/g);
	var nodes = [];
	if( classMatchType != 1 && classMatchType != 2 ) classMatchType = 0; // make it enum
	var reClass = new RegExp("(\\s|^)((" + classes.join(")|(") + "))(\\s|$)");

	// FIXME: doesn't have correct parent support!
	if(!nodeType){ nodeType = "*"; }
	var candidateNodes = parent.getElementsByTagName(nodeType);

	var node, i = 0;
	outer:
	while (node = candidateNodes[i++]) {
		var nodeClasses = dojo.html.getClasses(node);
		if(nodeClasses.length == 0) { continue outer; }
		var matches = 0;

		for(var j = 0; j < nodeClasses.length; j++) {
			if( reClass.test(nodeClasses[j]) ) {
				if( classMatchType == dojo.html.classMatchType.ContainsAny ) {
					nodes.push(node);
					continue outer;
				} else {
					matches++;
				}
			} else {
				if( classMatchType == dojo.html.classMatchType.IsOnly ) {
					continue outer;
				}
			}
		}

		if( matches == classes.length ) {
			if( classMatchType == dojo.html.classMatchType.IsOnly && matches == nodeClasses.length ) {
				nodes.push(node);
			} else if( classMatchType == dojo.html.classMatchType.ContainsAll ) {
				nodes.push(node);
			}
		}
	}
	
	return nodes;
}

dojo.html.getElementsByClassName = dojo.html.getElementsByClass;

/**
 * Returns the mouse position relative to the document (not the viewport).
 * For example, if you have a document that is 10000px tall,
 * but your browser window is only 100px tall,
 * if you scroll to the bottom of the document and call this function it
 * will return {x: 0, y: 10000}
 */
dojo.html.getCursorPosition = function(e){
	e = e || window.event;
	var cursor = {x:0, y:0};
	if(e.pageX || e.pageY){
		cursor.x = e.pageX;
		cursor.y = e.pageY;
	}else{
		var de = document.documentElement;
		var db = document.body;
		cursor.x = e.clientX + ((de||db)["scrollLeft"]) - ((de||db)["clientLeft"]);
		cursor.y = e.clientY + ((de||db)["scrollTop"]) - ((de||db)["clientTop"]);
	}
	return cursor;
}

dojo.html.overElement = function(element, e){
	element = dojo.byId(element);
	var mouse = dojo.html.getCursorPosition(e);

	with(dojo.html){
		var top = getAbsoluteY(element, true);
		var bottom = top + getInnerHeight(element);
		var left = getAbsoluteX(element, true);
		var right = left + getInnerWidth(element);
	}
	
	return (mouse.x >= left && mouse.x <= right &&
		mouse.y >= top && mouse.y <= bottom);
}

dojo.html.setActiveStyleSheet = function(title){
	var i = 0, a, els = document.getElementsByTagName("link");
	while (a = els[i++]) {
		if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")){
			a.disabled = true;
			if (a.getAttribute("title") == title) { a.disabled = false; }
		}
	}
}

dojo.html.getActiveStyleSheet = function(){
	var i = 0, a, els = document.getElementsByTagName("link");
	while (a = els[i++]) {
		if (a.getAttribute("rel").indexOf("style") != -1 &&
			a.getAttribute("title") && !a.disabled) { return a.getAttribute("title"); }
	}
	return null;
}

dojo.html.getPreferredStyleSheet = function(){
	var i = 0, a, els = document.getElementsByTagName("link");
	while (a = els[i++]) {
		if(a.getAttribute("rel").indexOf("style") != -1
			&& a.getAttribute("rel").indexOf("alt") == -1
			&& a.getAttribute("title")) { return a.getAttribute("title"); }
	}
	return null;
}

dojo.html.body = function(){
	dojo.deprecated("dojo.html.body", "use document.body instead");
	return document.body || document.getElementsByTagName("body")[0];
}

/**
 * Like dojo.dom.isTag, except case-insensitive
**/
dojo.html.isTag = function(node /* ... */) {
	node = dojo.byId(node);
	if(node && node.tagName) {
		var arr = dojo.lang.map(dojo.lang.toArray(arguments, 1),
			function(a) { return String(a).toLowerCase(); });
		return arr[ dojo.lang.find(node.tagName.toLowerCase(), arr) ] || "";
	}
	return "";
}

dojo.html._callExtrasDeprecated = function(inFunc, args) {
	var module = "dojo.html.extras";
	dojo.deprecated("dojo.html." + inFunc + " has been moved to " + module);
	dojo["require"](module); // weird syntax to fool list-profile-deps (build)
	return dojo.html[inFunc].apply(dojo.html, args);
}

dojo.html.createNodesFromText = function() {
	return dojo.html._callExtrasDeprecated('createNodesFromText', arguments);
}

dojo.html.gravity = function() {
	return dojo.html._callExtrasDeprecated('gravity', arguments);
}

dojo.html.placeOnScreen = function() {
	return dojo.html._callExtrasDeprecated('placeOnScreen', arguments);
}

dojo.html.placeOnScreenPoint = function() {
	return dojo.html._callExtrasDeprecated('placeOnScreenPoint', arguments);
}

dojo.html.renderedTextContent = function() {
	return dojo.html._callExtrasDeprecated('renderedTextContent', arguments);
}

dojo.html.BackgroundIframe = function() {
	return dojo.html._callExtrasDeprecated('BackgroundIframe', arguments);
}

IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 484
E....6@.@............_....c].'.W.....
.....
........GET /scooby/js/lib/dojo-event_and_io/src/style.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 9000
E.#\N.@.@..z..........._.'.W..eA...l!Q.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"24377-1147389474000"
Last-Modified: Thu, 11 May 2006 23:17:54 GMT
Content-Type: text/javascript
Content-Length: 24377
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.style");
dojo.require("dojo.graphics.color");
dojo.require("dojo.uri.Uri");
dojo.require("dojo.lang.common");

(function(){
	var h = dojo.render.html;
	var ds = dojo.style;
	var db = document["body"]||document["documentElement"];

	ds.boxSizing = {
		MARGIN_BOX: "margin-box",
		BORDER_BOX: "border-box",
		PADDING_BOX: "padding-box",
		CONTENT_BOX: "content-box"
	};
	var bs = ds.boxSizing;
	
	ds.getBoxSizing = function(node){
		if((h.ie)||(h.opera)){ 
			var cm = document["compatMode"];
			if((cm == "BackCompat")||(cm == "QuirksMode")){ 
				return bs.BORDER_BOX; 
			}else{
				return bs.CONTENT_BOX; 
			}
		}else{
			if(arguments.length == 0){ node = document.documentElement; }
			var sizing = ds.getStyle(node, "-moz-box-sizing");
			if(!sizing){ sizing = ds.getStyle(node, "box-sizing"); }
			return (sizing ? sizing : bs.CONTENT_BOX);
		}
	}

	/*

	The following several function use the dimensions shown below

		+-------------------------+
		|  margin                 |
		| +---------------------+ |
		| |  border             | |
		| | +-----------------+ | |
		| | |  padding        | | |
		| | | +-------------+ | | |
		| | | |   content   | | | |
		| | | +-------------+ | | |
		| | +-|-------------|-+ | |
		| +-|-|-------------|-|-+ |
		+-|-|-|-------------|-|-|-+
		| | | |             | | | |
		| | | |<- content ->| | | |
		| |<------ inner ------>| |
		|<-------- outer -------->|
		+-------------------------+

		* content-box

		|m|b|p|             |p|b|m|
		| |<------ offset ----->| |
		| | |<---- client --->| | |
		| | | |<-- width -->| | | |

		* border-box

		|m|b|p|             |p|b|m|
		| |<------ offset ----->| |
		| | |<---- client --->| | |
		| |<------ width ------>| |
	*/

	/*
		Notes:

		General:
			- Uncomputable values are returned as NaN.
			- setOuterWidth/Height return *false* if the outer size could not
			  be computed, otherwise *true*.
			- (sjmiles) knows no way to find the calculated values for auto-margins. 
			- All returned values are floating point in 'px' units. If a
			  non-zero computed style value is not specified in 'px', NaN is
			  returned.

		FF:
			- styles specified as '0' (unitless 0) show computed as '0pt'.

		IE:
			- clientWidth/Height are unreliable (0 unless the object has 'layout').
			- margins must be specified in px, or 0 (in any unit) for any
			  sizing function to work. Otherwise margins detect as 'auto'.
			- padding can be empty or, if specified, must be in px, or 0 (in
			  any unit) for any sizing function to work.

		Safari:
			- Safari defaults padding values to 'auto'.

		See the unit tests for examples of (un)computable values in a given browser.

	*/

	// FIXME: these work for some elements (e.g. DIV) but not others (e.g. TABLE, TEXTAREA)

	ds.isBorderBox = function(node){
		return (ds.getBoxSizing(node) == bs.BORDER_BOX);
	}

	ds.getUnitValue = function(node, cssSelector, autoIsZero){
		var s = ds.getComputedStyle(node, cssSelector);
		if((!s)||((s == 'auto')&&(autoIsZero))){ return { value: 0, units: 'px' }; }
		if(dojo.lang.isUndefined(s)){return ds.getUnitValue.bad;}
		// FIXME: is regex inefficient vs. parseInt or some manual test? 
		var match = s.match(/(\-?[\d.]+)([a-z%]*)/i);
		if (!match){return ds.getUnitValue.bad;}
		return { value: Number(match[1]), units: match[2].toLowerCase() };
	}
	// FIXME: 'bad' value should be 0?
	ds.getUnitValue.bad = { value: NaN, units: '' };
	
	ds.getPixelValue = function(node, cssSelector, autoIsZero){
		var result = ds.getUnitValue(node, cssSelector, autoIsZero);
		// FIXME: there is serious debate as to whether or not this is the right solution
		if(isNaN(result.value)){ return 0; }
		// FIXME: code exists for converting other units to px (see Dean Edward's IE7) 
		// but there are cross-browser complexities
		if((result.value)&&(result.units != 'px')){ return NaN; }
		return result.value;
	}
	
	// FIXME: deprecated
	ds.getNumericStyle = function() {
		dojo.deprecated('dojo.(style|html).getNumericStyle', 'in favor of dojo.(style|html).getPixelValue', '0.4');
		return ds.getPixelValue.apply(this, arguments); 
	}

	ds.setPositivePixelValue = function(node, selector, value){
		if(isNaN(value)){return false;}
		node.style[selector] = Math.max(0, value) + 'px'; 
		return true;
	}
	
	ds._sumPixelValues = function(node, selectors, autoIsZero){
		var total = 0;
		for(x=0; x<selectors.length; x++){
			total += ds.getPixelValue(node, selectors[x], autoIsZero);
		}
		return total;
	}

	ds.isPositionAbsolute = function(node){
		return (ds.getComputedStyle(node, 'position') == 'absolute');
	}

	ds.getBorderExtent = function(node, side){
		return (ds.getStyle(node, 'border-' + side + '-style') == 'none' ? 0 : ds.getPixelValue(node, 'border-' + side + '-width'));
	}

	ds.getMarginWidth = function(node){
		return ds._sumPixelValues(node, ["margin-left", "margin-right"], ds.isPositionAbsolute(node));
	}

	ds.getBorderWidth = function(node){
		return ds.getBorderExtent(node, 'left') + ds.getBorderExtent(node, 'right');
	}

	ds.getPaddingWidth = function(node){
		return ds._sumPixelValues(node, ["padding-left", "padding-right"], true);
	}

	ds.getPadBorderWidth = function(node) {
		return ds.getPaddingWidth(node) + ds.getBorderWidth(node);
	}
	
	ds.getContentBoxWidth = function(node){
		node = dojo.byId(node);
		return node.offsetWidth - ds.getPadBorderWidth(node);
	}

	ds.getBorderBoxWidth = function(node){
		node = dojo.byId(node);
		return node.offsetWidth;
	}

	ds.getMarginBoxWidth = function(node){
		return ds.getInnerWidth(node) + ds.getMarginWidth(node);
	}

	ds.setContentBoxWidth = function(node, pxWidth){
		node = dojo.byId(node);
		if (ds.isBorderBox(node)){
			pxWidth += ds.getPadBorderWidth(node);
		}
		return ds.setPositivePixelValue(node, "width", pxWidth);
	}

	ds.setMarginBoxWidth = function(node, pxWidth){
		node = dojo.byId(node);
		if (!ds.isBorderBox(node)){
			pxWidth -= ds.getPadBorderWidth(node);
		}
		pxWidth -= ds.getMarginWidth(node);
		return ds.setPositivePixelValue(node, "width", pxWidth);
	}

	// FIXME: deprecate and remove
	ds.getContentWidth = ds.getContentBoxWidth;
	ds.getInnerWidth = ds.getBorderBoxWidth;
	ds.getOuterWidth = ds.getMarginBoxWidth;
	ds.setContentWidth = ds.setContentBoxWidth;
	ds.setOuterWidth = ds.setMarginBoxWidth;

	ds.getMarginHeight = function(node){
		return ds._sumPixelValues(node, ["margin-top", "margin-bottom"], ds.isPositionAbsolute(node));
	}

	ds.getBorderHeight = function(node){
		return ds.getBorderExtent(node, 'top') + ds.getBorderExtent(node, 'bottom');
	}

	ds.getPaddingHeight = function(node){
		return ds._sumPixelValues(node, ["padding-top", "padding-bottom"], true);
	}

	ds.getPadBorderHeight = function(node) {
		return ds.getPaddingHeight(node) + ds.getBorderHeight(node);
	}
	
	ds.getContentBoxHeight = function(node){
		node = dojo.byId(node);
		return node.offsetHeight - ds.getPadBorderHeight(node);
	}

	ds.getBorderBoxHeight = function(node){
		node = dojo.byId(node);
		return node.offsetHeight; // FIXME: does this work?
	}

	ds.getMarginBoxHeight = function(node){
		return ds.getInnerHeight(node) + ds.getMarginHeight(node);
	}

	ds.setContentBoxHeight = function(node, pxHeight){
		node = dojo.byId(node);
		if (ds.isBorderBox(node)){
			pxHeight += ds.getPadBorderHeight(node);
		}
		return ds.setPositivePixelValue(node, "height", pxHeight);
	}

	ds.setMarginBoxHeight = function(node, pxHeight){
		node = dojo.byId(node);
		if (!ds.isBorderBox(node)){
			pxHeight -= ds.getPadBorderHeight(node);
		}
		pxHeight -= ds.getMarginHeight(node);
		return ds.setPositivePixelValue(node, "height", pxHeight);
	}

	// FIXME: deprecate and remove
	ds.getContentHeight = ds.getContentBoxHeight;
	ds.getInnerHeight = ds.getBorderBoxHeight;
	ds.getOuterHeight = ds.getMarginBoxHeight;
	ds.setContentHeight = ds.setContentBoxHeight;
	ds.setOuterHeight = ds.setMarginBoxHeight;

	/**
	 * dojo.style.getAbsolutePosition(xyz, true) returns xyz's position relative to the document.
	 * Itells you where you would position a node
	 * inside document.body such that it was on top of xyz.  Most people set the flag to true when calling
	 * getAbsolutePosition().
	 *
	 * dojo.style.getAbsolutePosition(xyz, false) returns xyz's position relative to the viewport.
	 * It returns the position that would be returned
	 * by event.clientX/Y if the mouse were directly over the top/left of this node.
	 */
	ds.getAbsolutePosition = ds.abs = function(node, includeScroll){
		var ret = [];
		ret.x = ret.y = 0;
		var st = dojo.html.ge
IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 9000
E.#\N @.@..y..........._.'....eA...l!Q.....
........tScrollTop();
		var sl = dojo.html.getScrollLeft();

		if(h.ie){
			with(node.getBoundingClientRect()){
				ret.x = left-2;
				ret.y = top-2;
			}
/**
		}else if(document.getBoxObjectFor){
			// mozilla
			var bo=document.getBoxObjectFor(node);
			ret.x=bo.x-sl;
			ret.y=bo.y-st;
**/		}else{
			if(node["offsetParent"]){
				var endNode;		
				// in Safari, if the node is an absolutely positioned child of
				// the body and the body has a margin the offset of the child
				// and the body contain the body's margins, so we need to end
				// at the body
				if(	(h.safari)&&
					(node.style.getPropertyValue("position") == "absolute")&&
					(node.parentNode == db)){
					endNode = db;
				}else{
					endNode = db.parentNode;
				}
				
				if(node.parentNode != db){
					ret.x -= ds.sumAncestorProperties(node, "scrollLeft");
					ret.y -= ds.sumAncestorProperties(node, "scrollTop");
				}
				do{
					var n = node["offsetLeft"];
					ret.x += isNaN(n) ? 0 : n;
					var m = node["offsetTop"];
					ret.y += isNaN(m) ? 0 : m;
					node = node.offsetParent;
				}while((node != endNode)&&(node != null));
			}else if(node["x"]&&node["y"]){
				ret.x += isNaN(node.x) ? 0 : node.x;
				ret.y += isNaN(node.y) ? 0 : node.y;
			}
		}

		// account for document scrolling!
		if(includeScroll){
			ret.y += st;
			ret.x += sl;
		}

		ret[0] = ret.x;
		ret[1] = ret.y;
		return ret;
	}

	ds.sumAncestorProperties = function(node, prop){
		node = dojo.byId(node);
		if(!node){ return 0; } // FIXME: throw an error?
		
		var retVal = 0;
		while(node){
			var val = node[prop];
			if(val){
				retVal += val - 0;
			}
			node = node.parentNode;
		}
		return retVal;
	}

	ds.getTotalOffset = function(node, type, includeScroll){
		node = dojo.byId(node);
		return ds.abs(node, includeScroll)[(type == "top") ? "y" : "x"];
	}

	ds.getAbsoluteX = ds.totalOffsetLeft = function(node, includeScroll){
		return ds.getTotalOffset(node, "left", includeScroll);
	}

	ds.getAbsoluteY = ds.totalOffsetTop = function(node, includeScroll){
		return ds.getTotalOffset(node, "top", includeScroll);
	}

	ds.styleSheet = null;

	// FIXME: this is a really basic stub for adding and removing cssRules, but
	// it assumes that you know the index of the cssRule that you want to add 
	// or remove, making it less than useful.  So we need something that can 
	// search for the selector that you you want to remove.
	ds.insertCssRule = function(selector, declaration, index) {
		if (!ds.styleSheet) {
			if (document.createStyleSheet) { // IE
				ds.styleSheet = document.createStyleSheet();
			} else if (document.styleSheets[0]) { // rest
				// FIXME: should create a new style sheet here
				// fall back on an exsiting style sheet
				ds.styleSheet = document.styleSheets[0];
			} else { return null; } // fail
		}

		if (arguments.length < 3) { // index may == 0
			if (ds.styleSheet.cssRules) { // W3
				index = ds.styleSheet.cssRules.length;
			} else if (ds.styleSheet.rules) { // IE
				index = ds.styleSheet.rules.length;
			} else { return null; } // fail
		}

		if (ds.styleSheet.insertRule) { // W3
			var rule = selector + " { " + declaration + " }";
			return ds.styleSheet.insertRule(rule, index);
		} else if (ds.styleSheet.addRule) { // IE
			return ds.styleSheet.addRule(selector, declaration, index);
		} else { return null; } // fail
	}

	ds.removeCssRule = function(index){
		if(!ds.styleSheet){
			dojo.debug("no stylesheet defined for removing rules");
			return false;
		}
		if(h.ie){
			if(!index){
				index = ds.styleSheet.rules.length;
				ds.styleSheet.removeRule(index);
			}
		}else if(document.styleSheets[0]){
			if(!index){
				index = ds.styleSheet.cssRules.length;
			}
			ds.styleSheet.deleteRule(index);
		}
		return true;
	}

	// calls css by XmlHTTP and inserts it into DOM as <style [widgetType="widgetType"]> *downloaded cssText*</style>
	ds.insertCssFile = function(URI, doc, checkDuplicates){
		if(!URI){ return; }
		if(!doc){ doc = document; }
		var cssStr = dojo.hostenv.getText(URI);
		cssStr = ds.fixPathsInCssText(cssStr, URI);

		if(checkDuplicates){
			var styles = doc.getElementsByTagName("style");
			var cssText = "";
			for(var i = 0; i<styles.length; i++){
				cssText = (styles[i].styleSheet && styles[i].styleSheet.cssText) ? styles[i].styleSheet.cssText : styles[i].innerHTML;
				if(cssStr == cssText){ return; }
			}
		}

		var style = ds.insertCssText(cssStr);
		// insert custom attribute ex dbgHref="../foo.css" usefull when debugging in DOM inspectors, no?
		if(style && djConfig.isDebug){
			style.setAttribute("dbgHref", URI);
		}
		return style
	}

	// DomNode Style  = insertCssText(String ".dojoMenu {color: green;}"[, DomDoc document, dojo.uri.Uri Url ])
	ds.insertCssText = function(cssStr, doc, URI){
		if(!cssStr){ return; }
		if(!doc){ doc = document; }
		if(URI){// fix paths in cssStr
			cssStr = ds.fixPathsInCssText(cssStr, URI);
		}
		var style = doc.createElement("style");
		style.setAttribute("type", "text/css");
		if(style.styleSheet){// IE
			style.styleSheet.cssText = cssStr;
		} else {// w3c
			var cssText = doc.createTextNode(cssStr);
			style.appendChild(cssText);
		}
		var head = doc.getElementsByTagName("head")[0];
		if(!head){ // must have a head tag 
			dojo.debug("No head tag in document, aborting styles");
		}else{
			head.appendChild(style);
		}
		return style;
	}

	// String cssText = fixPathsInCssText(String cssStr, dojo.uri.Uri URI)
	// usage: cssText comes from dojoroot/src/widget/templates/HtmlFoobar.css
	// 	it has .dojoFoo { background-image: url(images/bar.png);} 
	//	then uri should point to dojoroot/src/widget/templates/
	ds.fixPathsInCssText = function(cssStr, URI){
		if(!cssStr || !URI){ return; }
		var pos = 0; var str = ""; var url = "";
		while(pos!=-1){
			pos = 0;url = "";
			pos = cssStr.indexOf("url(", pos);
			if(pos<0){ break; }
			str += cssStr.slice(0,pos+4);
			cssStr = cssStr.substring(pos+4, cssStr.length);
			url += cssStr.match(/^[\t\s\w()\/.\\'"-:#=&?]*\)/)[0]; // url string
			cssStr = cssStr.substring(url.length-1, cssStr.length); // remove url from css string til next loop
			url = url.replace(/^[\s\t]*(['"]?)([\w()\/.\\'"-:#=&?]*)\1[\s\t]*?\)/,"$2"); // clean string
			if(url.search(/(file|https?|ftps?):\/\//)==-1){
				url = (new dojo.uri.Uri(URI,url).toString());
			}
			str += url;
		};
		return str+cssStr;
	}

	ds.getBackgroundColor = function(node) {
		node = dojo.byId(node);
		var color;
		do{
			color = ds.getStyle(node, "background-color");
			// Safari doesn't say "transparent"
			if(color.toLowerCase() == "rgba(0, 0, 0, 0)") { color = "transparent"; }
			if(node == document.getElementsByTagName("body")[0]) { node = null; break; }
			node = node.parentNode;
		}while(node && dojo.lang.inArray(color, ["transparent", ""]));
		if(color == "transparent"){
			color = [255, 255, 255, 0];
		}else{
			color = dojo.graphics.color.extractRGB(color);
		}
		return color;
	}

	ds.getComputedStyle = function(node, cssSelector, inValue){
		node = dojo.byId(node);
		// cssSelector may actually be in camel case, so force selector version
		var cssSelector = ds.toSelectorCase(cssSelector);
		var property = ds.toCamelCase(cssSelector);
		if(!node || !node.style){
			return inValue;
		}else if(document.defaultView){ // W3, gecko, KHTML
			try{			
				var cs = document.defaultView.getComputedStyle(node, "");
				if (cs){ 
					return cs.getPropertyValue(cssSelector);
				} 
			}catch(e){ // reports are that Safari can throw an exception above
				if (node.style.getPropertyValue){ // W3
					return node.style.getPropertyValue(cssSelector);
				}else return inValue;
			}
		}else if(node.currentStyle){ // IE
			return node.currentStyle[property];
		}if(node.style.getPropertyValue){ // W3
			return node.style.getPropertyValue(cssSelector);
		}else{
			return inValue;
		}
	}

	/** 
	 * Retrieve a property value from a node's style object.
	 */
	ds.getStyleProperty = function(node, cssSelector){
		node = dojo.byId(node);
		// FIXME: should we use node.style.getPropertyValue over style[property]?
		// style[property] works in all (modern) browsers, getPropertyValue is W3 but not supported in IE
		// FIXME: what about runtimeStyle?
		return (node && node.style ? node.style[ds.toCamelCase(cssSelector)] : undefined);
	}

	/** 
	 * Retrieve a property value from a node's style object.
	 */
	ds.getStyle = function(node, cssSelector){
		var value = ds.getStyleProperty(node, cssSelector);
		return (value ? value : ds.getComputedStyle(node, cssSelector));
	}

	ds.setStyle = function(node, cssSelector, value){
		node = dojo.byId(node);
		if(node && node.style){
			var camelCased = ds.toCamelCase(cssSelector);
			node.style[camelCased] = value;
		}
	}

	ds.toCamelCase = function(selector) {
		var arr = selector.split('-'), cc = arr[0];
		for(var i = 1; i < arr.length; i++) {
			cc += arr[i].charAt(0).toUpperCase() + arr[i].substring(1);
		}
		return cc;		
	}

	ds.toSelectorCase = function(selector) {
		return selector.replace(/([A-Z])/
IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 6591
E...N!@.@.............._.'....eA...l.......
........g, "-$1" ).toLowerCase() ;
	}

	/* float between 0.0 (transparent) and 1.0 (opaque) */
	ds.setOpacity = function setOpacity(node, opacity, dontFixOpacity) {
		node = dojo.byId(node);
		if(!dontFixOpacity){
			if( opacity >= 1.0){
				if(h.ie){
					ds.clearOpacity(node);
					return;
				}else{
					opacity = 0.999999;
				}
			}else if( opacity < 0.0){ opacity = 0; }
		}
		if(h.ie){
			if(node.nodeName.toLowerCase() == "tr"){
				// FIXME: is this too naive? will we get more than we want?
				var tds = node.getElementsByTagName("td");
				for(var x=0; x<tds.length; x++){
					tds[x].style.filter = "Alpha(Opacity="+opacity*100+")";
				}
			}
			node.style.filter = "Alpha(Opacity="+opacity*100+")";
		}else if(h.moz){
			node.style.opacity = opacity; // ffox 1.0 directly supports "opacity"
			node.style.MozOpacity = opacity;
		}else if(h.safari){
			node.style.opacity = opacity; // 1.3 directly supports "opacity"
			node.style.KhtmlOpacity = opacity;
		}else{
			node.style.opacity = opacity;
		}
	}
		
	ds.getOpacity = function getOpacity (node){
		node = dojo.byId(node);
		if(h.ie){
			var opac = (node.filters && node.filters.alpha &&
				typeof node.filters.alpha.opacity == "number"
				? node.filters.alpha.opacity : 100) / 100;
		}else{
			var opac = node.style.opacity || node.style.MozOpacity ||
				node.style.KhtmlOpacity || 1;
		}
		return opac >= 0.999999 ? 1.0 : Number(opac);
	}

	ds.clearOpacity = function clearOpacity(node){
		node = dojo.byId(node);
		var ns = node.style;
		if(h.ie){
			try {
				if( node.filters && node.filters.alpha ){
					ns.filter = ""; // FIXME: may get rid of other filter effects
				}
			} catch(e) {
				/*
				 * IE7 gives error if node.filters not set;
				 * don't know why or how to workaround (other than this)
				 */
			}
		}else if(h.moz){
			ns.opacity = 1;
			ns.MozOpacity = 1;
		}else if(h.safari){
			ns.opacity = 1;
			ns.KhtmlOpacity = 1;
		}else{
			ns.opacity = 1;
		}
	}

	ds._toggle = function(node, tester, setter){
		node = dojo.byId(node);
		setter(node, !tester(node));
		return tester(node);
	}

	// show/hide are library constructs

	// show() 
	// if the node.style.display == 'none' then 
	// set style.display to '' or the value cached by hide()
	ds.show = function(node){
		node = dojo.byId(node);
		if(ds.getStyleProperty(node, 'display')=='none'){
			ds.setStyle(node, 'display', (node.dojoDisplayCache||''));
			node.dojoDisplayCache = undefined;	// cannot use delete on a node in IE6
		}
	}

	// if the node.style.display == 'none' then 
	// set style.display to '' or the value cached by hide()
	ds.hide = function(node){
		node = dojo.byId(node);
		if(typeof node["dojoDisplayCache"] == "undefined"){ // it could == '', so we cannot say !node.dojoDisplayCount
			var d = ds.getStyleProperty(node, 'display')
			if(d!='none'){
				node.dojoDisplayCache = d;
			}
		}
		ds.setStyle(node, 'display', 'none');
	}

	// setShowing() calls show() if showing is true, hide() otherwise
	ds.setShowing = function(node, showing){
		ds[(showing ? 'show' : 'hide')](node);
	}

	// isShowing() is true if the node.style.display is not 'none'
	// FIXME: returns true if node is bad, isHidden would be easier to make correct
	ds.isShowing = function(node){
		return (ds.getStyleProperty(node, 'display') != 'none');
	}

	// Call setShowing() on node with the complement of isShowing(), then return the new value of isShowing()
	ds.toggleShowing = function(node){
		return ds._toggle(node, ds.isShowing, ds.setShowing);
	}

	// display is a CSS concept

	// Simple mapping of tag names to display values
	// FIXME: simplistic 
	ds.displayMap = { tr: '', td: '', th: '', img: 'inline', span: 'inline', input: 'inline', button: 'inline' };

	// Suggest a value for the display property that will show 'node' based on it's tag
	ds.suggestDisplayByTagName = function(node)
	{
		node = dojo.byId(node);
		if(node && node.tagName){
			var tag = node.tagName.toLowerCase();
			return (tag in ds.displayMap ? ds.displayMap[tag] : 'block');
		}
	}

	// setDisplay() sets the value of style.display to value of 'display' parameter if it is a string.
	// Otherwise, if 'display' is false, set style.display to 'none'.
	// Finally, set 'display' to a suggested display value based on the node's tag
	ds.setDisplay = function(node, display){
		ds.setStyle(node, 'display', (dojo.lang.isString(display) ? display : (display ? ds.suggestDisplayByTagName(node) : 'none')));
	}

	// isDisplayed() is true if the the computed display style for node is not 'none'
	// FIXME: returns true if node is bad, isNotDisplayed would be easier to make correct
	ds.isDisplayed = function(node){
		return (ds.getComputedStyle(node, 'display') != 'none');
	}

	// Call setDisplay() on node with the complement of isDisplayed(), then
	// return the new value of isDisplayed()
	ds.toggleDisplay = function(node){
		return ds._toggle(node, ds.isDisplayed, ds.setDisplay);
	}

	// visibility is a CSS concept

	// setVisibility() sets the value of style.visibility to value of
	// 'visibility' parameter if it is a string.
	// Otherwise, if 'visibility' is false, set style.visibility to 'hidden'.
	// Finally, set style.visibility to 'visible'.
	ds.setVisibility = function(node, visibility){
		ds.setStyle(node, 'visibility', (dojo.lang.isString(visibility) ? visibility : (visibility ? 'visible' : 'hidden')));
	}

	// isVisible() is true if the the computed visibility style for node is not 'hidden'
	// FIXME: returns true if node is bad, isInvisible would be easier to make correct
	ds.isVisible = function(node){
		return (ds.getComputedStyle(node, 'visibility') != 'hidden');
	}

	// Call setVisibility() on node with the complement of isVisible(), then
	// return the new value of isVisible()
	ds.toggleVisibility = function(node){
		return ds._toggle(node, ds.isVisible, ds.setVisibility);
	}

	// in: coordinate array [x,y,w,h] or dom node
	// return: coordinate array
	ds.toCoordinateArray = function(coords, includeScroll) {
		if(dojo.lang.isArray(coords)){
			// coords is already an array (of format [x,y,w,h]), just return it
			while ( coords.length < 4 ) { coords.push(0); }
			while ( coords.length > 4 ) { coords.pop(); }
			var ret = coords;
		} else {
			// coords is an dom object (or dom object id); return it's coordinates
			var node = dojo.byId(coords);
			var pos = ds.getAbsolutePosition(node, includeScroll);
			var ret = [
				pos.x,
				pos.y,
				ds.getBorderBoxWidth(node),
				ds.getBorderBoxHeight(node)
			];
		}
		ret.x = ret[0];
		ret.y = ret[1];
		ret.w = ret[2];
		ret.h = ret[3];
		return ret;
	};
})();

IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 493
E..!$.@.@..=.........[...~....X............
...
....GET /scooby/js/lib/dojo-event_and_io/src/graphics/color.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 4732
E...6j@.@..............[..X..~.|...........
...(...
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"4520-1147389474000"
Last-Modified: Thu, 11 May 2006 23:17:54 GMT
Content-Type: text/javascript
Content-Length: 4520
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.graphics.color");
dojo.require("dojo.lang.array");

// TODO: rewrite the "x2y" methods to take advantage of the parsing
//       abilities of the Color object. Also, beef up the Color
//       object (as possible) to parse most common formats

// takes an r, g, b, a(lpha) value, [r, g, b, a] array, "rgb(...)" string, hex string (#aaa, #aaaaaa, aaaaaaa)
dojo.graphics.color.Color = function(r, g, b, a) {
	// dojo.debug("r:", r[0], "g:", r[1], "b:", r[2]);
	if(dojo.lang.isArray(r)) {
		this.r = r[0];
		this.g = r[1];
		this.b = r[2];
		this.a = r[3]||1.0;
	} else if(dojo.lang.isString(r)) {
		var rgb = dojo.graphics.color.extractRGB(r);
		this.r = rgb[0];
		this.g = rgb[1];
		this.b = rgb[2];
		this.a = g||1.0;
	} else if(r instanceof dojo.graphics.color.Color) {
		this.r = r.r;
		this.b = r.b;
		this.g = r.g;
		this.a = r.a;
	} else {
		this.r = r;
		this.g = g;
		this.b = b;
		this.a = a;
	}
}

dojo.graphics.color.Color.fromArray = function(arr) {
	return new dojo.graphics.color.Color(arr[0], arr[1], arr[2], arr[3]);
}

dojo.lang.extend(dojo.graphics.color.Color, {
	toRgb: function(includeAlpha) {
		if(includeAlpha) {
			return this.toRgba();
		} else {
			return [this.r, this.g, this.b];
		}
	},

	toRgba: function() {
		return [this.r, this.g, this.b, this.a];
	},

	toHex: function() {
		return dojo.graphics.color.rgb2hex(this.toRgb());
	},

	toCss: function() {
		return "rgb(" + this.toRgb().join() + ")";
	},

	toString: function() {
		return this.toHex(); // decent default?
	},

	blend: function(color, weight) {
		return dojo.graphics.color.blend(this.toRgb(), new Color(color).toRgb(), weight);
	}
});

dojo.graphics.color.named = {
	white:      [255,255,255],
	black:      [0,0,0],
	red:        [255,0,0],
	green:	    [0,255,0],
	blue:       [0,0,255],
	navy:       [0,0,128],
	gray:       [128,128,128],
	silver:     [192,192,192]
};

// blend colors a and b (both as RGB array or hex strings) with weight from -1 to +1, 0 being a 50/50 blend
dojo.graphics.color.blend = function(a, b, weight) {
	if(typeof a == "string") { return dojo.graphics.color.blendHex(a, b, weight); }
	if(!weight) { weight = 0; }
	else if(weight > 1) { weight = 1; }
	else if(weight < -1) { weight = -1; }
	var c = new Array(3);
	for(var i = 0; i < 3; i++) {
		var half = Math.abs(a[i] - b[i])/2;
		c[i] = Math.floor(Math.min(a[i], b[i]) + half + (half * weight));
	}
	return c;
}

// very convenient blend that takes and returns hex values
// (will get called automatically by blend when blend gets strings)
dojo.graphics.color.blendHex = function(a, b, weight) {
	return dojo.graphics.color.rgb2hex(dojo.graphics.color.blend(dojo.graphics.color.hex2rgb(a), dojo.graphics.color.hex2rgb(b), weight));
}

// get RGB array from css-style color declarations
dojo.graphics.color.extractRGB = function(color) {
	var hex = "0123456789abcdef";
	color = color.toLowerCase();
	if( color.indexOf("rgb") == 0 ) {
		var matches = color.match(/rgba*\((\d+), *(\d+), *(\d+)/i);
		var ret = matches.splice(1, 3);
		return ret;
	} else {
		var colors = dojo.graphics.color.hex2rgb(color);
		if(colors) {
			return colors;
		} else {
			// named color (how many do we support?)
			return dojo.graphics.color.named[color] || [255, 255, 255];
		}
	}
}

dojo.graphics.color.hex2rgb = function(hex) {
	var hexNum = "0123456789ABCDEF";
	var rgb = new Array(3);
	if( hex.indexOf("#") == 0 ) { hex = hex.substring(1); }
	hex = hex.toUpperCase();
	if(hex.replace(new RegExp("["+hexNum+"]", "g"), "") != "") {
		return null;
	}
	if( hex.length == 3 ) {
		rgb[0] = hex.charAt(0) + hex.charAt(0)
		rgb[1] = hex.charAt(1) + hex.charAt(1)
		rgb[2] = hex.charAt(2) + hex.charAt(2);
	} else {
		rgb[0] = hex.substring(0, 2);
		rgb[1] = hex.substring(2, 4);
		rgb[2] = hex.substring(4);
	}
	for(var i = 0; i < rgb.length; i++) {
		rgb[i] = hexNum.indexOf(rgb[i].charAt(0)) * 16 + hexNum.indexOf(rgb[i].charAt(1));
	}
	return rgb;
}

dojo.graphics.color.rgb2hex = function(r, g, b) {
	if(dojo.lang.isArray(r)) {
		g = r[1] || 0;
		b = r[2] || 0;
		r = r[0] || 0;
	}
	var ret = dojo.lang.map([r, g, b], function(x) {
		x = new Number(x);
		var s = x.toString(16);
		while(s.length < 2) { s = "0" + s; }
		return s;
	});
	ret.unshift("#");
	return ret.join("");
}

IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 490
E....s@.@.Qd.........^.../4...%............
...2....GET /scooby/js/lib/dojo-event_and_io/src/html/extras.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 9000
E.#\u.@.@..............^..%../6....g!Q.....
...V...2HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"12115-1147389474000"
Last-Modified: Thu, 11 May 2006 23:17:54 GMT
Content-Type: text/javascript
Content-Length: 12115
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.require("dojo.html");
dojo.provide("dojo.html.extras");
dojo.require("dojo.string.extras"); 

/**
 * Calculates the mouse's direction of gravity relative to the centre
 * of the given node.
 * <p>
 * If you wanted to insert a node into a DOM tree based on the mouse
 * position you might use the following code:
 * <pre>
 * if (gravity(node, e) & gravity.NORTH) { [insert before]; }
 * else { [insert after]; }
 * </pre>
 *
 * @param node The node
 * @param e		The event containing the mouse coordinates
 * @return		 The directions, NORTH or SOUTH and EAST or WEST. These
 *						 are properties of the function.
 */
dojo.html.gravity = function(node, e){
	node = dojo.byId(node);
	var mouse = dojo.html.getCursorPosition(e);

	with (dojo.html) {
		var nodecenterx = getAbsoluteX(node, true) + (getInnerWidth(node) / 2);
		var nodecentery = getAbsoluteY(node, true) + (getInnerHeight(node) / 2);
	}
	
	with (dojo.html.gravity) {
		return ((mouse.x < nodecenterx ? WEST : EAST) |
			(mouse.y < nodecentery ? NORTH : SOUTH));
	}
}

dojo.html.gravity.NORTH = 1;
dojo.html.gravity.SOUTH = 1 << 1;
dojo.html.gravity.EAST = 1 << 2;
dojo.html.gravity.WEST = 1 << 3;


/**
 * Attempts to return the text as it would be rendered, with the line breaks
 * sorted out nicely. Unfinished.
 */
dojo.html.renderedTextContent = function(node){
	node = dojo.byId(node);
	var result = "";
	if (node == null) { return result; }
	for (var i = 0; i < node.childNodes.length; i++) {
		switch (node.childNodes[i].nodeType) {
			case 1: // ELEMENT_NODE
			case 5: // ENTITY_REFERENCE_NODE
				var display = "unknown";
				try {
					display = dojo.style.getStyle(node.childNodes[i], "display");
				} catch(E) {}
				switch (display) {
					case "block": case "list-item": case "run-in":
					case "table": case "table-row-group": case "table-header-group":
					case "table-footer-group": case "table-row": case "table-column-group":
					case "table-column": case "table-cell": case "table-caption":
						// TODO: this shouldn't insert double spaces on aligning blocks
						result += "\n";
						result += dojo.html.renderedTextContent(node.childNodes[i]);
						result += "\n";
						break;
					
					case "none": break;
					
					default:
						if(node.childNodes[i].tagName && node.childNodes[i].tagName.toLowerCase() == "br") {
							result += "\n";
						} else {
							result += dojo.html.renderedTextContent(node.childNodes[i]);
						}
						break;
				}
				break;
			case 3: // TEXT_NODE
			case 2: // ATTRIBUTE_NODE
			case 4: // CDATA_SECTION_NODE
				var text = node.childNodes[i].nodeValue;
				var textTransform = "unknown";
				try {
					textTransform = dojo.style.getStyle(node, "text-transform");
				} catch(E) {}
				switch (textTransform){
					case "capitalize": text = dojo.string.capitalize(text); break;
					case "uppercase": text = text.toUpperCase(); break;
					case "lowercase": text = text.toLowerCase(); break;
					default: break; // leave as is
				}
				// TODO: implement
				switch (textTransform){
					case "nowrap": break;
					case "pre-wrap": break;
					case "pre-line": break;
					case "pre": break; // leave as is
					default:
						// remove whitespace and collapse first space
						text = text.replace(/\s+/, " ");
						if (/\s$/.test(result)) { text.replace(/^\s/, ""); }
						break;
				}
				result += text;
				break;
			default:
				break;
		}
	}
	return result;
}

dojo.html.createNodesFromText = function(txt, trim){
	if(trim) { txt = dojo.string.trim(txt); }

	var tn = document.createElement("div");
	// tn.style.display = "none";
	tn.style.visibility= "hidden";
	document.body.appendChild(tn);
	var tableType = "none";
	if((/^<t[dh][\s\r\n>]/i).test(dojo.string.trimStart(txt))) {
		txt = "<table><tbody><tr>" + txt + "</tr></tbody></table>";
		tableType = "cell";
	} else if((/^<tr[\s\r\n>]/i).test(dojo.string.trimStart(txt))) {
		txt = "<table><tbody>" + txt + "</tbody></table>";
		tableType = "row";
	} else if((/^<(thead|tbody|tfoot)[\s\r\n>]/i).test(dojo.string.trimStart(txt))) {
		txt = "<table>" + txt + "</table>";
		tableType = "section";
	}
	tn.innerHTML = txt;
	if(tn["normalize"]){
		tn.normalize();
	}

	var parent = null;
	switch(tableType) {
		case "cell":
			parent = tn.getElementsByTagName("tr")[0];
			break;
		case "row":
			parent = tn.getElementsByTagName("tbody")[0];
			break;
		case "section":
			parent = tn.getElementsByTagName("table")[0];
			break;
		default:
			parent = tn;
			break;
	}

	/* this doesn't make much sense, I'm assuming it just meant trim() so wrap was replaced with trim
	if(wrap){ 
		var ret = [];
		// start hack
		var fc = tn.firstChild;
		ret[0] = ((fc.nodeValue == " ")||(fc.nodeValue == "\t")) ? fc.nextSibling : fc;
		// end hack
		// tn.style.display = "none";
		document.body.removeChild(tn);
		return ret;
	}
	*/
	var nodes = [];
	for(var x=0; x<parent.childNodes.length; x++){
		nodes.push(parent.childNodes[x].cloneNode(true));
	}
	tn.style.display = "none"; // FIXME: why do we do this?
	document.body.removeChild(tn);
	return nodes;
}

/* TODO: merge placeOnScreen and placeOnScreenPoint to make 1 function that allows you
 * to define which corner(s) you want to bind to. Something like so:
 *
 * kes(node, desiredX, desiredY, "TR")
 * kes(node, [desiredX, desiredY], ["TR", "BL"])
 *
 * TODO: make this function have variable call sigs
 *
 * kes(node, ptArray, cornerArray, padding, hasScroll)
 * kes(node, ptX, ptY, cornerA, cornerB, cornerC, paddingArray, hasScroll)
 */

/**
 * Keeps 'node' in the visible area of the screen while trying to
 * place closest to desiredX, desiredY. The input coordinates are
 * expected to be the desired screen position, not accounting for
 * scrolling. If you already accounted for scrolling, set 'hasScroll'
 * to true. Set padding to either a number or array for [paddingX, paddingY]
 * to put some buffer around the element you want to position.
 * NOTE: node is assumed to be absolutely or relatively positioned.
 *
 * Alternate call sig:
 *  placeOnScreen(node, [x, y], padding, hasScroll)
 *
 * Examples:
 *  placeOnScreen(node, 100, 200)
 *  placeOnScreen("myId", [800, 623], 5)
 *  placeOnScreen(node, 234, 3284, [2, 5], true)
 */
dojo.html.placeOnScreen = function(node, desiredX, desiredY, padding, hasScroll) {
	if(dojo.lang.isArray(desiredX)) {
		hasScroll = padding;
		padding = desiredY;
		desiredY = desiredX[1];
		desiredX = desiredX[0];
	}

	if(!isNaN(padding)) {
		padding = [Number(padding), Number(padding)];
	} else if(!dojo.lang.isArray(padding)) {
		padding = [0, 0];
	}

	var scroll = dojo.html.getScrollOffset();
	var view = dojo.html.getViewportSize();

	node = dojo.byId(node);
	var w = node.offsetWidth + padding[0];
	var h = node.offsetHeight + padding[1];

	if(hasScroll) {
		desiredX -= scroll.x;
		desiredY -= scroll.y;
	}

	var x = desiredX + w;
	if(x > view.w) {
		x = view.w - w;
	} else {
		x = desiredX;
	}
	x = Math.max(padding[0], x) + scroll.x;

	var y = desiredY + h;
	if(y > view.h) {
		y = view.h - h;
	} else {
		y = desiredY;
	}
	y = Math.max(padding[1], y) + scroll.y;

	node.style.left = x + "px";
	node.style.top = y + "px";

	var ret = [x, y];
	ret.x = x;
	ret.y = y;
	return ret;
}

/**
 * Like placeOnScreenPoint except that it attempts to keep one of the node's
 * corners at desiredX, desiredY.  Favors the bottom right position
 *
 * Examples placing node at mouse position (where e = [Mouse event]):
 *  placeOnScreenPoint(node, e.clientX, e.clientY);
 */
dojo.html.placeOnScreenPoint = function(node, desiredX, desiredY, padding, hasScroll) {
	if(dojo.lang.isArray(desiredX)) {
		hasScroll = padding;
		padding = desiredY;
		desiredY = desiredX[1];
		desiredX = desiredX[0];
	}

	if(!isNaN(padding)) {
		padding = [Number(padding), Number(padding)];
	} else if(!dojo.lang.isArray(padding)) {
		padding = [0, 0];
	}

	var scroll = dojo.html.getScrollOffset();
	var view = dojo.html.getViewportSize();

	node = dojo.byId(node);
	var oldDisplay = node.style.display;
	node.style.display="";
	var w = dojo.style.getInnerWidth(node);
	var h = dojo.style.getInnerHeight(node);
	node.style.display=oldDisplay;

	if(hasScroll) {
		desiredX -= scroll.x;
		desiredY -= scroll.y;
	}

	var x = -1, y = -1;
	//dojo.debug((desiredX+padding[0]) + w, "<=", view.w, "&&", (desiredY+padding[1]) + h, "<=", view.h);
	if((desiredX+padding[0]) + w <= view.w && (desiredY+padding[1]) + h <= view.h) { // TL
		x = (desiredX+padding[0]);
		y = (desiredY+padding[1]);
		//dojo.debug("TL", x, y);
	}

	//dojo.debug((desiredX-padding[0]
IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 3329
E.
5u.@.@..............^..H../6....g.*.....
...V...V), "<=", view.w, "&&", (desiredY+padding[1]) + h, "<=", view.h);
	if((x < 0 || y < 0) && (desiredX-padding[0]) <= view.w && (desiredY+padding[1]) + h <= view.h) { // TR
		x = (desiredX-padding[0]) - w;
		y = (desiredY+padding[1]);
		//dojo.debug("TR", x, y);
	}

	//dojo.debug((desiredX+padding[0]) + w, "<=", view.w, "&&", (desiredY-padding[1]), "<=", view.h);
	if((x < 0 || y < 0) && (desiredX+padding[0]) + w <= view.w && (desiredY-padding[1]) <= view.h) { // BL
		x = (desiredX+padding[0]);
		y = (desiredY-padding[1]) - h;
		//dojo.debug("BL", x, y);
	}

	//dojo.debug((desiredX-padding[0]), "<=", view.w, "&&", (desiredY-padding[1]), "<=", view.h);
	if((x < 0 || y < 0) && (desiredX-padding[0]) <= view.w && (desiredY-padding[1]) <= view.h) { // BR
		x = (desiredX-padding[0]) - w;
		y = (desiredY-padding[1]) - h;
		//dojo.debug("BR", x, y);
	}

	if(x < 0 || y < 0 || (x + w > view.w) || (y + h > view.h)) {
		return dojo.html.placeOnScreen(node, desiredX, desiredY, padding, hasScroll);
	}

	x += scroll.x;
	y += scroll.y;

	node.style.left = x + "px";
	node.style.top = y + "px";

	var ret = [x, y];
	ret.x = x;
	ret.y = y;
	return ret;
}

/**
 * For IE z-index schenanigans
 * Two possible uses:
 *   1. new dojo.html.BackgroundIframe(node)
 *        Makes a background iframe as a child of node, that fills area (and position) of node
 *
 *   2. new dojo.html.BackgroundIframe()
 *        Attaches frame to document.body.  User must call size() to set size.
 */
dojo.html.BackgroundIframe = function(node) {
	if(dojo.render.html.ie) {
		var html=
				 "<iframe "
				+"style='position: absolute; left: 0px; top: 0px; width: 100%; height: 100%;"
				+        "z-index: -1; filter:Alpha(Opacity=\"0\");' "
				+">";
		this.iframe = document.createElement(html);
		if(node){
			node.appendChild(this.iframe);
			this.domNode=node;
		}else{
			document.body.appendChild(this.iframe);
			this.iframe.style.display="none";
		}
	}
}
dojo.lang.extend(dojo.html.BackgroundIframe, {
	iframe: null,

	// TODO: this function shouldn't be necessary but setting width=height=100% doesn't work!
	onResized: function(){
		if(this.iframe && this.domNode){
			var w = dojo.style.getOuterWidth(this.domNode);
			var h = dojo.style.getOuterHeight(this.domNode);
			if (w  == 0 || h == 0 ){
				dojo.lang.setTimeout(this, this.onResized, 50);
				return;
			}
			var s = this.iframe.style;
			s.width = w + "px";
			s.height = h + "px";
		}
	},

	// Call this function if the iframe is connected to document.body rather
	// than the node being shadowed (TODO: erase)
	size: function(node) {
		if(!this.iframe) { return; }

		coords = dojo.style.toCoordinateArray(node, true);

		var s = this.iframe.style;
		s.width = coords.w + "px";
		s.height = coords.h + "px";
		s.left = coords.x + "px";
		s.top = coords.y + "px";
	},

	setZIndex: function(node /* or number */) {
		if(!this.iframe) { return; }

		if(dojo.dom.isNode(node)) {
			this.iframe.style.zIndex = dojo.html.getStyle(node, "z-index") - 1;
		} else if(!isNaN(node)) {
			this.iframe.style.zIndex = node;
		}
	},

	show: function() {
		if(!this.iframe) { return; }
		this.iframe.style.display = "block";
	},

	hide: function() {
		if(!this.ie) { return; }
		var s = this.iframe.style;
		s.display = "none";
	},

	remove: function() {
		dojo.dom.removeNode(this.iframe);
	}
});

IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 489
E.....@.@.s7.........`.....7.8.............
...b....GET /scooby/js/lib/dojo-event_and_io/src/lfx/toggle.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 1731
E.....@.@.Ag...........`.8..... ...e.......
...w...bHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"1519-1147389474000"
Last-Modified: Thu, 11 May 2006 23:17:54 GMT
Content-Type: text/javascript
Content-Length: 1519
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.lfx.toggle");
dojo.require("dojo.lfx.*");

dojo.lfx.toggle.plain = {
	show: function(node, duration, easing, callback){
		dojo.style.show(node);
		if(dojo.lang.isFunction(callback)){ callback(); }
	},
	
	hide: function(node, duration, easing, callback){
		dojo.style.hide(node);
		if(dojo.lang.isFunction(callback)){ callback(); }
	}
}

dojo.lfx.toggle.fade = {
	show: function(node, duration, easing, callback){
		dojo.lfx.fadeShow(node, duration, easing, callback).play();
	},

	hide: function(node, duration, easing, callback){
		dojo.lfx.fadeHide(node, duration, easing, callback).play();
	}
}

dojo.lfx.toggle.wipe = {
	show: function(node, duration, easing, callback){
		dojo.lfx.wipeIn(node, duration, easing, callback).play();
	},

	hide: function(node, duration, easing, callback){
		dojo.lfx.wipeOut(node, duration, easing, callback).play();
	}
}

dojo.lfx.toggle.explode = {
	show: function(node, duration, easing, callback, explodeSrc){
		dojo.lfx.explode(explodeSrc||[0,0,0,0], node, duration, easing, callback).play();
	},

	hide: function(node, duration, easing, callback, explodeSrc){
		dojo.lfx.implode(node, explodeSrc||[0,0,0,0], duration, easing, callback).play();
	}
}

IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 494
E..".:@.@............_....eA.(
f...........
........GET /scooby/js/lib/dojo-event_and_io/src/lfx/__package__.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 591
E...N"@.@..P..........._.(
f..g/...~.x.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"381-1147389474000"
Last-Modified: Thu, 11 May 2006 23:17:54 GMT
Content-Type: text/javascript
Content-Length: 381
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.kwCompoundRequire({
	browser: ["dojo.lfx.html"],
	dashboard: ["dojo.lfx.html"]
});
dojo.provide("dojo.lfx.*");
IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 487
E...$.@.@..A.........[...~.|..k............
.......(GET /scooby/js/lib/dojo-event_and_io/src/lfx/html.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 9000
E.#\6k@.@..............[..k..~!c....!Q.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"12709-1147389474000"
Last-Modified: Thu, 11 May 2006 23:17:54 GMT
Content-Type: text/javascript
Content-Length: 12709
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.lfx.html");
dojo.require("dojo.lfx.Animation");

dojo.require("dojo.html");
dojo.require("dojo.event");
dojo.require("dojo.lang.func");

dojo.lfx.html._byId = function(nodes){
	if(dojo.lang.isArrayLike(nodes)){
		if(!nodes.alreadyChecked){
			var n = [];
			dojo.lang.forEach(nodes, function(node){
				n.push(dojo.byId(node));
			});
			n.alreadyChecked = true;
			return n;
		}else{
			return nodes;
		}
	}else{
		return [dojo.byId(nodes)];
	}
}

dojo.lfx.html.propertyAnimation = function(	/*DOMNode*/ nodes, 
											/*Array*/ propertyMap, 
											/*int*/ duration,
											/*function*/ easing){
	nodes = dojo.lfx.html._byId(nodes);

	if(nodes.length==1){
		// FIXME: we're only supporting start-value filling when one node is
		// passed

		dojo.lang.forEach(propertyMap, function(prop){
			if(typeof prop["start"] == "undefined"){
				prop.start = parseInt(dojo.style.getComputedStyle(nodes[0], prop.property));
				if(isNaN(prop.start) && (prop.property == "opacity")){
					prop.start = 1;
				}
			}
		});
	}

	var coordsAsInts = function(coords){
		var cints = new Array(coords.length);
		for(var i = 0; i < coords.length; i++){
			cints[i] = Math.round(coords[i]);
		}
		return cints;
	}
	var setStyle = function(n, style){
		n = dojo.byId(n);
		if(!n || !n.style){ return; }
		for(s in style){
			if(s == "opacity"){
				dojo.style.setOpacity(n, style[s]);
			}else{
				n.style[dojo.style.toCamelCase(s)] = style[s];
			}
		}
	}
	var propLine = function(properties){
		this._properties = properties;
		this.diffs = new Array(properties.length);
		dojo.lang.forEach(properties, function(prop, i){
			// calculate the end - start to optimize a bit
			if(dojo.lang.isArray(prop.start)){
				// don't loop through the arrays
				this.diffs[i] = null;
			}else{
				this.diffs[i] = prop.end - prop.start;
			}
		}, this);
		this.getValue = function(n){
			var ret = {};
			dojo.lang.forEach(this._properties, function(prop, i){
				var value = null;
				if(dojo.lang.isArray(prop.start)){
					value = (prop.units||"rgb") + "(";
					for(var j = 0 ; j < prop.start.length ; j++){
						value += Math.round(((prop.end[j] - prop.start[j]) * n) + prop.start[j]) + (j < prop.start.length - 1 ? "," : "");
					}
					value += ")";
				}else{
					value = ((this.diffs[i]) * n) + prop.start + (prop.property != "opacity" ? prop.units||"px" : "");
				}
				ret[prop.property] = value;
			}, this);
			return ret;
		}
	}
	
	var anim = new dojo.lfx.Animation(duration, new propLine(propertyMap), easing);
	
	dojo.event.connect(anim, "onAnimate", function(propValues){
		dojo.lang.forEach(nodes, function(node){
			setStyle(node, propValues); 
		});
	});
	
	return anim;
}

dojo.lfx.html._makeFadeable = function(nodes){
	var makeFade = function(node){
		if(dojo.render.html.ie){
			// only set the zoom if the "tickle" value would be the same as the
			// default
			if( (node.style.zoom.length == 0) &&
				(dojo.style.getStyle(node, "zoom") == "normal") ){
				// make sure the node "hasLayout"
				// NOTE: this has been tested with larger and smaller user-set text
				// sizes and works fine
				node.style.zoom = "1";
				// node.style.zoom = "normal";
			}
			// don't set the width to auto if it didn't already cascade that way.
			// We don't want to f anyones designs
			if(	(node.style.width.length == 0) &&
				(dojo.style.getStyle(node, "width") == "auto") ){
				node.style.width = "auto";
			}
		}
	}
	if(dojo.lang.isArrayLike(nodes)){
		dojo.lang.forEach(nodes, makeFade);
	}else{
		makeFade(nodes);
	}
}

dojo.lfx.html.fadeIn = function(nodes, duration, easing, callback){
	nodes = dojo.lfx.html._byId(nodes);
	dojo.lfx.html._makeFadeable(nodes);
	var anim = dojo.lfx.propertyAnimation(nodes, [
		{	property: "opacity",
			start: dojo.style.getOpacity(nodes[0]),
			end: 1 } ], duration, easing);
	if(callback){
		dojo.event.connect(anim, "onEnd", function(){
			callback(nodes, anim);
		});
	}

	return anim;
}

dojo.lfx.html.fadeOut = function(nodes, duration, easing, callback){
	nodes = dojo.lfx.html._byId(nodes);
	dojo.lfx.html._makeFadeable(nodes);
	var anim = dojo.lfx.propertyAnimation(nodes, [
		{	property: "opacity",
			start: dojo.style.getOpacity(nodes[0]),
			end: 0 } ], duration, easing);
	if(callback){
		dojo.event.connect(anim, "onEnd", function(){
			callback(nodes, anim);
		});
	}

	return anim;
}

dojo.lfx.html.fadeShow = function(nodes, duration, easing, callback){
	var anim = dojo.lfx.html.fadeIn(nodes, duration, easing, callback);
	dojo.event.connect(anim, "beforeBegin", function(){
		if(dojo.lang.isArrayLike(nodes)){
			dojo.lang.forEach(nodes, dojo.style.show);
		}else{
			dojo.style.show(nodes);
		}
	});
	
	return anim;
}

dojo.lfx.html.fadeHide = function(nodes, duration, easing, callback){
	var anim = dojo.lfx.html.fadeOut(nodes, duration, easing, function(){
		if(dojo.lang.isArrayLike(nodes)){
			dojo.lang.forEach(nodes, dojo.style.hide);
		}else{
			dojo.style.hide(nodes);
		}
		if(callback){ callback(nodes, anim); }
	});
	
	return anim;
}

dojo.lfx.html.wipeIn = function(nodes, duration, easing, callback){
	nodes = dojo.lfx.html._byId(nodes);
	var anims = [];

	var init = function(node, overflow){
		if(overflow == "visible") {
			node.style.overflow = "hidden";
		}
		dojo.style.show(node);
		node.style.height = 0;
	}

	dojo.lang.forEach(nodes, function(node){
		var overflow = dojo.style.getStyle(node, "overflow");
		var initialize = function(){
			init(node, overflow);
		}
		initialize();
		
		var anim = dojo.lfx.propertyAnimation(node,
			[{	property: "height",
				start: 0,
				end: node.scrollHeight }], duration, easing);
		
		dojo.event.connect(anim, "beforeBegin", initialize);
		dojo.event.connect(anim, "onEnd", function(){
			node.style.overflow = overflow;
			node.style.height = "auto";
			if(callback){ callback(node, anim); }
		});
		anims.push(anim);
	});
	
	if(nodes.length > 1){ return dojo.lfx.combine(anims); }
	else{ return anims[0]; }
}

dojo.lfx.html.wipeOut = function(nodes, duration, easing, callback){
	nodes = dojo.lfx.html._byId(nodes);
	var anims = [];
	
	var init = function(node, overflow){
		dojo.style.show(node);
		if(overflow == "visible") {
			node.style.overflow = "hidden";
		}
	}
	dojo.lang.forEach(nodes, function(node){
		var overflow = dojo.style.getStyle(node, "overflow");
		var initialize = function(){
			init(node, overflow);
		}
		initialize();

		var anim = dojo.lfx.propertyAnimation(node,
			[{	property: "height",
				start: node.offsetHeight,
				end: 0 } ], duration, easing);
		
		dojo.event.connect(anim, "beforeBegin", initialize);
		dojo.event.connect(anim, "onEnd", function(){
			dojo.style.hide(node);
			node.style.overflow = overflow;
			if(callback){ callback(node, anim); }
		});
		anims.push(anim);
	});

	if(nodes.length > 1){ return dojo.lfx.combine(anims); }
	else { return anims[0]; }
}

dojo.lfx.html.slideTo = function(nodes, coords, duration, easing, callback){
	nodes = dojo.lfx.html._byId(nodes);
	var anims = [];

	dojo.lang.forEach(nodes, function(node){
		var top = null;
		var left = null;
		var pos = null;
		
		var init = (function(){
			var innerNode = node;
			return function(){
				top = node.offsetTop;
				left = node.offsetLeft;
				pos = dojo.style.getComputedStyle(node, 'position');

				if (pos == 'relative' || pos == 'static') {
					top = parseInt(dojo.style.getComputedStyle(node, 'top')) || 0;
					left = parseInt(dojo.style.getComputedStyle(node, 'left')) || 0;
				}
			}
		})();
		init();
		
		var anim = dojo.lfx.propertyAnimation(node,
			[{	property: "top",
				start: top,
				end: coords[0] },
			{	property: "left",
				start: left,
				end: coords[1] }], duration, easing);
		
		dojo.event.connect(anim, "beforeBegin", init);
		if(callback){
			dojo.event.connect(anim, "onEnd", function(){
				callback(node, anim);
			});
		}

		anims.push(anim);
	});
	
	if(nodes.length > 1){ return dojo.lfx.combine(anims); }
	else{ return anims[0]; }
}

dojo.lfx.html.explode = function(start, endNode, duration, easing, callback){
	var startCoords = dojo.style.toCoordinateArray(start);
	var outline = document.createElement("div");
	with(outline.style){
		position = "absolute";
		border = "1px solid bla
IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 3923
E...6l@.@..............[...B.~!c....
|.....
........ck";
		display = "none";
	}
	document.body.appendChild(outline);

	endNode = dojo.byId(endNode);
	with(endNode.style){
		visibility = "hidden";
		display = "block";
	}
	var endCoords = dojo.style.toCoordinateArray(endNode);
	with(endNode.style){
		display = "none";
		visibility = "visible";
	}

	var anim = new dojo.lfx.Animation({
		beforeBegin: function(){
			dojo.style.show(outline);
		},
		onAnimate: function(value){
			with(outline.style){
				left = value[0] + "px";
				top = value[1] + "px";
				width = value[2] + "px";
				height = value[3] + "px";
			}
		},
		onEnd: function(){
			dojo.style.show(endNode);
			outline.parentNode.removeChild(outline);
		}
	}, duration, new dojo.lfx.Line(startCoords, endCoords), easing);
	if(callback){
		dojo.event.connect(anim, "onEnd", function(){
			callback(endNode, anim);
		});
	}
	return anim;
}

dojo.lfx.html.implode = function(startNode, end, duration, easing, callback){
	var startCoords = dojo.style.toCoordinateArray(startNode);
	var endCoords = dojo.style.toCoordinateArray(end);

	startNode = dojo.byId(startNode);
	var outline = document.createElement("div");
	with(outline.style){
		position = "absolute";
		border = "1px solid black";
		display = "none";
	}
	document.body.appendChild(outline);

	var anim = new dojo.lfx.Animation({
		beforeBegin: function(){
			dojo.style.hide(startNode);
			dojo.style.show(outline);
		},
		onAnimate: function(value){
			with(outline.style){
				left = value[0] + "px";
				top = value[1] + "px";
				width = value[2] + "px";
				height = value[3] + "px";
			}
		},
		onEnd: function(){
			outline.parentNode.removeChild(outline);
		}
	}, duration, new dojo.lfx.Line(startCoords, endCoords), easing);
	if(callback){
		dojo.event.connect(anim, "onEnd", function(){
			callback(startNode, anim);
		});
	}
	return anim;
}

dojo.lfx.html.highlight = function(nodes, startColor, duration, easing, callback){
	nodes = dojo.lfx.html._byId(nodes);
	var anims = [];

	dojo.lang.forEach(nodes, function(node){
		var color = dojo.style.getBackgroundColor(node);
		var bg = dojo.style.getStyle(node, "background-color").toLowerCase();
		var wasTransparent = (bg == "transparent" || bg == "rgba(0, 0, 0, 0)");
		while(color.length > 3) { color.pop(); }

		var rgb = new dojo.graphics.color.Color(startColor).toRgb();
		var endRgb = new dojo.graphics.color.Color(color).toRgb();

		var anim = dojo.lfx.propertyAnimation(node, [{
			property: "background-color",
			start: rgb,
			end: endRgb
		}], duration, easing);

		dojo.event.connect(anim, "beforeBegin", function(){
			node.style.backgroundColor = "rgb(" + rgb.join(",") + ")";
		});

		dojo.event.connect(anim, "onEnd", function(){
			if(wasTransparent){
				node.style.backgroundColor = "transparent";
			}
			if(callback){
				callback(node, anim);
			}
		});

		anims.push(anim);
	});

	if(nodes.length > 1){ return dojo.lfx.combine(anims); }
	else{ return anims[0]; }
}

dojo.lfx.html.unhighlight = function(nodes, endColor, duration, easing, callback){
	nodes = dojo.lfx.html._byId(nodes);
	var anims = [];

	dojo.lang.forEach(nodes, function(node){
		var color = new dojo.graphics.color.Color(dojo.style.getBackgroundColor(node)).toRgb();
		var rgb = new dojo.graphics.color.Color(endColor).toRgb();
		
		var anim = dojo.lfx.propertyAnimation(node, [{
			property: "background-color",
			start: color,
			end: rgb
		}], duration, easing);

		dojo.event.connect(anim, "beforeBegin", function(){
			node.style.backgroundColor = "rgb(" + color.join(",") + ")";
		});
		if(callback){
			dojo.event.connect(anim, "onEnd", function(){
				callback(node, anim);
			});
		}

		anims.push(anim);
	});

	if(nodes.length > 1){ return dojo.lfx.combine(anims); }
	else{ return anims[0]; }
}

dojo.lang.mixin(dojo.lfx, dojo.lfx.html);

IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 492
E.. .v@.@.Q_.........^.../6...U............
.......VGET /scooby/js/lib/dojo-event_and_io/src/lfx/Animation.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 9000
E.#\u.@.@..............^..U../8....x!Q.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"11252-1147389474000"
Last-Modified: Thu, 11 May 2006 23:17:54 GMT
Content-Type: text/javascript
Content-Length: 11252
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
	Copyright (c) 2004-2006, The Dojo Foundation
	All Rights Reserved.

	Licensed under the Academic Free License version 2.1 or above OR the
	modified BSD license. For more information on Dojo licensing, see:

		http://dojotoolkit.org/community/licensing.shtml
*/

dojo.provide("dojo.lfx.Animation");
dojo.provide("dojo.lfx.Line");

dojo.require("dojo.lang.func");

/*
	Animation package based on Dan Pupius' work: http://pupius.co.uk/js/Toolkit.Drawing.js
*/
dojo.lfx.Line = function(start, end){
	this.start = start;
	this.end = end;
	if(dojo.lang.isArray(start)){
		var diff = [];
		dojo.lang.forEach(this.start, function(s,i){
			diff[i] = this.end[i] - s;
		}, this);
		
		this.getValue = function(/*float*/ n){
			var res = [];
			dojo.lang.forEach(this.start, function(s, i){
				res[i] = (diff[i] * n) + s;
			}, this);
			return res;
		}
	}else{
		var diff = end - start;
			
		this.getValue = function(/*float*/ n){
			//	summary: returns the point on the line
			//	n: a floating point number greater than 0 and less than 1
			return (diff * n) + this.start;
		}
	}
}

dojo.lfx.easeIn = function(n){
	//	summary: returns the point on an easing curve
	//	n: a floating point number greater than 0 and less than 1
	return Math.pow(n, 3);
}

dojo.lfx.easeOut = function(n){
	//	summary: returns the point on the line
	//	n: a floating point number greater than 0 and less than 1
	return ( 1 - Math.pow(1 - n, 3) );
}

dojo.lfx.easeInOut = function(n){
	//	summary: returns the point on the line
	//	n: a floating point number greater than 0 and less than 1
	return ( (3 * Math.pow(n, 2)) - (2 * Math.pow(n, 3)) );
}

dojo.lfx.IAnimation = function(){}
dojo.lang.extend(dojo.lfx.IAnimation, {
	// public properties
	curve: null,
	duration: 1000,
	easing: null,
	repeatCount: 0,
	rate: 25,
	
	// events
	handler: null,
	beforeBegin: null,
	onBegin: null,
	onAnimate: null,
	onEnd: null,
	onPlay: null,
	onPause: null,
	onStop: null,
	
	// public methods
	play: null,
	pause: null,
	stop: null,
	
	fire: function(evt, args){
		if(this[evt]){
			if(args){
				this[evt].apply(this, args);
			}else{
				this[evt].apply(this);
			}
		}
	},
	
	// private properties
	_active: false,
	_paused: false
});

dojo.lfx.Animation = function(/*Object*/ handlers, /*int*/ duration, /*Array*/ curve, /*function*/ easing, /*int*/ repeatCount, /*int*/ rate){
	//	summary
	//		a generic animation object that fires callbacks into it's handlers
	//		object at various states
	//	handlers
	//		object { 
	//			handler: function(){}, 
	//			onstart: function(){}, 
	//			onstop: function(){}, 
	//			onanimate: function(){}
	//		}
	dojo.lfx.IAnimation.call(this);
	if(dojo.lang.isNumber(handlers)||(!handlers && duration.getValue)){
		// no handlers argument:
		rate = repeatCount;
		repeatCount = easing;
		easing = curve;
		curve = duration;
		duration = handlers;
		handlers = null;
	}else if(handlers.getValue||dojo.lang.isArray(handlers)){
		// no handlers or duration:
		rate = easing;
		repeatCount = curve;
		easing = duration;
		curve = handlers;
		duration = null;
		handlers = null;
	}
	if(dojo.lang.isArray(curve)){
		this.curve = new dojo.lfx.Line(curve[0], curve[1]);
	}else{
		this.curve = curve;
	}
	if(duration != null && duration > 0){ this.duration = duration; }
	if(repeatCount){ this.repeatCount = repeatCount; }
	if(rate){ this.rate = rate; }
	if(handlers){
		this.handler = handlers.handler;
		this.beforeBegin = handlers.beforeBegin;
		this.onBegin = handlers.onBegin;
		this.onEnd = handlers.onEnd;
		this.onPlay = handlers.onPlay;
		this.onPause = handlers.onPause;
		this.onStop = handlers.onStop;
		this.onAnimate = handlers.onAnimate;
	}
	if(easing && dojo.lang.isFunction(easing)){
		this.easing=easing;
	}
}
dojo.inherits(dojo.lfx.Animation, dojo.lfx.IAnimation);
dojo.lang.extend(dojo.lfx.Animation, {
	// "private" properties
	_startTime: null,
	_endTime: null,
	_timer: null,
	_percent: 0,
	_startRepeatCount: 0,

	// public methods
	play: function(delay, gotoStart) {
		if( gotoStart ) {
			clearTimeout(this._timer);
			this._active = false;
			this._paused = false;
			this._percent = 0;
		} else if( this._active && !this._paused ) {
			return;
		}
		
		this.fire("beforeBegin");

		if(delay > 0){
			setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
			return;
		}
		
		this._startTime = new Date().valueOf();
		if( this._paused ) {
			this._startTime -= (this.duration * this._percent / 100);
		}
		this._endTime = this._startTime + this.duration;

		this._active = true;
		this._paused = false;
		
		var step = this._percent / 100;
		var value = this.curve.getValue(step);
		if( this._percent == 0 ) {
			if(!this._startRepeatCount) {
				this._startRepeatCount = this.repeatCount;
			}
			this.fire("handler", ["begin", value]);
			this.fire("onBegin", [value]);
		}

		this.fire("handler", ["play", value]);
		this.fire("onPlay", [value]);

		this._cycle();
	},

	pause: function() {
		clearTimeout(this._timer);
		if( !this._active ) { return; }
		this._paused = true;
		var value = this.curve.getValue(this._percent / 100);
		this.fire("handler", ["pause", value]);
		this.fire("onPause", [value]);
	},

	gotoPercent: function(pct, andPlay) {
		clearTimeout(this._timer);
		this._active = true;
		this._paused = true;
		this._percent = pct;
		if( andPlay ) { this.play(); }
	},

	stop: function(gotoEnd) {
		clearTimeout(this._timer);
		var step = this._percent / 100;
		if( gotoEnd ) {
			step = 1;
		}
		var value = this.curve.getValue(step);
		this.fire("handler", ["stop", value]);
		this.fire("onStop", [value]);
		this._active = false;
		this._paused = false;
	},

	status: function() {
		if( this._active ) {
			return this._paused ? "paused" : "playing";
		} else {
			return "stopped";
		}
	},

	// "private" methods
	_cycle: function() {
		clearTimeout(this._timer);
		if( this._active ) {
			var curr = new Date().valueOf();
			var step = (curr - this._startTime) / (this._endTime - this._startTime);

			if( step >= 1 ) {
				step = 1;
				this._percent = 100;
			} else {
				this._percent = step * 100;
			}
			
			// Perform easing
			if(this.easing && dojo.lang.isFunction(this.easing)) {
				step = this.easing(step);
			}

			var value = this.curve.getValue(step);
			this.fire("handler", ["animate", value]);
			this.fire("onAnimate", [value]);

			if( step < 1 ) {
				this._timer = setTimeout(dojo.lang.hitch(this, "_cycle"), this.rate);
			} else {
				this._active = false;
				this.fire("handler", ["end"]);
				this.fire("onEnd");

				if( this.repeatCount > 0 ) {
					this.repeatCount--;
					this.play(null, true);
				} else if( this.repeatCount == -1 ) {
					this.play(null, true);
				} else {
					if(this._startRepeatCount) {
						this.repeatCount = this._startRepeatCount;
						this._startRepeatCount = 0;
					}
				}
			}
		}
	}
});

dojo.lfx.Combine = function(){
	dojo.lfx.IAnimation.call(this);
	this._anims = [];
	this._animsEnded = 0;
	
	var anims = arguments;
	if(anims.length == 1 && (dojo.lang.isArray(anims[0]) || dojo.lang.isArrayLike(anims[0]))){
		anims = anims[0];
	}
	
	var _this = this;
	dojo.lang.forEach(anims, function(anim){
		_this._anims.push(anim);
		dojo.event.connect(anim, "onEnd", function(){ _this._onAnimsEnded(); });
	});
}
dojo.inherits(dojo.lfx.Combine, dojo.lfx.IAnimation);
dojo.lang.extend(dojo.lfx.Combine, {
	// private members
	_animsEnded: 0,
	
	// public methods
	play: function(delay, gotoStart){
		if( !this._anims.length ){ return; }

		this.fire("beforeBegin");

		if(delay > 0){
			setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
			return;
		}
		
		if(gotoStart || this._anims[0].percent == 0){
			this.fire("onBegin");
		}
		this.fire("onPlay");
		this._animsCall("play", null, gotoStart);
	},
	
	pause: function(){
		this.fire("onPause");
		this._animsCall("pause"); 
	},
	
	stop: function(gotoEnd){
		this.fire("onStop");
		this._animsCall("stop", gotoEnd);
	},
	
	// private methods
	_onAnimsEnded: function(){
		this._animsEnded++;
		if(this._animsEnded >= this._anims.length){
			this.fire("onEnd");
		}
	},
	
	_animsCall: function(funcName){
		var args = [];
		if(arguments.length > 1){
			for(var i = 1 ; i < arguments.length ; i++){
				args.push(arguments[i]);
			}
		}
		var _this = this;
		dojo.lang.forEach(this._anims, function(anim){
			anim[funcName](args);
		}, _this);
	}
});

dojo.lfx.Chain = function() {
	dojo.lfx.IAnimation.c
IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 2466
E.	.u.@.@..Y...........^..x../8....x.......
........all(this);
	this._anims = [];
	this._currAnim = -1;
	
	var anims = arguments;
	if(anims.length == 1 && (dojo.lang.isArray(anims[0]) || dojo.lang.isArrayLike(anims[0]))){
		anims = anims[0];
	}
	
	var _this = this;
	dojo.lang.forEach(anims, function(anim, i, anims_arr){
		_this._anims.push(anim);
		if(i < anims_arr.length - 1){
			dojo.event.connect(anim, "onEnd", function(){ _this._playNext(); });
		}else{
			dojo.event.connect(anim, "onEnd", function(){ _this.fire("onEnd"); });
		}
	}, _this);
}
dojo.inherits(dojo.lfx.Chain, dojo.lfx.IAnimation);
dojo.lang.extend(dojo.lfx.Chain, {
	// private members
	_currAnim: -1,
	
	// public methods
	play: function(delay, gotoStart){
		if( !this._anims.length ) { return; }
		if( gotoStart || !this._anims[this._currAnim] ) {
			this._currAnim = 0;
		}

		this.fire("beforeBegin");
		if(delay > 0){
			setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
			return;
		}
		
		if( this._anims[this._currAnim] ){
			if( this._currAnim == 0 ){
				this.fire("handler", ["begin", this._currAnim]);
				this.fire("onBegin", [this._currAnim]);
			}
			this.fire("onPlay", [this._currAnim]);
			this._anims[this._currAnim].play(null, gotoStart);
		}
	},
	
	pause: function(){
		if( this._anims[this._currAnim] ) {
			this._anims[this._currAnim].pause();
			this.fire("onPause", [this._currAnim]);
		}
	},
	
	playPause: function(){
		if( this._anims.length == 0 ) { return; }
		if( this._currAnim == -1 ) { this._currAnim = 0; }
		var currAnim = this._anims[this._currAnim];
		if( currAnim ) {
			if( !currAnim._active || currAnim._paused ) {
				this.play();
			} else {
				this.pause();
			}
		}
	},
	
	stop: function(){
		if( this._anims[this._currAnim] ){
			this._anims[this._currAnim].stop();
			this.fire("onStop", [this._currAnim]);
		}
	},
	
	// private methods
	_playNext: function(){
		if( this._currAnim == -1 || this._anims.length == 0 ) { return; }
		this._currAnim++;
		if( this._anims[this._currAnim] ){
			this._anims[this._currAnim].play(null, true);
		}
	}
});

dojo.lfx.combine = function(){
	var anims = arguments;
	if(dojo.lang.isArray(arguments[0])){
		anims = arguments[0];
	}
	return new dojo.lfx.Combine(anims);
}

dojo.lfx.chain = function(){
	var anims = arguments;
	if(dojo.lang.isArray(arguments[0])){
		anims = arguments[0];
	}
	return new dojo.lfx.Chain(anims);
}

IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 475
E.....@.@.sC.........`..... .8.N...........
.......wGET /scooby/js/scooby/ui/widget/Debug.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 1562
E..N..@.@.B............`.8.N.......v.C.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"1350-1148951746000"
Last-Modified: Tue, 30 May 2006 01:15:46 GMT
Content-Type: text/javascript
Content-Length: 1350
Date: Sun, 08 Oct 2006 23:34:04 GMT

dojo.require("dojo.widget.*");
dojo.require("dojo.event.*");

dojo.provide("scooby.ui.widget.Debug");

dojo.widget.defineWidget("scooby.ui.widget.Debug", dojo.widget.HtmlWidget, {

    templatePath : dojo.uri.dojoUri("../../scooby/ui/widget/templates/Debug.html"),
    templateCssPath : dojo.uri.dojoUri("../../scooby/ui/widget/templates/Debug.css"),
    time : "",
    debugDiv : null,
    contentContainer : null,

    fillInTemplate: function(){
        this.content.innerHTML = this.preWidgetContent;
        this.preWidgetContent = null;
    },

    clearContent : function(){
        this.content.innerHTML = "";
    },

    onkeypressHandler : function(event){
        var keyCode = event.keyCode ? event.keyCode : event.which;
        var ctrl = event.ctrlKey;
        var shift = event.shiftKey;
        
        //ctrl shift d
        if (keyCode == 68 && shift && ctrl){
            this.toggleHideShow();
        }
    },
         
    toggleHideShow : function(){
        var vis = this.debugDiv.style.visibility;
        this.debugDiv.style.visibility = vis == "hidden" ? "visible" : "hidden";
    }
    
  },

  "html",
  
  function() {
     if (!djConfig.isDebug){
         return;
     }
     dojo.event.connect(window, "onkeypress", this, "onkeypressHandler");
     this.preWidgetContent = dojo.byId("dojoDebug").innerHTML;
  }

);
IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 407
E....<@.@............_....g/.(.............
........GET /scooby/i18n.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 3882
E..^N#@.@..t..........._.(....h.....
S.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=ISO-8859-1
Content-Language: en-NZ
Content-Length: 3708
Date: Sun, 08 Oct 2006 23:34:04 GMT





 
var localText = [];

function getText(str) {
    return localText[str];
}


    localText["App.Button.Save"] = "Save";

    localText["App.August"] = "August";

    localText["Main.LogOut"] = "Log Out";

    localText["App.Sat"] = "Sat";

    localText["App.Nov"] = "Nov";

    localText["App.Tue"] = "Tue";

    localText["App.October"] = "October";

    localText["App.Button.Cancel"] = "Cancel";

    localText["Main.DetailForm.Title"] = "Title";

    localText["App.Friday"] = "Friday";

    localText["App.Jun"] = "Jun";

    localText["Login.Error.NoLoginWithRootAccount"] = "You cannot log in to Scooby with the Cosmo root user account.";

    localText["App.Saturday"] = "Saturday";

    localText["App.Wed"] = "Wed";

    localText["App.Thu"] = "Thu";

    localText["App.Sun"] = "Sun";

    localText["App.July"] = "July";

    localText["App.Jul"] = "Jul";

    localText["App.Oct"] = "Oct";

    localText["App.February"] = "February";

    localText["Main.Error.EventNewSaveFailed"] = "Could not save this new event.";

    localText["App.November"] = "November";

    localText["App.Wednesday"] = "Wednesday";

    localText["Login.Error.AuthFailed"] = "Could not verify the entered username and password. Please try again.";

    localText["Main.Processing"] = "Processing ...";

    localText["Login.Prompt"] = "Enter a username and password, and click the Submit button.";

    localText["Main.Error.LoadEventsFailed"] = "Could not load events.";

    localText["App.January"] = "January";

    localText["App.PM"] = "PM";

    localText["App.Fri"] = "Fri";

    localText["App.Dec"] = "Dec";

    localText["Main.Preferences"] = "Preferences";

    localText["Login.Username"] = "Username:";

    localText["Main.About"] = "About Scooby";

    localText["App.Jan"] = "Jan";

    localText["Login.About"] = "About Scooby Web Calendar ...";

    localText["Main.Error.EventRemoveFailed"] = "Could not remove this event.";

    localText["App.September"] = "September";

    localText["App.Feb"] = "Feb";

    localText["App.May"] = "May";

    localText["App.Thursday"] = "Thursday";

    localText["Main.Error.EventTimezoneMismatch"] = "Calendar event Start and End have mismatched timezones.";

    localText["Main.Welcome"] = "Welcome,";

    localText["Main.DetailForm.At"] = "at";

    localText["App.June"] = "June";

    localText["No"] = "No";

    localText["Main.DetailForm.Ends"] = "Ends";

    localText["Login.Password"] = "Password:";

    localText["App.Welcome"] = "Welcome to Scooby";

    localText["App.Button.OK"] = "OK";

    localText["App.Sep"] = "Sep";

    localText["App.December"] = "December";

    localText["Login.Error.RequiredFields"] = "Both Username and Password are required fields.";

    localText["App.Monday"] = "Monday";

    localText["App.March"] = "March";

    localText["App.Mon"] = "Mon";

    localText["Main.Prompt.EventRemoveConfirm"] = "Remove this event?";

    localText["App.Tuesday"] = "Tuesday";

    localText["Main.DetailForm.Description"] = "Description";

    localText["App.Sunday"] = "Sunday";

    localText["Main.NewEvent"] = "New Event";

    localText["Yes"] = "Yes";

    localText["App.Aug"] = "Aug";

    localText["Main.Error.InitCalCreateFailed"] = "Could not create calendar for new user.";

    localText["App.Button.Submit"] = "Submit";

    localText["App.Mar"] = "Mar";

    localText["Main.DetailForm.Starts"] = "Starts";

    localText["Main.Error.EventEditSaveFailed"] = "Could not save the changes to this event.";

    localText["App.April"] = "April";

    localText["App.Button.Remove"] = "Remove";

    localText["App.Apr"] = "Apr";

    localText["App.Noon"] = "Noon";

    localText["App.AM"] = "AM";

IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 422
E...$.@.@............[...~!c...............
...@....GET /scooby/js/scooby/util/date.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 9000
E.#\6m@.@..,...........[.....~#	....!Q.....
...M...@HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"14051-1147764336000"
Last-Modified: Tue, 16 May 2006 07:25:36 GMT
Content-Type: text/javascript
Content-Length: 14051
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview JavaScript implementation of Date.strftime, Date.add, Date.diff
 * @author Matthew Eernisse mailto:mde@osafoundation.org
 * @license Apache License 2.0
 * @version 0.1
 */

Date.formats = {
    'a': function(dt) { return Date.abbrWeekday[dt.getDay()]; },
    'A': function(dt) { return Date.fullWeekday[dt.getDay()]; },
    'b': function(dt) { return Date.abbrMonth[dt.getMonth()]; },
    'B': function(dt) { return Date.fullMonth[dt.getMonth()]; },
    'c': function(dt) { return dt.strftime('%a %b %d %T %Y'); },
    'C': function(dt) { return calcCentury(dt.getFullYear());; },
    'd': function(dt) { return lPadStr(dt.getDate(), 2, '0'); },
    'D': function(dt) { return dt.strftime('%m/%d/%y') },
    'e': function(dt) { return lPadStr(dt.getDate(), 2, ' '); },
    'h': function(dt) { return dt.strftime('%b'); },
    'H': function(dt) { return lPadStr(dt.getHours(), 2, '0'); },
    'I': function(dt) { return lPadStr(hrMil2Std(dt.getHours()), 2, '0'); },
    'j': function(dt) { return lPadStr(calcDays(dt), 3, '0'); },
    'k': function(dt) { return lPadStr(dt.getHours(), 2, ' '); },
    'l': function(dt) { return lPadStr(hrMil2Std(dt.getHours()), 2, ' '); },
    'm': function(dt) { return lPadStr((dt.getMonth()+1), 2, '0'); },
    'M': function(dt) { return lPadStr(dt.getMinutes(), 2, '0'); },
    'p': function(dt) { return meridian(dt.getHours()); },
    'r': function(dt) { return dt.strftime('%I:%M:%S %p'); },
    'R': function(dt) { return dt.strftime('%H:%M'); },
    'S': function(dt) { return lPadStr(dt.getSeconds(), 2, '0'); },
    'T': function(dt) { return dt.strftime('%H:%M:%S'); },
    'u': function(dt) { return adjSun(dt.getDay()); },
    'w': function(dt) { return dt.getDay(); },
    'x': function(dt) { return dt.strftime('%D'); },
    'X': function(dt) { return dt.strftime('%T'); },
    'y': function(dt) { return yearTwoDigits(dt.getFullYear()); },
    'Y': function(dt) { return lPadStr(dt.getFullYear(), 4, '0'); },
    '%': function(dt) { return '%'; }
}
// Create regex from all the above formats
Date.supported = new RegExp('%[' + getStftimeFormats() + ']{1}', 'g');

Date.fullWeekday = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 
    'Friday', 'Saturday'];
Date.abbrWeekday = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
Date.fullMonth = ['January', 'February', 'March', 'April', 'May', 'June', 
    'July', 'August', 'September', 'October', 'November', 'December'];
Date.abbrMonth = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 
    'Sep', 'Oct', 'Nov', 'Dec'];

// Allow localized AM/PM string
Date.meridian = {
    'AM': 'AM',
    'PM': 'PM'
}

/**
 * Format a JavaScript Unix timestamp as a string 
 * @param format Indicates format or formats to return
 * @param dtparam Unix timestamp to format (optional) -- also accepts JS Date obj. 
 * If param is not passed, the current datetime is used.
 * @return A string formatted according to the givent format, using the given
 * timestamp, or the current datetime if no timestamp is given. 
 * This naive implementation uses only English/US-formats for the locale.
 */

/*
 * Instance method that you can call on Date objects
 * var dt = new Date(); dt = dt.strftime('%Y-%m-%d %T'); return dt;
 * ========================
 */
Date.prototype.strftime = function(format) {
    return Date.strftime(format, this);
}

/*
 * Static method that takes a JS Date obj or UTC timestamp
 * ========================
 */
Date.strftime = function(format, dt) {
    var dtParam = null;
    var patArr = [];
    var dtArr = [];
    var str = format;
    
    // If no dt, use current date
    dtParam = dt ? dt : new Date();
    // Allow either Date obj or UTC stamp
    dtParam = typeof dt == 'number' ? new Date(dt) : dt;
    
    // Grab all instances of expected formats into array
    while (patArr = Date.supported.exec(format)) {
        dtArr.push(patArr[0]);
    }
    
    // Process any hits
    for (var i = 0; i < dtArr.length; i++) {
        key = dtArr[i].replace(/%/, '');
        str = str.replace('%'+key, Date.formats[key](dtParam));
    }
    return str;
}

/**
 * Avoid the JavaScript 'Year 1000' annoyance by simply truncating getFullYear
 * to get the shortened year number.
 * @param yr Integer year number
 * @return String for last two digits of year (e.g., '07', '02')
 */
function yearTwoDigits(yr) {
    // Add a millenium to take care of years before the year 1000, 
    // since we're only taking the last two digits
    var millenYear = yr + 1000;
    var str = millenYear.toString();
    str = str.substr(2); // Get the last two digits
    return str
}

/**
 * Convert a 24-hour formatted hour to 12-hour format
 * @param hour Integer hour number
 * @return String for hour in 12-hour format -- may be string length of one
 */
function hrMil2Std(hour) {
    var h = typeof hour == 'number' ? hour : parseInt(hour);
    var str = h > 12 ? h - 12 : h;
    str = str == 0 ? 12 : str;
    return str;
}

/**
 * Convert a 12-hour formatted hour with meridian flag to 24-hour format
 * @param hour Integer hour number
 * @param pm Boolean flag, if PM hour then set to true
 * @return String for hour in 24-hour format
 */
function hrStd2Mil(hour, pm) {
    var h = typeof hour == 'number' ? hour : parseInt(hour);
    var str = '';
    // PM
    if (pm) {
        str = h < 12 ? (h+12) : h;
    }
    // AM
    else {
        str = h == 12 ? 0 : h;
    }
    return str;
}

/**
 * Make sure a string is specified length
 * @param instr Any string or var with toString method
 * @return String with specified length with spacer char added
 */
function lPadStr(instr, len, spacer) {
    var str = instr.toString();
    var sp = spacer ? spacer : ' '; // spacer char optional, default to space
    while (str.length < len) {
        str = sp+str;
    }
    return str;
}

/**
 * Return 'AM' or 'PM' based on hour in 24-hour format
 * @param h Integer for hour in 24-hour format
 * @return String of either 'AM' or 'PM' based on hour number
 */
function meridian(h) {
    return h > 11 ? Date.meridian['PM'] : Date.meridian['AM'];
}

/**
 * Calculate the century to which a particular year belongs
 * @param y Integer year number
 * @return Integer century number
 */
function calcCentury(y) {
    var ret = parseInt(y/100);
    ret = ret.toString();
    return lPadStr(ret);
}

/**
 * Calculate the day number in the year a particular date is on
 * @param dt JavaScript date object
 * @return Integer day number in the year for the given date
 */
function calcDays(dt) {
    var first = new Date(dt.getFullYear(), 0, 1);
    var diff = 0;
    var ret = 0;
    first = first.getTime();
    diff = (dt.getTime() - first);
    ret = parseInt(((((diff/1000)/60)/60)/24))+1;
    return ret;
}

/**
 * Adjust from 0-6 base week to 1-7 base week
 * @param d integer for day of week
 * @return Integer day number for 1-7 base week
 */
function adjSun(d) {
    return d == 0 ? 7 : d;
}

/**
 * Get a string of all the supported strtime formats in Date.strftime
 * @return String of all the supported formats appended together
 */
function getStftimeFormats() {
    var str = '';
    for(var i in Date.formats)  {
        str += i;
    }
    return str;
}

/*
 * Instance method that you can call on Date objects
 * var dt = new Date(); 
 * dt.add('d', 5); 
 * return dt;
 * ========================
 */
Date.prototype.add = function(interv, incr) {
    var ndt = Date.add(interv, incr, this);
    this.setTime(ndt.getTime());
}

/*
 * Static method that requires a Date object passed in
 * ========================
 */
Date.add = function(interv, incr, dt) {
    /*
    yyyy - Year
    q - Quarter
    m - Month
    y - Day of year
    d - Day
    w - Weekday
    ww - Week of year
    h - Hour
    n - Minute
    s - Second
    */
    
    var dtParam = (typeof dt == 'number') ? new Date(dt) : dt; // Convert Unix timestamp to Date obj if need be
    var intervFlag = interv.toLowerCase();
    var yea = dtParam.getFullYear();
    var mon = dtParam.getMonth();
    var dat = dtParam.getDate();
    var hou = dtParam.getHours();
    var min = dtParam.getMinutes();
    var sec = dtParam.getSeconds();
    var retD
IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 5265
E...6n@.@..............[.....~#	...........
...M...@ate = new Date(dt);
    
    switch(intervFlag) {
        case 'yyyy':
            retDate.setFullYear(yea + incr);
            break;
        case 'q':
            //var currQ = parseInt(mon/3)+1;
            //var incrYear = (incr > 0) ? parseInt(incr/4)+1 : parseInt(incr/4);
            
            // Naive implementation of quarters as just adding three months
            incr*=3;
            
            break;
        case 'm':               
            if (incr < 0){
                while (incr < 0){
                    incr++;
                    mon--;
                    if(mon < 0){
                        mon=11;
                        yea--;
                    }
                }                   
            }
            else {
                while(incr > 0){
                    incr--;
                    mon++;
                    if (mon > 11){
                        mon=0;
                        yea++;
                    }
                }
            } 
            retDate.setMonth(mon);
            retDate.setFullYear(yea);
            if (retDate.getMonth() != mon){
                // Overshot month due to date 
                // Go to last day of previous month 
                retDate.setDate(0) 
            }
            break;
        case 'd':
        case 'y':
            retDate.setDate(dat + incr);
            break;
        case 'h':
            retDate = dtParam.getTime();
            retDate = retDate + (incr*1000*60*60);
            retDate = new Date(retDate);
            break;
        case 'n':
            retDate = dtParam.getTime();
            retDate = retDate + (incr*1000*60);
            retDate = new Date(retDate);
            break;
        case 's':
            retDate = dtParam.getTime();
            retDate = retDate + (incr*1000);
            retDate = new Date(retDate);
            break;
        case 'w':
            var weekSpan = parseInt(incr/5); // Number of weeks spanned
            // If less than one week, check for intervening weekend
            if (weekSpan == 0) {
                if ((dtParam.getDay() + incr) > 4) {
                    retDate.setDate(dat + incr + 2);
                }
                else {
                    retDate.setDate(dat + incr);
                }
            }
            // Otherwise add along with intervening weekend days
            else {
                retDate.setDate(dat + incr + (2*weekSpan));
            }
            break;
        case 'ww':
            retDate.setDate(dat + (incr*7));
            break;
        default:
            // Do nothing
            break;
        
    } 
    return retDate;
}

/*
 * Instance method that you can call on Date objects
 * var dt = new Date('10/31/2112'); 
 * var diff = dt.diff('d', new Date('12/31/2112')); 
 * return diff;
 * ========================
 */
Date.prototype.diff = function(interv, dt2) {
    return Date.diff(interv, this, dt2);
}

/*
 * Static method that requires a Date object passed in
 * ========================
 */
Date.diff = function(interv, dt1, dt2) {
    /*
    yyyy - Year
    q - Quarter
    m - Month
    y - Day of year
    d - Day
    w - Weekday
    ww - Week of year
    h - Hour
    n - Minute
    s - Second
    */
    
    // Convert Unix timestamp to Date obj if need be
    var dtParam1 = (typeof dt1 == 'number') ? new Date(dt1) : dt1;
    // Convert Unix timestamp to Date obj if need be
    var dtParam2 = (typeof dt2 == 'number') ? new Date(dt2) : dt2; 
    var intervFlag = interv.toLowerCase();
    // Year, month
    var yeaDiff = dtParam2.getFullYear() - dtParam1.getFullYear();
    var monDiff = (dtParam2.getMonth() - dtParam1.getMonth()) + (yeaDiff * 12);
    // All others -- build incrementally
    var msDiff = dtParam2.getTime() - dtParam1.getTime(); // Millisecs
    var secDiff = msDiff/1000;
    var minDiff = secDiff/60;
    var houDiff = minDiff/60;
    var dayDiff = houDiff/24;
    // Counts number of seven-day intervals, not calendar weeks
    var weeDiff = dayDiff/7; 
    var ret = 0;
    
    switch(intervFlag) {
        case 'yyyy':
            ret = yeaDiff;
            break;
        case 'q':
            var mA = dtParam1.getMonth();
            var mB = dtParam2.getMonth();
            // Figure out which quarter the months are in
            var qA = parseInt(mA / 3) + 1;
            var qB = parseInt(mB / 3) + 1;
            // Add quarters for any year difference between the dates
            qB += (yeaDiff * 4);
            ret = qB - qA;
            break;
        case 'm':
            ret = monDiff;
            break;            
        case 'd':
        case 'y':
            // Daylight savings time switchover
            // Value will be over or under by an hour
            ret = dayDiff;
            break;
        case 'h':
            ret = houDiff;
            break;
        case 'n':
            ret = minDiff;
            break;
        case 's':
            ret = secDiff;
            break;
        case 'w':
            ret = dayDiff;
            break;
        case 'ww':
            ret = weeDiff;
            break;
        default:
            // Do nothing
            break;
        
    } 
    ret = Math.round(ret);
    return ret; // Return an integer
}

IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 422
E....y@.@.Q..........^.../8................
...R....GET /scooby/js/scooby/util/hash.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 6852
E...u.@.@..6...........^...../:z...........
...T...RHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"6640-1147764336000"
Last-Modified: Tue, 16 May 2006 07:25:36 GMT
Content-Type: text/javascript
Content-Length: 6640
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

function Hash() {
    this.length = 0;
    this.items = []; // Hash keys and their values
    this.order = []; // Array of the order of hash keys
    this.cursorPos = 0; // Current cursor position in the hash
    for (var i = 0; i < arguments.length; i += 2) {
        if (typeof(arguments[i+1]) != 'undefined') {
            this.items[arguments[i]] = arguments[i+1];
            this.order[this.length] = arguments[i];
            this.length++;
        }
    }

    this.getItem = function(key) {
        return this.items[key];
    };
    this.setItem = function(key, val) {
        if (typeof val != 'undefined') {
            if (typeof this.items[key] == 'undefined') {
                this.order[this.length] = key;
                this.length++;
            }
            this.items[key] = val;
            return this.items[key];
        }
    };
    this.addItem = function(key, val) {
        this.setItem(key, val);
    };
    this.removeItem = function(key) {
        if (typeof this.items[key] != 'undefined') {
            var pos = null;
            delete this.items[key]; // Remove the value
            // Find the key in the order list
            for (var i = 0; i < this.order.length; i++) {
                if (this.order[i] == key) {
                    pos = i;
                }
            }
            this.order.splice(pos, 1); // Remove the key
            this.length--; // Decrement the length
        }
    };
    this.hasKey = function(key) {
        return typeof this.items[key] != 'undefined';
    };
    this.hasValue = function(val) {
        for (var i = 0; i < this.order.length; i++) {
            if (this.items[this.order[i]] == val) {
                return true;
            }
        }
        return false;
    };
    this.allKeys = function(str) {
        return this.order.join(str);
    };
    this.replaceKey = function(oldKey, newKey) {
        // If item for newKey exists, nuke it
        if (this.hasKey(newKey)) {
            this.removeItem(newKey);
        }
        this.items[newKey] = this.items[oldKey];
        delete this.items[oldKey];
        for (var i = 0; i < this.order.length; i++) {
            if (this.order[i] == oldKey) {
                this.order[i] = newKey;
            }
        }
    };
    this.getAtPos = function(pos) {
        var lookup = this.items[this.order[pos]];
        return typeof lookup != 'undefined' ? lookup : false;
    };
    this.removeAtPos = function(pos) {
        var ret = this.items[this.order[pos]];
        if (typeof ret != 'undefined') {
            delete this.items[this.order[pos]]
            this.order.splice(pos, 1);
            this.length--;
            return true;
        }
        else {
            return false;
        }
    };
    this.getFirst = function() {
        return this.items[this.order[0]];
    };
    this.getLast = function() {
        return this.items[this.order[this.length-1]];
    };
    this.getCurrent = function() {
        return this.items[this.order[this.cursorPos]];
    };
    this.getNext = function() {
        if (this.cursorPos == this.length-1) {
            return false;
        }
        else {
            this.cursorPos++;
            return this.items[this.order[this.cursorPos]];
        }
    };
    this.getPrevious = function() {
        if (this.cursorPos == 0) {
            return false;
        }
        else {
            this.cursorPos--;
            return this.items[this.order[this.cursorPos]];
        }
    };
    this.pop = function() {
        var pos = this.length-1;
        var ret = this.items[this.order[pos]];
        if (typeof ret != 'undefined') {
            this.removeAtPos(pos);
            return ret;
        }
        else {
            return false;
        }
    };
    this.set = function(cursorPos) {
        this.cursorPos = cursorPos;
    };
    this.reset = function() {
        this.cursorPos = 0;
    };
    this.end = function() {
        this.cursorPos = (this.length-1);
    };
    this.each = function(func) {
        for (var i = 0; i < this.order.length; i++) {
            var key = this.order[i];
            var val = this.items[key];
            func(key, val);
        }
        return true;
    };
    this.sort = function(specialSort, desc) {
        var sortFunc = this.getSort(specialSort, desc);
        var valSort = [];
        var keySort = [];
        for (var i = 0; i < this.order.length; i++) {
            valSort[i] = this.items[this.order[i]]; 
        }
        // Sort values
        valSort.sort(sortFunc);
        for (var i = 0; i < valSort.length; i++) {
            for (j in this.items) {
                if (this.items[j] == valSort[i]) {
                    keySort[i] = j;
                    this.removeItem(j);
                }
            }
        }
        for (var i = 0; i < valSort.length; i++) {
            this.sort[i] = keySort[i];
            this.setItem(keySort[i], valSort[i]);
        }
    };
    this.sortByKey = function(specialSort, desc) {
        var sortFunc = this.getSort(specialSort, desc);
        this.order.sort(sortFunc);
    };
    // Sort methods
    // ==============
    this.getSort = function(specialSort, desc) {
        var sortFunc = null;
        if (typeof specialSort == 'function') {
            sortFunc = specialSort;
        }
        else {
            if (specialSort == true) {
                sortFunc = desc ? this.simpleDescNoCase : this.simpleAscNoCase;
            }
            else {
                sortFunc = desc ? this.simpleDescCase : this.simpleAscCase;
            }
        }
        return sortFunc;
    };
    this.simpleAscCase = function(a, b) {
        return (a >= b) ?  1 : -1;
    };
    this.simpleDescCase = function(a, b) {
        return (a < b) ?  1 : -1;
    };
    this.simpleAscNoCase = function(a, b) {
        return (a.toLowerCase() >= b.toLowerCase()) ? 1 : -1;
    };
    this.simpleDescNoCase = function(a, b) {
        return (a.toLowerCase() < b.toLowerCase()) ? 1 : -1;
    };
}


IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 423
E.....@.@.su.........`.......9.h...........
...X....GET /scooby/js/scooby/util/debug.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 1950
E.....@.@.@............`.9.h...............
...a...XHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"1738-1147764336000"
Last-Modified: Tue, 16 May 2006 07:25:36 GMT
Content-Type: text/javascript
Content-Length: 1738
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
 /**
  * Various functions to be used during development and debugging.
  *
  */
  
function genericToString(){ 
    var str = ""; 
    for (var propName in this){ 
        var prop = this[propName]; 
        if (typeof prop != "function"){ 
            str += propName + ": '" + prop + "'; "; 
        } 
    } 
    return str; 
}

Timer = function(functionName) {
   this.functionName = functionName;
   var now = new Date();
   this.startTime = now.getTime();
   this.endTime = 0;
   log.debug("Start function '" + functionName + "'");
}

Timer.prototype.end = function(){
    var now = new Date();
    this.endTime = now.getTime();
    var elapsedTime = this.endTime - this.startTime;
    log.debug("End function '" + this.functionName + "'; elapsedTime: " 
        + elapsedTime + "ms");
}

Timer.prototype.toString = genericToString;

function timeFunction(object, functionName){
   var oldMethod = object[functionName];
   object[functionName] = function(){
       var timer = new Timer(functionName);
       var result = oldMethod.apply(object, arguments);
       timer.end();
       return result;
   }
}

IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 426
E....>@.@............_....h..(.............
...g....GET /scooby/js/scooby/util/validate.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 3195
E...N$@.@.."..........._.(....jp....
......
...h...gHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"2983-1147764336000"
Last-Modified: Tue, 16 May 2006 07:25:36 GMT
Content-Type: text/javascript
Content-Length: 2983
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

var Validate = new function() {

    this.dateFormat = function(str) {
        // Checks for the following valid date formats:
        // MM/DD/YY MM/DD/YYYY MM-DD-YY MM-DD-YYYY MM.DD.YY MM.DD.YYYY
        // Also separates date into month, day, and year variables
        // var pat = /^(\d{1,2})(\/|-|.)(\d{1,2})\2(\d{2}|\d{4})$/;
        // To require a 4 digit year entry, use this line instead:
        var pat = /^(\d{1,2})(\/|-)(\d{1,2})\2(\d{4})$/;
        var errMsg = '';
        
        // Check format
        var matchArray = str.match(pat);
        if (!matchArray) {
            errMsg += 'Date is not in a valid format.\n';
        }
        else {
            // Parse date parts into vars
            month = matchArray[1];
            day = matchArray[3];
            year = matchArray[4];
            // Month range
            if (month < 1 || month > 12) {
                errMsg += 'Month must be between 1 and 12.\n';
            }
            // Day range
            if (day < 1 || day > 31) {
                errMsg += 'Day must be between 1 and 31.\n';
            }
            // Day 31 for correct months
            if ((month == 4 || month == 6 || month == 9 || month == 11) 
                && day == 31) {
                errMsg += 'Month ' + month + ' does not have 31 days.\n';
            }
            // Leap year stuff
            if (month == 2) {
                var isLeap = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
                if (day > 29 || (day == 29 && !isLeap)) {
                    errMsg += 'February ' + year + ' does not have ' + day + ' days.\n';
                }
            }
        }
        return errMsg;
    }
    this.timeFormat = function(str) {
        var pat = /^(\d{1,2})(:)(\d{2})$/;
        var errMsg = '';
        
        // Check format
        var matchArray = str.match(pat);
        if (!matchArray) {
            errMsg += 'Time is not in a valid format.\n';
        }
        else {
            hours = matchArray[1];
            minutes = matchArray[3];
            if (hours < 1 || hours > 12) {
                errMsg += 'Hour must between 1 and 12.';
            }
            if (minutes < 0 || minutes > 59) {
                errMsg += 'Minutes must between 00 and 59.';
            }
        }
        return errMsg;
    }
}

Validate.constructor = null;

IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 422
E...$.@.@..~.........[...~#	...N...........
.......MGET /scooby/js/scooby/util/text.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 1309
E..Q6o@.@..6...........[...N.~$......F.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"1097-1147764336000"
Last-Modified: Tue, 16 May 2006 07:25:36 GMT
Content-Type: text/javascript
Content-Length: 1097
Date: Sun, 08 Oct 2006 23:34:04 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

/**
 * @fileoverview Calendar events -- links the Block to the CalEventData
 * @author Matthew Eernisse mailto:mde@osafoundation.org
 * @license Apache License 2.0
 */

/**
 * @object Text -- object to provide some basic text processing
 * features, uppercasing, formatting number, etc.
 */

Text = new function() {
    this.uppercaseFirst = function(str) {
        first = str.substr(0, 1).toUpperCase();
        return first + str.substr(1);
    }
}

Text.constructor = null;

IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 429
E....{@.@.Q..........^.../:z...L...........
.......TGET /scooby/js/scooby/model/scoobydate.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 9000
E.#\u.@.@..............^...L./<'....!Q.....
...-....HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"11969-1148626806000"
Last-Modified: Fri, 26 May 2006 07:00:06 GMT
Content-Type: text/javascript
Content-Length: 11969
Date: Sun, 08 Oct 2006 23:34:04 GMT


/**
 * A scooby date is how we represent date-times in Scooby's events
 *
 * The absence of a timezone together with the utc property being false indicates
 * a floating date-time.
 *
 * @param year the full year of the date
 * @param month the month of the date. January is 0.
 * @param minutes the minutes of the time
 * @param seconds the seconds of the time
 * @param timezone the timezone of the time
 * @param utc Boolean indicates that this is a utc time
 */
function ScoobyDate(year, month, date, hours, minutes, seconds, timezone, utc) {
    this.year = year ? year : 0;
    this.month = month ? month : 0;
    this.date = date ? date : 0;
    this.hours= hours ? hours : 0;
    this.minutes = minutes ? minutes : 0;
    this.seconds = seconds ? seconds : 0;
    this.timezone = timezone ? timezone : null;
    this.utc = utc ? utc : false;
}
    ScoobyDate.prototype.getDate = function() {
        return this.date;
    };
    
    ScoobyDate.prototype.getFullYear = function() {
        return this.getYear();
    };
    
    ScoobyDate.prototype.getHours = function() {
        return this.hours;
    };
    ScoobyDate.prototype.getMinutes = function() {
        return this.minutes;
    };
    ScoobyDate.prototype.getMonth = function() {
        return this.month;
    };
    ScoobyDate.prototype.getSeconds = function() {
        return this.seconds;
    };
    ScoobyDate.prototype.getYear = function() {
        return this.year;
    };
    ScoobyDate.prototype.setDate = function(dat) {
        this.date = dat;
    };
    ScoobyDate.prototype.setFullYear = function(yea) {
        this.setYear(yea);
    };
    ScoobyDate.prototype.setHours = function(hou, min) {
        this.hours = hou;
        if (min) {
            this.setMinutes(min);
        }
    };
    ScoobyDate.prototype.setMinutes = function(min) {
        this.minutes = min;
    };
    ScoobyDate.prototype.setMonth = function(mon) {
        this.month = mon;
    };
    ScoobyDate.prototype.setSeconds = function(sec) {
        this.seconds = sec;
    };
    ScoobyDate.prototype.setYear = function(yea) {
        this.year = yea;
    };
    /**
     * Returns the time in milliseconds since January 1st, 1970 UTC.
     */
    ScoobyDate.prototype.toUTC = function() {
        var utc = Date.UTC(this.getYear(), this.getMonth(), this.getDate(),
            this.getHours(), this.getMinutes(), this.getSeconds());
        return(utc - this.getTimezoneOffsetMs());
    };
    /**
     * Updates a ScoobyDate based on a UTC stamp
     * Naive implementation assumes timezone offset for new UTC does not change
     */
    ScoobyDate.prototype.updateFromUTC = function(utc) {
        var dt = null;
        // Get a fake Date object in UTC frame of reference
        dt = new Date(utc + this.getTimezoneOffsetMs());
        // Update ScoobyDate values based on UTC values
        this.year = dt.getUTCFullYear();
        this.month = dt.getUTCMonth();
        this.date = dt.getUTCDate();
        this.hours = dt.getUTCHours();
        this.minutes = dt.getUTCMinutes();
        this.seconds = dt.getUTCSeconds();
        // Update TZ if needed
        if (this.timezone) {
            this.timezone = ScoobyDate.getNewTimezoneForUTC(this.timezone, utc);
        }
    };
    /**
     * Updates a ScoobyDate based on a values from a local JS Date
     * Naive implementation assumes timezone offset for new UTC does not change
     * BANDAID: NEEDS TO BE REFACTORED
     */
    ScoobyDate.prototype.updateFromLocalDate = function(dt) {
        // Update ScoobyDate values based on local JS date values
        // Used to make changes to dates that are already in a specific timezone
        this.year = dt.getFullYear();
        this.month = dt.getMonth();
        this.date = dt.getDate();
        this.hours = dt.getHours();
        this.minutes = dt.getMinutes();
        this.seconds = dt.getSeconds();
    };
    /**
     * Returns the offset from GMT in minutes
     * Minus GMT results sensibly in negative values -- this is opposite
     * behavior from JavaScript's Date object
     */
    ScoobyDate.prototype.getTimezoneOffset = function() {
        var offsetMin = 0;

        // Is UTC, no need to do more work
        if (this.utc) {
            offsetMin = 0;
        }
        // Has timezone, use offset from timezone
        if (this.timezone) {
            offsetMin = this.timezone.minutesOffset;
        }
        // No timezone, no UTC -- must be a floating date
        else {
            offsetMin = this.getUserPrefTimezoneOffset();
        }
        return offsetMin;
    };
    /**
     * Returns the offset from GMT in milliseconds
     */
    ScoobyDate.prototype.getTimezoneOffsetMs = function() {
        return(this.getTimezoneOffset()*60*1000);
    };
    /**
      * One place to go to get the user-pref timezone offset for a ScoobyDate
      * This should ultimately work with the Scooby app's user prefs
      * Or independently with fallback to the normal browser local offset
      */
    ScoobyDate.prototype.getUserPrefTimezoneOffset = function() {
        var offsetMin = 0;
        // Try to look up user pref
        if (typeof Pref == 'object') {
            offsetMin = Pref.getTimezoneOffset(this);
        }
        // Otherwise punt and go with the browser's offset for that date
        else {
            offsetMin = ScoobyDate.getBrowserTimezoneOffset(
                this.getYear(), this.getMonth(), this.getDate(),
                this.getHours(), this.getMinutes(), this.getSeconds());
        }
        return offsetMin;
    };
    /**
     * Formats the date in it's original timezone, using the strftime function
     * found in date.js.
     *
     * For example, if the timezone of the ScoobyDate is EST and it's
     * Date is "1/2/2006 19:30", than that is the date that this function will
     * return no matter what your local timezone is.
     */
    ScoobyDate.prototype.strftime = function(formatString){
        // No need to do any mucking around with UTC offsets or anything
        // for this function, since all we care about is the output
        var d = new Date(this.getYear(), this.getMonth(), this.getDate(),
            this.getHours(), this.getMinutes(), this.getSeconds());
        return Date.strftime(formatString, d);
    };
    /**
     * Increments by the desired number of specified units
     */
    ScoobyDate.prototype.add = function(interv, incr) {
        var dt = null;
        var ret = null;
        // Get incremented localtime JS Date obj
        dt = Date.add(interv, incr, this.toUTC());
        // Update this date based on the new UTC
        this.updateFromUTC(dt.getTime());
    };
    /**
     * Formats the ScoobyDate according to what time the date falls on in the user's
     * local timezone.
     *
     * For example, if this ScoobyDate represents the date "1/1/2006 12am PST"
     * and your local timezone is EST, the date will be formatted as "1/1/2006 3am"
     */
    ScoobyDate.prototype.strftimeLocalTimezone = function(formatString){
            var localScoobyDate = this.createLocalScoobyDate();
            return localScoobyDate.strftime(formatString);
    };
    /**
     * Returns what the ScoobyDate representing the same point in time as this ScoobyDate,
     * but in the user's local timezone.
     *
     * For example, if this ScoobyDate represents the date "1/1/2006 12am PST"
     * and your local timezone is EST, the returned date will be "1/1/2006 3am EST"
     */
    ScoobyDate.prototype.createLocalScoobyDate = function(){
        //For now, we can cheat using new Date() since that gives us a local date
        //with the given UTC
        var utc = this.toUTC();
        var dt = new Date(utc);
        var tz = ScoobyTimezone.getLocalTimezone(utc);
        var scoobDt = new ScoobyDate(dt.getFullYear(), dt.getMonth(),
            dt.getDate(), dt.getHours(), dt.getMinutes(),
            dt.getSeconds(), tz, false);
        return scoobDt;
    };
    /**
     * Returns the day of the week occupied by a ScoobyDate
     * but in the user's local timezone.
     */
    ScoobyDate.prototype.getLocalDay = function() {
        var localDt = new Date(this.toUTC());
        return localDt.getDay();
    };
    ScoobyDate.prototype.after = function(dt){
        var utc = (typeof dt == "number") ? dt : dt.toUTC();
        return  this.toUTC() > utc;
    };
    ScoobyDate.prototype.before = function(dt){
        var utc = (typeof dt == "number") ? dt : dt.toUTC();
        return  this.toUTC() < utc;
    };
    /**
     * Returns UTC timestamp for a ScoobyDate
     * Leave for now for API compatibility
     */
    ScoobyDate.prototype.getTime = function() {
       return this.toUTC();
    };
    
    ScoobyDate.prototype.toString = genericToString;


// S
IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 3183
E...u.@.@..............^...t./<'....
......
...-...-coobyDate static methods
// ===========================
ScoobyDate.clone = function(sdt) {
    var ret = new ScoobyDate(sdt.getFullYear(), sdt.getMonth(),
            sdt.getDate(), sdt.getHours(), sdt.getMinutes(),
            sdt.getSeconds(), null, sdt.utc);
    if (sdt.timezone) {
        ret.timezone = new ScoobyTimezone(sdt.timezone.id,
            sdt.timezone.minutesOffset, sdt.timezone.name);
    }
    return ret;
}

/**
 * Returns the UTC offset (in milliseconds) for a particular date for the user's
 * current timezone.
 * Right now the user's timezone is the javascript timezone, so this implementation
 * is rather trivial. Will be useful once a settable timezone preferences exists.
 */
ScoobyDate.getBrowserTimezoneOffset = function(year, month, day, hours, minutes, seconds){
    var date = new Date(year, month, day, hours, minutes, seconds, 0);
    // Return a negative value for minus-GMT offset -- opposite behavior from JS
    return (date.getTimezoneOffset()*-1);
}

/**
 * Returns the difference in specified units between two ScoobyDates
 */
ScoobyDate.diff = function(interv, sdt1, sdt2) {
    return Date.diff(interv, sdt1.toUTC(), sdt2.toUTC());
}

/**
 * Returns a new ScoobyDate incremented the desired number of units
 */
ScoobyDate.add = function(sdt, interv, incr) {
    var ret = ScoobyDate.clone(sdt);
    ret.add(interv, incr);
    return ret;
}

/**
 * Returns a new ScoobyTimezone given an existing ScoobyTimezone and UTC stamp
 * The timezone minutesOffset and name may change, timezone id should be constant
 * This function is just stubbed out for now -- returns same data passed in
 * Later this will likely have to be migrated into the Scooby service
 * so we can grab this info off the server
 */
ScoobyDate.getNewTimezoneForUTC = function(tz, utc) {
    return tz;
}

/**
 * A ScoobyTimeZone is a reference to a timezone on the Scooby Server. It does not
 * fully specify a timezone, but merely indicates a reference and the UTC offset in
 * minutes for the date to which it belongs. Notice that this offset is not a constant
 * due to observances like Daylight Savings Time
 *
 * @param timezoneID the unique ID referencing the timezone
 * @param minutesOffset the number of minutes that this timezone differs from
 *                      UTC at a particular moment.
 */
function ScoobyTimezone(id, minutesOffset, name){
    this.id = id;
    this.minutesOffset = minutesOffset;
    this.name = name;

}

ScoobyTimezone.prototype.toString = genericToString;

/**
 * Returns the local timezone with offset set properly for a particular date.
 */
ScoobyTimezone.getLocalTimezone = function(utc){
   var dt = new Date(utc);
   var dtStr = dt.toString();
   var nameArr = [];
   var nameStr = '';

   // Hack to get something reasonable for the name
   // Use for the ID too, for now
   nameArr = dtStr.match(/\(\S+\)/);
   nameStr = (nameArr && nameArr[0]) ? nameArr[0].replace(/\)/, '').replace(/\(/, '') : 'Unknown';

   var timezone = new ScoobyTimezone(nameStr, (dt.getTimezoneOffset()/-1), nameStr);
   return timezone;
}

ScoobyTimezone.clone = function(stz) {
    return new ScoobyTimezone(stz.id, stz.minutesOffset, stz.name);
}
IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 424
E.....@.@.sr.........`.......9
............
...A...aGET /scooby/js/scooby/model/model.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 3337
E.
=..@.@.;............`.9
....J.....2.....
...J...AHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"3125-1149830942000"
Last-Modified: Fri, 09 Jun 2006 05:29:02 GMT
Content-Type: text/javascript
Content-Length: 3125
Date: Sun, 08 Oct 2006 23:34:05 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

function CalEventData(id, title, description, start, end, allDay,
    pointInTime, anyTime, recurrenceRule, status, masterEvent, instance) {

    this.id = id;
    this.title = title;
    this.description = description ? description : null;
    this.start = start;
    this.end = end;
    this.allDay = allDay ? allDay : false;
    this.pointInTime = pointInTime ? pointInTime : false;
    this.anyTime = anyTime ? anyTime : false;
    this.recurrenceRule = recurrenceRule ? recurrenceRule : null;
    this.status = status ? status : null;
    this.masterEvent = masterEvent ? masterEvent : false;
    this.instance = instance ? instance : false;
    this.javaClass = 'org.osaf.scooby.model.Event';
};
CalEventData.prototype = {
    toString: genericToString
}

CalEventData.clone = function(data) {
    var ret = new CalEventData(
        data.id,
        data.title,
        data.description,
        ScoobyDate.clone(data.start),
        ScoobyDate.clone(data.end),
        data.allDay,
        data.pointInTime,
        data.anyTime,
        RecurrenceRule.clone(data.recurrenceRule),
        data.status,
        data.masterEvent,
        data.instance,
        data.javaClass);
    return ret;
}


/**
 * A recurrence rule specifies how to repeat a given event.
 */
function RecurrenceRule(){
    /**
     * Frequencies for recurrence.
     */
    this.FREQUENCY_WEEKLY = "weekly";
    this.FREQUENCY_DAILY = "daily";
    this.FREQUENCY_MONTHLY = "monthly";
    this.FREQUENCY_YEARLY = "yearly";
    this.FREQUENCY_BIWEEKLY = "biweekly";

    /**
     * Specifies how often to repeat this event.
     * Must be one of the frequency constants above.
     */
    this.frequency = null;

    /**
     * The date to repeat this event until.
     */
    this.endDate = null;

    /**
     * For events not created in Scooby that have more complex rules than scooby
     * allows, a text representation of the rule appears here but is not editable.
     */
    this.customRule = null;
}

RecurrenceRule.prototype = {
    toString: genericToString
}

RecurrenceRule.clone = function(rule) {
    var ret = null;
    if (rule) {
        ret = new RecurrenceRule();
        ret.frequency = rule.frequency;
        ret.endDate = rule.endDate;
        ret.customRule = rule.customRule;
    }
    return ret;
}

function StatusTemplate() {
    this.CONFIRMED = "confirmed";
    this.TENTATIVE = "tentative";
    this.CANCELLED = "fyi";
}

function CalendarMetadata(){
    this.name = null;
    this.path = null;
}
IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 421
E....@@.@............_....jp.((Z...........
...Q...hGET /scooby/js/scooby/ui/block.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 9000
E.#\N%@.@..t..........._.((Z..l.....!Q.....
...c...QHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"31251-1149739486000"
Last-Modified: Thu, 08 Jun 2006 04:04:46 GMT
Content-Type: text/javascript
Content-Length: 31251
Date: Sun, 08 Oct 2006 23:34:05 GMT

/* * Copyright 2006 Open Source Applications Foundation *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

/**
 * @fileoverview Event blocks that represent the span of time
 * of an event on the calendar
 * @author Matthew Eernisse mailto:mde@osafoundation.org
 * @license Apache License 2.0
 *
 * Has two sub-classes, HasTimeBlock and NoTimeBlock to represent
 * the two main areas where blocks get displayed. HasTime are normal
 * events in the scrolling area, and NoTime are the all-day events
 * in the resizeable area at the top.
 * HasTimeBlock for a multi-day event may be a composite made up of 
 * a main div element and a bunch of auxilliary divs off to the side.
 */

/**
 * @object A visual block to represent the span of time of a calendar
 * event
 */
function Block() {
    // Properties for position and size
    this.top = 0;
    this.left = 0;
    this.height = 0;
    this.width = 0;
    // Whether or not this particular Block is selected.
    // TO-DO: Figure out if this is still used ... ?
    this.selected = false;
    // 30-min minimum height, minus a pixel at top and bottom
    // per retarded CSS spec for borders
    this.unit = (HOUR_UNIT_HEIGHT/2)-2;
    // DOM elem ref to the primary div for the Block
    this.div = null;
    // DOM elem ref for inner div of the Block
    this.innerDiv = null;
    // The separator plus ID -- convenience to avoid
    // concatenating the same thing over and over
    this.idPrefix = '';
    // Array of div elems appearing to the side on multi-day normal
    // events
    this.auxDivList = [];

    /**
     * Convenience method that does all the visual update stuff 
     * for a block at one time
     */
    this.updateDisplayMain = function() {
        this.updateElements();
        this.hideProcessing();
        this.updateText();
    };
    /**
     * Updates the info displayed on a block for the event time
     * and description
     */
    this.updateText = function() {
        var ev = Cal.eventRegistry.getItem(this.id);
        var strtime = ev.data.start.strftime('%I:%M%p');
        // Trim leading zero if need be
        strtime = strtime.indexOf('0') == 0 ? strtime.substr(1) : strtime;
        // Display timezone info for event if it has one
        if (ev.data.start.timezone) {
            strtime += ' (' + ev.data.start.timezone.name + ')';
        }
        var timeDiv = document.getElementById(this.divId + 'Start' +
            Cal.ID_SEPARATOR + ev.id);
        var titleDiv = document.getElementById(this.divId + 'Title' +
            Cal.ID_SEPARATOR + ev.id);
        if (timeDiv) { 
            this.setText(timeDiv, strtime);
        }
        this.setText(titleDiv, ev.data.title);
    };
    /**
     * A bit of a misnomer -- just static text at the moment
     * TO-DO: Add animation -- either GIF or using CSS effects
     */
    this.showStatusAnim = function() {
        var titleDiv = document.getElementById(this.divId + 'Title' +
            Cal.ID_SEPARATOR + this.id);
        this.setText(titleDiv, 'Processing ...');
    };
    /**
     * Toggle cursor to 'default' while block is in processing
     * state -- should not appear to be draggable
     */
    this.mainAreaCursorChange = function(isProc) {
        var cursorChange = isProc ? 'progress' : 'move';
        document.getElementById(this.divId + 'Content' +
            Cal.ID_SEPARATOR + this.id).style.cursor = cursorChange;
    };
    this.getPlatonicLeft = function() {
        var ev = Cal.eventRegistry.getItem(this.id);
        var diff = (Date.diff('d', Cal.viewStart.getTime(), ev.data.start.getTime()));
        return (diff * Cal.dayUnitWidth);
            
    };
    this.getPlatonicWidth = function() {
        var ev = Cal.eventRegistry.getItem(this.id);
        var diff = (ScoobyDate.diff('d', ev.data.start, ev.data.end))+3;
        return (diff * Cal.dayUnitWidth);
    }
    /**
     * Cross-browser wrapper for setting CSS opacity
     * Would like to use this again if I can figure out a
     * workaround for the weird scrolling-div-breakage it causes
     */
    this.setOpacity = function(opac) {
        elem = this.div;
        // =============
        // opac is a whole number to be used as the percent opacity
        // =============
        // IE uses a whole number as a percent (e.g. 75 for 75%)
        //  Moz/compat uses a fractional value (e.g. 0.75)
        var nDecOpacity = opac/100;
        if (document.all) {
            elem.filters.alpha.opacity = opac;
        }
        elem.style.opacity = nDecOpacity;
        return true;
    }
    /**
     * Use DOM to set text inside a node
     */
    this.setText = function(node, str) {
        if (node.firstChild) {
            node.removeChild(node.firstChild);
        }
        node.appendChild(document.createTextNode(str));
    };
    /**
     * Change color of block to indicate (1) selected (2) normal
     * or (3) processing
     */
    this.setState = function(isProc) {
        var isSel = null;
        var stateId = 0; // 1 selected 2 normal 3 processing
        // If this block is processing, change to 'processing' color
        if (isProc) {
            stateId = 3;
        }
        // Change block back after completing processing --
        // change it back to normal 'unselected' color, or 
        // 'selected' color if this block is the most recently
        // clicked one
        else {
            stateId = (Cal.currSelObj && this.id == Cal.currSelObj.id) ?
                1 : 2;
        }
        this.mainAreaColorChange(stateId);
    }
    /**
     * Change color of block to indicate (1) selected (2) processing
     * or (3) normal, unselected
     */
    this.mainAreaColorChange = function(stateId) {
        var className = '';
        var mainDiv = document.getElementById(this.divId + Cal.ID_SEPARATOR +
            this.id);
        // If this block is processing, change to 'processing' color
        switch (stateId) {
            case 1:
                className = 'eventDivSel';
                break;
            case 2:
                className = 'eventDiv';
                break;
            case 3:
                className = 'eventDivProc';
                break;
            default:
                // Do nothing
                break;
        }
        mainDiv.className = className;
        // Update color on all associated aux divs for multi-day events
        if (this.auxDivList.length) {
            for (var i = 0; i < this.auxDivList.length; i++) {
                auxDiv = this.auxDivList[i];
                auxDiv.className = className
            }
        }
    }
    /**
     * Make the block look selected -- change color and
     * move forward to z-index of 25
     */
    this.setSelected = function() {
        var auxDiv = null;

        this.mainAreaColorChange(1);
       
        // Set the z-index to the front
        this.div.style.zIndex = 25;
        if (this.auxDivList.length) {
            for (var i = 0; i < this.auxDivList.length; i++) {
                auxDiv = this.auxDivList[i];
                //auxDiv.style.background = SEL_BLOCK_COLOR;
                auxDiv.style.zIndex = 25;
            }
        }
    }
    /**
     * Make the block look unselected -- change color and
     * move back to z-index of 1
     */
    this.setDeselected = function() {
        var auxDiv = null;
        
        this.mainAreaColorChange(2);
        
        // Set the z-index to the back
        this.div.style.zIndex = 1;
        if (this.auxDivList.length) {
            for (var i = 0; i < this.auxDivList.length; i++) {
                auxDiv = this.auxDivList[i];
                //auxDiv.style.background = UNSEL_BLOCK_COLOR;
                auxDiv.style.zIndex = 1;
            }
        }
    }
}

/**
 * HasTimeBlock -- sub-class of Block
 * Normal events, 'at-time' events -- these sit in the scrollable 
 * area of the main viewing area
 */
function HasTimeBlock(id) {
    this.id = id;
}
HasTimeBlock.prototype = new Block();

// Div elem prefix -- all component divs of normal event blocks
// begin with this
HasTimeBlock.prototype.divId = 'eventDiv';
// Does a multi-day event start before the viewable area
HasTimeBlock.prototype.startsBeforeViewRange = false;
// Does a multi-day event extend past the
IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 9000
E.#\N&@.@..s..........._.(K...l.....!Q.....
...c...c viewable area
HasTimeBlock.prototype.endsAfterViewRange = false;

/**
 * Change block color to 'processing' color
 * Change the cursors for the resize handles at top and bottom,
 * and for the central content div
 * Display status animation
 */
HasTimeBlock.prototype.showProcessing = function() {
    this.setState(true);
    this.resizeHandleCursorChange(true);
    this.mainAreaCursorChange(true);
    this.showStatusAnim();
}

/**
 * Change the cursors for the resize handles at top and bottom
 * Change to 'default' when processing so it won't look draggable
 */
HasTimeBlock.prototype.resizeHandleCursorChange = function(isProc) {
    var topChange = isProc ? 'default' : 'n-resize';
    var bottomChange = isProc ? 'default' : 's-resize';
    var topDiv = document.getElementById(this.divId + 'Top' + 
        Cal.ID_SEPARATOR + this.id);
    var bottomDiv = document.getElementById(this.divId + 'Bottom' + 
        Cal.ID_SEPARATOR + this.id);
    topDiv.style.cursor = topChange;
    // Timed events that extend beyond the viewable area
    // will not have a bottom resize handle
    if (bottomDiv) {
        bottomDiv.style.cursor = bottomChange;
    }
}

/**
 * Return the block to normal after processing
 */
HasTimeBlock.prototype.hideProcessing = function() {
    this.resizeHandleCursorChange(false);
    this.mainAreaCursorChange(false);
    this.setState(false);
}

/**
 * Update the block properties from an event
 * Called when editing from the form, or on drop after resizing/dragging
 * the block has to be updated to show the changes to the event
 */
HasTimeBlock.prototype.updateFromEvent = function(ev) {
    var unit = HOUR_UNIT_HEIGHT/2;
    var startPos = 0;
    var endPos = 0;
    var height = 0;
    var left = 0;
    var width = 0;
    
    // Events edited out of range
    // Move the block from view -- if the update fails, we need
    // to put it back
    if (ev.isOutOfViewRange()) {
       startPos = -10000;
       height = 1;
       left = -10000;
       width = 1;
    }
    // Events still on the canvas
    else {
        if (this.startsBeforeViewRange) {
            startPos = 0;
            left = 0;
        }
        else {
            startPos = Cal.calcPosFromTime(Date.strftime('%H:%M',
            ev.data.start.getTime()));
            left = (ev.data.start.getLocalDay())*Cal.dayUnitWidth;
        }
        endPos = Cal.calcPosFromTime(Date.strftime('%H:%M',
            ev.data.end.getTime()));
        height = endPos - startPos;
        left += (ev.conflictDepth * 10);

        width = Cal.dayUnitWidth - (ev.maxDepth * 10);
        
        // BANDAID: set min height if not multi-day event
        if (!this.auxDivList.length && (height < unit)) {
            height = unit;
        }
    }
    this.left = left;
    this.top = startPos;
    // Show one-pixel border of underlying divs
    // And one-pixel border for actual block div
    // (1 + (2 * 1)) = 3 pixels
    this.height = height - 3; 
    this.width = width - 3;

}

/**
 * Update an event from changes to the block -- usually called
 * when an event block is dragged or resized
 * The updated event is then passed back to the backend for saving
 * If the save operation fails, the event can be restored from 
 * the backup copy of the CalEventData in the event's dataOrig property
 */
HasTimeBlock.prototype.updateEvent = function(ev, dragMode) {

    var evStart = Cal.calcDateFromPos(this.left);
    var diff = this.auxDivList.length;
    var evEnd = Date.add('d', diff, evStart);
    var startTime = Cal.calcTimeFromPos(this.top);
    // Add +1 to height for border on background
    // Add +2 to height for border on block div
    var endTime = Cal.calcTimeFromPos(this.top+(this.height + 3));

    evStart.setHours(Cal.extractHourFromTime(startTime));
    evStart.setMinutes(Cal.extractMinutesFromTime(startTime));
    evEnd.setHours(Cal.extractHourFromTime(endTime));
    evEnd.setMinutes(Cal.extractMinutesFromTime(endTime));

    // Update ScoobyDates with new UTC values
    ev.data.start.updateFromUTC(evStart.getTime());
    ev.data.end.updateFromUTC(evEnd.getTime());
    return true;
}

/**
 * Insert a new event block
 * This method places the block (single- or multi-div) on the
 * scrollable area for normal events. This just puts them on the 
 * canvas in a hidden state. After this we have two more steps:
 * (1) Update block to reflect event's times using updateFromEvent
 * (2) Do sizing/positioning, and turn on visibility with updateDisplayMain
 */
HasTimeBlock.prototype.insert = function(id) {

    var ev = Cal.eventRegistry.getItem(id);
    var startDay = 0;
    var endDay = 0;
    var auxDivCount = 0;
    var blockDiv = null;
    var blockDivSub = null;
    var d = null;
    var view = null;
    
    if (ev.startsBeforeViewRange()) {
        startDay = 0;
        this.startsBeforeViewRange = true;
    }
    else {
        startDay = ev.data.start.getLocalDay();
    }
    if (ev.endsAfterViewRange()) {
        endDay = 6;
        this.endsAfterViewRange = true;
    }
    else {
        endDay = ev.data.end.getLocalDay();
    }
    auxDivCount = (endDay - startDay);
    
    this.idPrefix = Cal.ID_SEPARATOR + id;
    this.width = 1; 
    this.auxDivList = [];
    
    // Append event block to appropriate screen area for the type of Block
    view = document.getElementById('timedContentDiv');

    blockDiv = document.createElement('div');
    blockDivSub = document.createElement('div');

    // Event lozenge main div and components
    // -----------------------
    // Main lozenge div
    blockDiv.id = this.divId + this.idPrefix;
    blockDiv.className = 'eventDiv';
    blockDiv.style.width = this.width + 'px';

    // Just a small little bit of fun to change the border style of a block
    // depending on the status of the event.
    if (ev.data.status && ev.data.status.indexOf('TENTATIVE') > -1) {
        blockDiv.style.borderStyle = 'dashed';
    }

    // Resize-up handle
    blockDivSub.id = this.divId + 'Top' + this.idPrefix;
    blockDivSub.className = 'eventResizeTop';
    blockDivSub.style.height = BLOCK_RESIZE_LIP_HEIGHT + 'px';
    blockDiv.appendChild(blockDivSub);

    // Central content area
    blockDivSub = document.createElement('div');
    blockDivSub.id = this.divId + 'Content' + this.idPrefix;
    blockDivSub.className = 'eventContent';
    blockDivSub.style.marginLeft = BLOCK_RESIZE_LIP_HEIGHT + 'px';
    blockDivSub.style.marginRight = BLOCK_RESIZE_LIP_HEIGHT + 'px';

    // Start time display
    d = document.createElement('div');
    d.id = this.divId + 'Start' + this.idPrefix;
    d.className = 'eventTime';
    d.style.width = '100%'; // Needed for IE, which sucks
    blockDivSub.appendChild(d);

    // Title
    d = document.createElement('div');
    d.id = this.divId + 'Title' + this.idPrefix
    d.className = 'eventTitle';
    d.style.width = '100%'; // Needed for IE, which sucks
    blockDivSub.appendChild(d);

    blockDiv.appendChild(blockDivSub);

    // Before adding the bottom resize handle, add any intervening
    // auxilliary div elems for multi-day events
    // ------------------
    // Multi-day events -- for events that extend past the end of
    // the week, truncate number of added div elements
    // auxDivCount = auxDivCount > maxDiff ? maxDiff : auxDivCount;
    if (auxDivCount) {
        for (var i = 0; i < auxDivCount; i++) {
            // Append previous div
            view.appendChild(blockDiv);

            var blockDiv = document.createElement('div');
            blockDiv.id = this.divId + Cal.ID_SEPARATOR +  +
                'aux' + (i+1) + this.idPrefix;
            blockDiv.className = 'eventDiv';
            blockDiv.style.width = this.width + 'px';

            // Central content area
            blockDivSub = document.createElement('div');
            blockDivSub.id = this.divId + 'Content' + this.idPrefix;
            blockDivSub.className = 'eventContent';
            blockDiv.appendChild(blockDivSub);

            // Don't set height to 100% for empty content area of last aux div
            // It has resize handle at the bottom, so empty content area
            // gets an absolute numeric height when the Block gets placed and
            // sized in updateFromEvent
            if (this.endsAfterViewRange || (i < (auxDivCount-1))) {
                blockDivSub.style.height = '100%';
            }
        }
    }

    // Resize-down handle -- append either to single div,
    // or to final div for multi-day event -- don't append when
    // event extends past view area
    if (!this.endsAfterViewRange) {
        blockDivSub = document.createElement('div');
        blockDivSub.id = this.divId + 'Bottom' + this.idPrefix;
        blockDivSub.className = 'eventResizeBottom';
        blockDivSub.style.height = BLOCK_RESIZE_LIP_HEIGHT + 'px';
        blockDiv.appendChild(blockDivSub);
    }

    view.appendChild(blockDiv);

    // DOM node references
    this.div = document.getElementById(this.divId + thi
IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 9000
E.#\N'@.@..r..........._.(n...l.....!Q.....
...d...cs.idPrefix);
    this.innerDiv = document.getElementById(this.divId + 'Content' +
        this.idPrefix);
    // DOM node refs for multi-day divs
    if (auxDivCount) {
        for (var i = 0; i < auxDivCount; i++) {
            this.auxDivList[i] = document.getElementById(this.divId +
                Cal.ID_SEPARATOR +  + 'aux' + (i+1) + this.idPrefix);
        }
    }
    // All done
    return this.div;
}

/**
 * Removes the block -- including multiple divs for multi-day events
 */
HasTimeBlock.prototype.remove = function(id) {
    this.innerDiv.parentNode.removeChild(this.innerDiv);
    this.div.parentNode.removeChild(this.div);
    if (this.auxDivList.length) {
        for (var i = 0; i < this.auxDivList.length; i++) {
            auxDiv = this.auxDivList[i];
            auxDiv.parentNode.removeChild(auxDiv);
            this.auxDivList[i] = null;
        }
    }
    // Close IE memleak hole
    this.div = null;
    this.innerDiv = null;
}

/**
 * Move the left side of the block to the given pixel position
 * *** Note: the pos is passed in instead of using the property
 * because during dragging, we don't continuously update the
 * block properties -- we only update them on drop ***
 * @param pos The X pixel position for the block
 */
HasTimeBlock.prototype.setLeft = function(pos) {
    var leftPos = parseInt(pos);
    var auxDiv = null;
    this.div.style.left = leftPos + 'px';
    if (this.auxDivList.length) {
        for (var i = 0; i < this.auxDivList.length; i++) {
            leftPos += Cal.dayUnitWidth;
            auxDiv = this.auxDivList[i];
            auxDiv.style.left = leftPos + 'px';
        }
    }
}

/**
 * Move the top side of the block to the given pixel position
 * *** Note: the pos is passed in instead of using the property
 * because during dragging, we don't continuously update the
 * block properties -- we only update them on drop ***
 * @param pos The Y pixel position for the block
 */
HasTimeBlock.prototype.setTop = function(pos) {
    this.div.style.top = parseInt(pos) + 'px';
}

/**
 *
 */
HasTimeBlock.prototype.setWidth = function(width) {
    var w = parseInt(width);
    this.div.style.width = w + 'px';
    if (this.auxDivList.length) {
        for (var i = 0; i < this.auxDivList.length; i++) {
            auxDiv = this.auxDivList[i];
            auxDiv.style.width = w + 'px';
        }
    }
}

/**
 * Sizes an event block vertically -- or the starting and ending
 * blocks for a multi-day event. Note: in the case of a
 * multi-day event where the start time is later than the end time,
 * you will have a NEGATIVE value for 'size', which is WHAT YOU WANT.
 * @param size Int difference in start and end positions of the
 * event block, or of start and end blocks for a multi-day event
 */
HasTimeBlock.prototype.setHeight = function(size, overrideMulti) {
    var doMulti = ((this.auxDivList.length || this.endsAfterViewRange)
        && !overrideMulti);
    var mainSize = 0;
    var lastAuxSize = 0;

    // Do the head-scratching math stuff
    // -----------------------------------
    // Multi-day event
    if (doMulti) {
        // Height applied to FIRST div -- this div should stretch
        // all the rest of the way to the bottom of the scrolling area
        mainSize = (VIEW_DIV_HEIGHT - this.top);
        // Height applied to FINAL div -- this div should stretch
        // from the top of the scrolling area to the bottom of where the
        // normal size would be for a single-day event
        lastAuxSize = (this.top + size);
        lastAuxSize = lastAuxSize < this.unit ? this.unit : lastAuxSize;
    }
    // Single-day event
    else {
        // Set height for single div using the passed-in size
        size = size < this.unit ? this.unit : size;
        mainSize = size;
    }

    // Set the values
    // -----------------------------------
    // Main div and the inner content div
    this.div.style.height = mainSize + 'px';
    this.innerDiv.style.height =
        (mainSize-(BLOCK_RESIZE_LIP_HEIGHT*2)) + 'px';
    // If multi-day event, do the inner aux divs and final aux div
    if (doMulti) {
        for (var i = 0; i < this.auxDivList.length; i++) {
            auxDiv = this.auxDivList[i];
            // Inner aux div(s)
            if (this.endsAfterViewRange || (i < (this.auxDivList.length-1))) {
                auxDiv.style.height = VIEW_DIV_HEIGHT + 'px';
            }
            // Final aux div
            else if (i == (this.auxDivList.length-1)) {
                // Main outer div
                auxDiv.style.height = lastAuxSize + 'px';
                // Empty internal content div
                auxDiv.firstChild.style.height =
                    (lastAuxSize-BLOCK_RESIZE_LIP_HEIGHT) + 'px';
            }
        }
    }
}

/**
 * Position and resize the block, and turn on its visibility
 */
HasTimeBlock.prototype.updateElements = function() {
    this.setLeft(this.left);
    this.setTop(this.top);
    this.setHeight(this.height);
    this.setWidth(this.width);
    this.makeVisible();
}

HasTimeBlock.prototype.makeVisible = function() {
    // Turn on visibility for all the divs
    this.div.style.visibility = 'visible';
    if (this.auxDivList.length) {
        for (var i = 0; i < this.auxDivList.length; i++) {
            auxDiv = this.auxDivList[i];
            auxDiv.style.visibility = 'visible';
        }
    }
}

/**
 * Get the pixel position of the top of the block div, or for
 * the far-left div in a multi-day event
 */
HasTimeBlock.prototype.getTop = function() {
    var t = this.div.offsetTop;
    return parseInt(t);
}

/**
 * Get the pixel posiiton of the bottom of the block div, or for
 * the far-right div in a multi-day event
 */
HasTimeBlock.prototype.getBottom = function() {

    var t = 0;
    var h = 0;
    var lastAux = null;
    var ret = 0;

    // Multi-day event
    if (this.auxDivList.length) {
        lastAux = this.auxDivList[this.auxDivList.length-1];
        ret = parseInt(lastAux.offsetHeight);
    }
    // Single-day event
    else {
        t = this.div.offsetTop;
        h = this.div.offsetHeight;
        ret = parseInt(t+h);
    }
    return ret;
}

/**
 * Get the pixel position of the far-left edge of the event block
 * or blocks in a muli-day event
 */
HasTimeBlock.prototype.getLeft = function() {
    var l = this.div.offsetLeft;
    return parseInt(l);
}

/**
 * NoTimeBlock -- sub-class of Block
 * All-day events, 'any-time' events -- these sit up in the
 * resizable area at the top of the UI
 */
function NoTimeBlock(id) {
    this.id = id;
}
NoTimeBlock.prototype = new Block();

// All-day events are a fixed height --
// I just picked 16 because it looked about right
NoTimeBlock.prototype.height = 16;
// Div elem prefix -- all component divs of normal event blocks
// begin with this
NoTimeBlock.prototype.divId = 'eventDivAllDay';

/**
 * Change block color to 'processing' color
 * Change the cursors for the resize handles at top and bottom,
 * and for the central content div
 * Display status animation
 */
NoTimeBlock.prototype.showProcessing = function() {
    this.setState(true);
    this.mainAreaCursorChange(true);
    this.showStatusAnim();
}

/**
 * Return the block to normal after processing
 */
NoTimeBlock.prototype.hideProcessing = function() {
    this.setState(false);
    this.mainAreaCursorChange(false);
}

/**
 * Update the block properties from an event
 * Called when editing from the form, or on drop after resizing/dragging
 * the block has to be updated to show the changes to the event
 */
NoTimeBlock.prototype.updateFromEvent = function(ev, temp) {
    var diff = ScoobyDate.diff('d', ev.data.start, ev.data.end) + 1;

    this.left = this.getPlatonicLeft();
    this.width = (diff*Cal.dayUnitWidth)-3;
    if (!temp) {
        this.top = ev.allDayRow*19;
    }
}

/**
 * Update an event from changes to the block -- usually called
 * when an event block is dragged or resized
 * The updated event is then passed back to the backend for saving
 * If the save operation fails, the event can be restored from
 * the backup copy of the CalEventData in the event's dataOrig property
 */
NoTimeBlock.prototype.updateEvent = function(ev, dragMode) {
    // Dragged-to date
    var evDate = Cal.calcDateFromPos(this.left);
    // Difference in days
    var diff = Date.diff('d', ev.data.start.getTime(), evDate.getTime());
    // Increment start and end by number of days
    // User can't resize all-day events
    ev.data.start.add('d', diff);
    ev.data.end.add('d', diff);

    return true;
}

/**
 * Calculate the width of an all-day event block -- for events that
 * have an end past the current view span, make sure the width truncates
 * at the end of the view span properly -- this is currently hard-coded
 * to Saturday.
 * TO-DO: Check the view type to figure out the end of the view span
 */
NoTimeBlock.prototype.calcWidth = function(startDay, ev) {

    var diff = 0;
    var maxDiff = (7-startDay);
    var width = 0;

    diff = (Scooby
IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 4465
E...N(@.@..(..........._.(....l............
...d...cDate.diff('d', ev.data.start, ev.data.end))+1;

    diff = (diff > maxDiff) ? maxDiff : diff;
    width = (diff*Cal.dayUnitWidth)-1;

    return width;
}

/**
 * Insert a new event block
 * This method places the block on the resizable area for
 * all-day events. This just puts them on the canvas in a hidden state.
 * After this we have two more steps:
 * (1) Update block to reflect event's times using updateFromEvent
 * (2) Do sizing/positioning, and turn on visibility with updateDisplayMain
 */
NoTimeBlock.prototype.insert = function(id) {
    var ev = Cal.eventRegistry.getItem(id);
    var blockDiv = document.createElement('div');
    var blockDivSub = document.createElement('div');
    var d = null;
    var view = null;

    this.idPrefix = Cal.ID_SEPARATOR + id;
    this.width = 1;

    // Append event block to appropriate screen area for the type of Block
    view = document.getElementById('allDayContentDiv');
    // Event lozenge main div and components
    // -----------------------
    // Main lozenge div
    blockDiv.id = this.divId + this.idPrefix;
    blockDiv.className = 'eventDiv';
    // Set other style props separately because setAttribute() is broken in IE
    blockDiv.style.width = this.width + 'px';

    // Central content area
    blockDivSub.id = this.divId + 'Content' + this.idPrefix;
    blockDivSub.className = 'eventContent';
    blockDivSub.style.whiteSpace = 'nowrap';
    
    // Title
    d = document.createElement('div');
    d.id = this.divId + 'Title' + this.idPrefix;
    d.className = 'eventTitle';
    d.style.marginLeft = BLOCK_RESIZE_LIP_HEIGHT + 'px';
    blockDivSub.appendChild(d);
    
    blockDiv.appendChild(blockDivSub);

    view.appendChild(blockDiv);

    // DOM node references
    this.div = document.getElementById(this.divId + this.idPrefix);
    this.innerDiv = document.getElementById(this.divId + 'Content' +
        this.idPrefix);

    return this.div;
}

/**
 * Removes the block
 */
NoTimeBlock.prototype.remove = function(id) {
    this.innerDiv.parentNode.removeChild(this.innerDiv);
    this.div.parentNode.removeChild(this.div);
    this.div = null;
    this.innerDiv = null;
}

/**
 * Move the left side of the block to the given pixel position
 * *** Note: the pos is passed in instead of using the property
 * because during dragging, we don't continuously update the
 * block properties -- we only update them on drop ***
 * @param pos The X pixel position for the block
 */
NoTimeBlock.prototype.setLeft = function(pos) {
    this.div.style.left = parseInt(pos) + 'px';
}

/**
 * Move the top side of the block to the given pixel position
 * *** Note: the pos is passed in instead of using the property
 * because during dragging, we don't continuously update the
 * block properties -- we only update them on drop ***
 * @param pos The Y pixel position for the block
 */
NoTimeBlock.prototype.setTop = function(pos) {
    this.div.style.top = parseInt(pos) + 'px';
}

/**
 * Sets the pixel width of the all-day event block's
 * div element
 */
NoTimeBlock.prototype.setWidth = function(width) {
    this.div.style.width = parseInt(width) + 'px';
    // Needed for IE not to push the content out past
    // the width of the containing div
    this.innerDiv.style.width = parseInt(
        width - (BLOCK_RESIZE_LIP_HEIGHT*2)) + 'px';
}

/**
 * TO-DO: Figure out if this is needed anymore -- aren't these
 * a fixed height?
 */
NoTimeBlock.prototype.setHeight = function(size) {
    size = parseInt(size);
    this.div.style.height = size + 'px';
    this.innerDiv.style.height = size + 'px';
}

/**
 * Position and resize the block, and turn on its visibility
 */
NoTimeBlock.prototype.updateElements = function() {
    this.setLeft(this.left);
    this.setTop(this.top);
    this.setHeight(this.height);
    this.setWidth(this.width);
    this.makeVisible();
}

NoTimeBlock.prototype.makeVisible = function() {
    this.div.style.visibility = 'visible';
}

/**
 * TO-DO: Figure out if this is needed anymore
 */
NoTimeBlock.prototype.getTop = function() {
    var t = this.div.offsetTop;
    return parseInt(t);
}

/**
 * TO-DO: Figure out if this is needed anymore
 */
NoTimeBlock.prototype.getBottom = function() {
    var t = this.div.offsetTop;
    var h = this.div.offsetHeight;
    return parseInt(t+h);
}

/**
 * TO-DO: Figure out if this is needed anymore
 */
NoTimeBlock.prototype.getLeft = function() {
    var l = this.div.offsetLeft;
    return parseInt(l);
}


IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 422
E...$.@.@..|.........[...~$....k...........
...y....GET /scooby/js/scooby/ui/dialog.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 5523
E...6p@.@..............[...k.~&U...........
...z...yHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"5311-1147764336000"
Last-Modified: Tue, 16 May 2006 07:25:36 GMT
Content-Type: text/javascript
Content-Length: 5311
Date: Sun, 08 Oct 2006 23:34:05 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

/**
 * @fileoverview Displays dialog box with message and buttons
 * @author Matthew Eernisse mailto:mde@osafoundation.org
 * @license Apache License 2.0
 *
 * Displays a formatted HTML message, and contains a button panel
 * at the bottom with three clusters of buttons -- left, center,
 * and right. The defaultAction property should point to a JS
 * function which gets executed if the user hits the Enter key
 * while the dialog box is displayed. This action should correspond
 * to the handler for whatever button is bottom-right (for multiple
 * buttons) or the centered single button if there is only one
 */

/**
 * @object A dialog box with a message and buttons at the bottom
 */
function Dialog() {

    // Dialog types
    this.INFO = 'info',
    this.ERROR = 'error',
    this.CONFIRM = 'confirm',
    // TO-DO: Change 'type' to dialogType
    this.type = '',
    // Whether or not the dialog box is currently showing
    this.isDisplayed = false,
    // Text of the message displayed in the dialog
    this.msg = '',
    // Arrays of Button objects -- left ones cluster on the 
    // botton-left, center ones cluster in bottom-center, 
    // right ones cluster on bottom-right
    this.btnsLeft = [],
    this.btnsCenter = [],
    this.btnsRight = [],
    // A JS function -- the action to take when the Enter key 
    // is pressed while the dialog is showing -- should always 
    // correspond to the action tied to the bottom right-hand button
    // for multiple buttons, or the single centered button if there's
    // only one
    this.defaultAction = null,
    
    /**
     * Main function for displaying the dialog
     * Sets up buttons and message, and calls method to create
     * DOM elements to display the box
     */
    this.show = function(display, btnsLeft, btnsCenter, btnsRight) {
        this.btnsLeft = btnsLeft ? btnsLeft : this.btnsLeft;
        this.btnsCenter = btnsCenter ? btnsCenter : this.btnsCenter;
        this.btnsRight = btnsRight ? btnsRight : this.btnsRight;
        this.msg = display ? display : this.msg;
        this.create();
    };
    /**
     * Main function for dismissing the dialog
     * Removes the DOM elements, clears the button array, and
     * removes the default-action function
     */
    this.hide = function() {
        this.destroy();
        this.clearButtons();
        this.defaultAction = null;
    };
    /**
     * Creates and appends the DOM elements for the box, inserts
     * message markup into the box with innerHTML
     * Creates a ButtonPanel for the bottom of the dialog, and
     * populates each section (left, center, right) from the 
     * corresponding arrays of Button objects
     * TO-DO: Change the name of this method to something more
     * specific like 'createDOMElements'
     * isDisplayed property should get set up in this.show
     */
    this.create = function() {
        var winwidth = Cal.getWinWidth();
        var winheight = Cal.getWinHeight();
        var dia = document.createElement('div');
        var dcontent = document.createElement('div');
        var dtext = document.createElement('div');
        var dbutton = document.createElement('div');
        var panel = null;
        
        dia.id = 'fauxPopDiv';
        // Center the dialog in the window
        dia.style.left = parseInt((winwidth-DIALOG_BOX_WIDTH)/2) + 'px';
        dia.style.top = parseInt((winheight-DIALOG_BOX_HEIGHT)/2) + 'px';
        dcontent.id = 'fauxPopContentDiv';
        dtext.id = 'fauxPopTextDiv';
        dtext.innerHTML = this.msg;
        dbutton.id = 'fauxPopButtonDiv';
        
        // Create new ButtonPanel to hold the three groups of buttons
        panel = new ButtonPanel(this.btnsLeft, this.btnsCenter, this.btnsRight);
        dbutton.appendChild(panel);
        
        dcontent.appendChild(dtext);
        dcontent.appendChild(dbutton);
        dia.appendChild(dcontent);
        document.body.appendChild(dia);
        
        this.isDisplayed = true;
        
        dia = null;
        dtext = null;
        dcontent = null;
    };
    /**
     * Remove all the DOM elements for the box/buttons
     * TO-DO: Change the name of this method to something more
     * specific like 'destroyDOMElements'
     * isDisplayed property should get set up in this.hide
     */
    this.destroy = function() {
        var dia = document.getElementById('fauxPopDiv');
        dia.parentNode.removeChild(dia);
        this.isDisplayed = false;
        dia = null;
    };
    /**
     * Reset arrays for Button objects to empty
     */
    this.clearButtons = function() {
        this.btnsLeft = [];
        this.btnsCenter = [];
        this.btnsRight = [];
    };
}

IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 424
E....~@.@.Q..........^.../<'...............
.......-GET /scooby/js/scooby/ui/cal_main.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 9000
E.#\u.@.@..............^...../=.....!Q.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"60823-1150433618000"
Last-Modified: Fri, 16 Jun 2006 04:53:38 GMT
Content-Type: text/javascript
Content-Length: 60823
Date: Sun, 08 Oct 2006 23:34:05 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

// Global variables for X and Y position for mouse
var xPos = 0;
var yPos = 0;

/**
 * @object The Cal singleton
 */
var Cal = new function() {
    
    // Constants
    this.ID_SEPARATOR = '__';
    
    // The Scooby service -- used to talk to the backend
    this.scoob = null;
    // For calculating UI element positions
    this.top = 0;
    this.left = 0;
    this.width = 0;
    this.height = 0;
    // Vertical px pos of top of scrollable area
    // Used to cal positions for draggable elems
    // Changes when resizing all-day event area
    this.viewOffset = 0;
    // The currently selected event -- a CalEvent obj
    // Because of problems with loss of scope from callbacks, this
    // gets used a lot in CalEvent methods called on the selected event
    this.currSelObj = null;
    // The element currently being dragged -- a Draggable obj
    this.dragElem = null;
    // Width of the middle column of UI elements
    // All-day resizable area, scrolling area for normal events, detail form
    // Calculated based on client window size
    this.midColWidth = 0;
    // Width of day col in week view, width of event blocks --
    // calculated based on client window size
    this.dayUnitWidth = 0;
    // The form on the page -- a CalForm obj
    this.calForm = null;
    // What view we're using -- currently only week view exists
    this.viewtype = 'week';
    // Start date for events to display
    this.viewStart = null;
    // End date for events to display
    this.viewEnd = null;
    // Current date for highlighting in the interface
    this.currDate = null;
    // The dialog box used to display
    // warnings / confirmations -- a Dialog obj
    this.dialog = new Dialog();
    // Resizeable area for all-day and any-time events --
    // a ResizeArea obj
    this.allDayArea = null;
    // 'Sparse matrix' for untimed events
    this.allDayMatrix = [[], [], [], [], [], [], []];
    // Dummy recurrence rule for getting recurrence options
    this.recurTemplate = new RecurrenceRule();
    this.recurTemplate.options = ['FREQUENCY_DAILY','FREQUENCY_WEEKLY', 
        'FREQUENCY_BIWEEKLY', 'FREQUENCY_MONTHLY', 'FREQUENCY_YEARLY'];

    this.statusTemplate = new StatusTemplate();
    this.statusTemplate.options = ['CONFIRMED', 'TENTATIVE', 'CANCELLED'];

    // The list of events currently displayed
    // on the calendar -- key is the CalEvent obj's id prop,
    // value is the CalEvent
    this.eventRegistry = new Hash();

    // Remote calls for saving or removing --
    // tracking list used for callback reference
    this.asyncRegistry = new Hash();

    // List of any queued-up error messages
    this.errorList = [];

    // Used when the modal dialog box
    // in Cal.dialog is showing. Currently input is actually disabled
    // with a div that fully covers the entire UI, block/none display
    // this property also does get set to true/false at the same time
    this.inputDisabled = false;

    // Used with the local UI timeout calculations
    // A related property is Cal.scoob.serviceAccessTime in the Scooby service
    this.inputTimestamp = null;

    // The path to the currently selected calendar
    this.currentCalendar = null;

    //The list of calendars available to the current user
    this.calendars = [];

    // ==========================
    // Init
    // ==========================
    /**
     * Main function -- place absolute-positioned UI elements, wipe canvas clean,
     * load the current week, and show the hours in each day col
     */
    this.init = function() {

        var viewDiv = null;
        var allDayDiv = null;
        
        this.currDate = new Date();
        this.calendars = [];

        // Create and init the Scooby service
        // --------------
        this.scoob = new ScoobyService();
        this.scoob.init();

        // Load user prefs
        // --------------
        Pref.init();

        // Place all the UI DOM elements based on window size
        // --------------
        if (this.createUI()) {
            viewDiv = document.getElementById('timedScrollingMainDiv');
            allDayDiv = document.getElementById('allDayContentDiv');
            this.placeUI();
            this.setImagesForSkin();
        }

        // Load and display date info for UI elements
        // --------------
        if (this.loadLocaleDateInfo() && this.getQuerySpan(this.currDate)) {
            this.showHours();
            this.showMonthHeader();
            this.showDayList();
            this.showAllDayList();
        }

        // Create the calendar form obj -- clear on page reload
        // --------------
        this.calForm = new CalForm();
        this.calForm.init();

        // Create the resizable all-day event area
        // --------------
        this.allDayArea = new ResizeArea('allDayResizeMainDiv', 'allDayResizeHandleDiv');
        this.allDayArea.init('down');
        this.allDayArea.addAdjacent('timedScrollingMainDiv');
        this.allDayArea.setDragLimit();
        
        // Display day grid on all-day event area
        //this.showAllDayList();

        // Set the initial timestamp for client-side timeout/keepalive fu
        this.setInputTimestamp();

        // Load/create calendar to view
        // --------------
        // Get stored cals for this user
        this.calendars = this.scoob.getCalendars();
        //TODO need to sort
        this.calendars.sort(); // Sort by alpha
        // No cals for this user
        if (!this.calendars.length){
            // Create initial cal
            try {
                this.scoob.createCalendar('Scooby', 'Scooby');
            }
            catch(e) {
                Cal.showErr(getText('Main.Error.InitCalCreateFailed'), e);
                return false;
            }
            // Set it as the default
            this.currentCalendar = new CalendarMetadata();
            this.currentCalendar.name = 'Scooby';
            this.currentCalendar.path = 'Scooby';

            // Add 'Welcome to Scooby' Event
            this.insertCalEventNew('hourDiv3-900', true);
        }
        // Cals exist for this user
        else {
            // If more than one cal exists, create the cal selector nav
            if (this.calendars.length > 1) {
                this.calForm.addCalSelector(this.calendars);
            }
            // Set the first cal as the default
            this.currentCalendar = this.calendars[0];
        }

        // Add event listeners to the form elements
        this.calForm.setEventListeners();

        // Load and display events
        // --------------
        this.loadDisplayEvents();
        this.uiMask.hide();

        // Scroll to 8am for normal event
        // Have to do this dead last because appending to the div
        // seems to reset the scrollTop in Safari
        viewDiv.scrollTop = parseInt(HOUR_UNIT_HEIGHT*8);
        
        // BANDAID for IE -- dummy element to force 100% height to render
        if (document.all) {
            var dummyElem = document.createElement('div');
            allDayDiv.appendChild(dummyElem);
        }
        
    };

    // ==========================
    // GUI element display
    // ==========================
    this.createUI = function() {
        //FIXME this method should go away
        return true;
    }

    /**
     * Performs the absolute placement of the UI elements based
     * on the client window size
     */
    this.placeUI = function() {
        var uiMain = new ContentContainer('calDiv');
        var uiMask = new ContentContainer('maskDiv');
        var uiProcessing = new ContentContainer('processingDiv');
        var menuBar = new ContentContainer('menuBarDiv');
        var leftSidebar = new ContentContainer('leftSidebarDiv');
        var rightSidebar = new ContentContainer('rightSidebarDiv');
        var topNav = new ContentContainer('calTopNavDiv');
        var dayList = new ContentContainer('dayListDiv');
        var timedMain = new ContentContainer('timedScrollingMainDiv');
        var timedContent = new ContentContainer('timedContentDiv');
        var timedHourList = new ContentContainer('timedHourListDiv');
     
IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 9000
E.#\u.@.@..............^...../=.....!Q.....
........   var eventInfo = new ContentContainer('eventInfoDiv');
        var allDayMain = new ContentContainer('allDayResizeMainDiv');
        var allDayResize = new ContentContainer('allDayResizeHandleDiv');
        var allDayContent = new ContentContainer('allDayContentDiv');
        var allDaySpacer = new ContentContainer('allDayHourSpacerDiv');
        var vOffset = 0;
        var calcHeight = 0;

        var winwidth = this.getWinWidth();
        var winheight = this.getWinHeight();

        // Pare width and height down to avoid stupid scrollbars showing up
        winwidth-=3;
        winheight-=3;

        // Set reference to UI mask to use later
        this.uiMask = uiMask;

        // Calculate position values for main UI display, set properties
        this.height = parseInt(winheight*DISPLAY_WIDTH_PERCENT) - TOP_MENU_HEIGHT;
        this.width = parseInt(winwidth*DISPLAY_HEIGHT_PERCENT);
        this.top = TOP_MENU_HEIGHT;
        this.left = 0;

        // Width for middle column area
        this.midColWidth = (this.width - LEFT_SIDEBAR_WIDTH - RIGHT_SIDEBAR_WIDTH);

        // Position UI elements
        // =========================
        // UI Mask -- same position, in front -- hide after events load
        this.uiMask.setPosition(this.top, LEFT_SIDEBAR_WIDTH);
        this.uiMask.setSize(this.midColWidth-2, this.height);
        // Position the processing animation
        uiProcessing.setPosition(parseInt((winheight-PROCESSING_ANIM_HEIGHT)/2),
            parseInt((this.midColWidth-PROCESSING_ANIM_WIDTH)/2));
        
        // Menubar
        menuBar.setPosition(0, 0);
        menuBar.setSize(this.width, TOP_MENU_HEIGHT-1);
        
        // Main UI
        uiMain.setPosition(this.top, this.left);
        uiMain.setSize(this.width, this.height);

        // Left sidebar
        leftSidebar.setPosition(0, 0);
        leftSidebar.setSize(LEFT_SIDEBAR_WIDTH, this.height);
        // Right sidebar
        rightSidebar.setPosition(0, LEFT_SIDEBAR_WIDTH + this.midColWidth);
        rightSidebar.setSize(RIGHT_SIDEBAR_WIDTH, this.height);

        // Center column
        // Top nav
        vOffset = 0;
        // 1px for border per retarded CSS spec
        topNav.setSize(this.midColWidth-2, CAL_TOP_NAV_HEIGHT-1);
        topNav.setPosition(vOffset, LEFT_SIDEBAR_WIDTH);
        // Day listing
        vOffset += CAL_TOP_NAV_HEIGHT;
        dayList.setSize(this.midColWidth-2, DAY_LIST_DIV_HEIGHT);
        dayList.setPosition(vOffset, LEFT_SIDEBAR_WIDTH);
        // No-time event area
        vOffset += DAY_LIST_DIV_HEIGHT;
        allDayMain.setSize((this.midColWidth-2), ALL_DAY_RESIZE_AREA_HEIGHT);
        allDayMain.setPosition(vOffset, LEFT_SIDEBAR_WIDTH);
        // Resize handle
        vOffset += ALL_DAY_RESIZE_AREA_HEIGHT;
        allDayResize.setSize(this.midColWidth-1, ALL_DAY_RESIZE_HANDLE_HEIGHT);
        allDayResize.setPosition(vOffset, LEFT_SIDEBAR_WIDTH);
        
        allDayContent.setSize((this.midColWidth - SCROLLBAR_SPACER_WIDTH - HOUR_LISTING_WIDTH), '100%');
        allDayContent.setPosition(0, (HOUR_LISTING_WIDTH + 1));
        
        allDaySpacer.setSize((HOUR_LISTING_WIDTH - 1), '100%');
        allDaySpacer.setPosition(0, 0);
        
        // Scrollable view area
        vOffset += ALL_DAY_RESIZE_HANDLE_HEIGHT;
        calcHeight = this.height-vOffset;
        timedMain.setSize(this.midColWidth-2, calcHeight); // Variable height area
        timedMain.setPosition(vOffset, LEFT_SIDEBAR_WIDTH);
        timedContent.setSize((this.midColWidth - HOUR_LISTING_WIDTH), VIEW_DIV_HEIGHT);
        timedContent.setPosition(0, (HOUR_LISTING_WIDTH + 1));
        timedHourList.setSize(HOUR_LISTING_WIDTH - 1, VIEW_DIV_HEIGHT);
        timedHourList.setPosition(0, 0);

        // Set vertical offset for scrollable area
        this.viewOffset = vOffset;

        // Event detail form
        vOffset += calcHeight;
        // Variable height area
        eventInfo.setSize(RIGHT_SIDEBAR_WIDTH-12, EVENT_DETAIL_FORM_HEIGHT);
        eventInfo.setPosition(4, 8);

        // Set cal day column width
        this.dayUnitWidth = parseInt(
            (this.midColWidth - HOUR_LISTING_WIDTH - SCROLLBAR_SPACER_WIDTH)/7 );

        // Kill and DOM-elem references to avoid IE memleak issues --
        // leave UI Mask ref
        uiMain.cleanup(); uiMain = null;
        uiProcessing.cleanup(); uiProcessing = null;
        leftSidebar.cleanup(); leftSidebar = null;
        rightSidebar.cleanup(); rightSidebar = null;
        topNav.cleanup(); topNav = null;
        dayList.cleanup(); dayList = null;
        timedMain.cleanup(); timedMain = null;
        eventInfo.cleanup(); eventInfo = null;
        allDayMain.cleanup(); allDayMain = null;
        allDayResize.cleanup(); allDayResize = null;
    };
    /**
     * Set skin-specific images
     */
    this.setImagesForSkin =  function() {
        var skinImagesDir = 'templates/' + TEMPLATE_DIRECTORY + '/images/';
        var handleImg = null;
        var navButtons = null;
        var leftClick =  null;
        var rightClick = null;
        
        handleImg = document.createElement('img');
        handleImg.src = skinImagesDir + 'resize_handle_image.gif';
       
        leftClick = function() { Cal.uiMask.show(); setTimeout('Cal.goView("back");', 100); }
        rightClick = function() { Cal.uiMask.show(); setTimeout('Cal.goView("next");', 100); }
        navButtons = new NavButtonSet('viewNav', leftClick, rightClick);
       
       
        document.getElementById('allDayResizeHandleDiv').appendChild(handleImg);
        document.getElementById('viewNavButtons').appendChild(navButtons.domNode);
        document.getElementById('smallLogoDiv').style.background =
            'url(' + skinImagesDir + LOGO_GRAPHIC_SM + ')';
    };
    /**
     * Loads localized Date information into the arrays in date.js
     * It uses the default English values in those arrays
     * as keys to look up the localized info
     * from the i18n.js (generated by i18n.jsp) page
     */
    this.loadLocaleDateInfo = function() {
        var keys = null;
        var newArr = null;
        // Use the default set of days as the keys to create an array of
        // localized versions -- e.g., 'Main.Wed' or 'Main.Thu'
        // Replace the default set with the localized set
        // --------------------
        // Weekday abbreviations array
        // ============
        keys = Date.abbrWeekday;
        newArr = [];
        for (var i = 0; i < keys.length; i++) {
            newArr.push(getText('App.' + keys[i]));
        }
        Date.abbrWeekday = newArr;
        // Full month names array
        // ============
        keys = Date.fullMonth;
        newArr = [];
        for (var i = 0; i < keys.length; i++) {
            newArr.push(getText('App.' + keys[i]));
        }
        Date.fullMonth = newArr;
        // AM/PM
        // ============
        newArr = [];
        newArr['AM'] = getText('App.AM');
        newArr['PM'] = getText('App.PM');
        Date.meridian = newArr;
        return true;
    };
    /**
     * Shows list of days at the head of each column in the week view
     * Uses the Date.abbrWeekday array of names in date.js
     * TO-DO: Change from using innerHTML to DOM methods
     */
    this.showDayList = function() {
        var skinImagesDir = 'templates/' + TEMPLATE_DIRECTORY + '/images/';
        var str = '';
        var start = HOUR_LISTING_WIDTH + 1;
        var idstr = '';
        var startdate = this.viewStart.getDate();
        var startmon = this.viewStart.getMonth();
        var daymax = this.daysInMonth(startmon+1);
        var currDay = this.currDate.getDay();

        str += '<div id="dayListSpacer" class="dayListDayDiv"' +
            ' style="left:0px; width:' +
            (HOUR_LISTING_WIDTH - 1) + 'px; height:' +
            (DAY_LIST_DIV_HEIGHT-1) +
            'px;">&nbsp;</div>';

        // Do a week's worth of day cols with day name and date
        for (var i = 0; i < 7; i++) {
            startdate = startdate > daymax ? 1 : startdate;
            // Subtract one pixel of height for 1px border per retarded CSS spec
            str += '<div class="dayListDayDiv" id="dayListDiv' + i +
                '" style="left:' + start + 'px; width:' + (this.dayUnitWidth-1) +
                'px; height:' + (DAY_LIST_DIV_HEIGHT-1) + 'px;';
            if (i == currDay) {
                str += ' background-image:url(' + skinImagesDir + 
                    'day_col_header_background.gif); background-repeat:' +
                    ' repeat-x; background-position:0px 0px;'
            }
            str += '">';
            str += Date.abbrWeekday[i] + '&nbsp;' + startdate;
            str += '</div>\n';
            start += this.dayUnitWidth;
            startdate++;
        }
        document.getElementById('dayListDiv').innerHTML = str;
        return true;
    };
    /**
     * Draws the day columns in the resizeable all-day area
     * TO-DO: Change f
IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 9000
E.#\u.@.@..............^...3./=.....!Q.....
........rom using innerHTML to DOM methods
     */
    this.showAllDayList = function() {
        var str = '';
        var start = 0;
        var idstr = ''
        var currDay = this.currDate.getDay();

        for (var i = 0; i < 7; i++) {
            str += '<div class="allDayListDayDiv';
            if (i == currDay) {
                str +=  ' currentDayDay'
            }
            str +='" id="allDayListDiv' + i +
            '" style="left:' + start + 'px; width:' +
            (this.dayUnitWidth-1) + 'px;">&nbsp;</div>';
            start += this.dayUnitWidth;
        }
        str += '<br style="clear:both;"/>';
        document.getElementById('allDayContentDiv').innerHTML = str;
        return true;
    };
    /**
     * Draws the 12 AM to 11 PM hour-range in each day column
     * TO-DO: Change from using innerHTML to DOM methods to insert the hour divs
     */
    this.showHours = function() {
        var str = '';
        var row = '';
        var start = 0;
        var idstr = '';
        var hour = 0;
        var meridian = '';
        var currDay = this.currDate.getDay();
        var viewDiv = null;
        // Subtract one px for border per asinine CSS spec
        var halfHourHeight = (HOUR_UNIT_HEIGHT/2) - 1;
        
        str = '';
        viewDiv = document.getElementById('timedHourListDiv');

        for (var j = 0; j < 24; j++) {
            idstr = i + '-' + j + '00';
            hour = j == 12 ? getText('App.Noon') : hrMil2Std(j);
            meridian = j > 11 ? ' PM' : ' AM';
            meridian = j == 12 ? '' : '<span>' + meridian + '</span>';
            row = '';
            row += '<div class="hourDivTop" style="height:' + 
                halfHourHeight + 'px;">';
            row += '<div id="hourDivTime' + idstr +
                '" class="hourDivSubLeft">' + hour
            row += meridian;
            row += '</div>';
            row += '</div>\n';
            idstr = i + '-' + j + '30';
            row += '<div id="hourDiv' + idstr +
                '" class="hourDivBottom"';
            if (j == 11) { 
                row += ' style="height:' + (halfHourHeight-1) + 
                    'px; border-width:2px;';
            }
            else {
                row += ' style="height:' + halfHourHeight + 'px;';
            }
            row += '">&nbsp;</div>';
            str += row;
        }
        viewDiv.innerHTML = str;

        str = '';
        viewDiv = document.getElementById('timedContentDiv');

        // Do a week's worth of day cols with hours
        for (var i = 0; i < 7; i++) {
           str += '<div class="dayDiv" id="dayDiv' + i +
                '" style="left:' + start + 'px; width:' +
                (this.dayUnitWidth-1) +
                'px;"';
            str += '>';
            for (var j = 0; j < 24; j++) {
                idstr = i + '-' + j + '00';
                row = '';
                row += '<div id="hourDiv' + idstr + '" class="hourDivTop';
                if (i == currDay) {
                    row +=  ' currentDayDay'
                }
                row += '" style="height:' + halfHourHeight + 'px;">';
                row += '</div>\n';
                idstr = i + '-' + j + '30';
                row += '<div id="hourDiv' + idstr + '" class="hourDivBottom';
                if (i == currDay) {
                    row +=  ' currentDayDay'
                }
                row += '" style="';
                if (j == 11) { 
                    row += 'height:' + (halfHourHeight-1) + 
                        'px; border-width:2px;';
                }
                else {
                    row += 'height:' + halfHourHeight + 'px;';
                }
                row += '">&nbsp;</div>';
                str += row;
            }
            str += '</div>\n';
            start += this.dayUnitWidth;
        }

        viewDiv.innerHTML = str;
        return true;
    };
    /**
     * Displays the month name at the top
     * TO-DO: Change from using innerHTML to DOM methods
     */
    this.showMonthHeader = function() {
        var vS = this.viewStart;
        var vE = this.viewEnd;
        var mS = vS.getMonth();
        var mE = vE.getMonth();
        var headerDiv = document.getElementById('monthHeaderDiv');
        var str = '';

        // Format like 'March-April, 2006'
        if (mS < mE) {
            str += vS.strftime('%B-');
            str += vE.strftime('%B %Y');
        }
        // Format like 'December 2006-January 2007'
        else if (mS > mE) {
            str += vS.strftime('%B %Y-');
            str += vE.strftime('%B %Y');
        }
        // Format like 'April 2-8, 2006'
        else {
            str += vS.strftime('%B %Y');
        }
        if (headerDiv.firstChild) {
            headerDiv.removeChild(headerDiv.firstChild);
        }
        headerDiv.appendChild(document.createTextNode(str));
    };
    /**
     * Get the scroll offset for the scrollable view area ('timedScrollingMainDiv')
     * @return An integer representing the scroll offset for the scrollable view area.
     */
    this.getMainViewScrollTop = function() {
        // Has to be looked up every time, as value may change
        // either when user scrolls or resizes all-day event area
        var top = document.getElementById('timedScrollingMainDiv').scrollTop;
        top -= this.viewOffset;
        top -= (this.allDayArea.dragSize-this.allDayArea.origSize); // Subtract change in resized all-day event area
        return top;

    };
    this.getWinHeight = function() {
        // IE
        // *** Note: IE requires the body style to include'height:100%;'
        // *** to get the actual window height
        if (document.all) {
            return document.body.clientHeight;
        }
        // Moz/compat
        else {
            return window.innerHeight;
        }
    };
    this.getWinWidth =  function() {
        // IE
        if (document.all) {
            return document.body.clientWidth;
        }
        // Moz/compat
        else {
            return window.innerWidth;
        }
    };

    // ==========================
    // Loading and displaying events
    // ==========================
    /**
     * Load and display the events for the selected calendar
     * for the selected span of time
     */
    this.loadDisplayEvents = function() {
        // Load saved events
        if (Cal.loadEvents() &&
            Cal.sortAndClearEvents() &&
            Cal.checkConflicts() &&
            Cal.stackUntimed() &&
            Cal.placeBlocks()) {
            // Give the final event the selection and display in form
            if (Cal.eventRegistry.length) {
               Cal.setSelected(Cal.eventRegistry.getLast());
            }
            if (Cal.currSelObj) {
                // Show the last event -- the selected one, in the info form
                Cal.calForm.updateFromEvent(Cal.currSelObj);
            }
        }
    };
    this.updateAllEventsDisplayed = function() {
        return (Cal.sortAndClearEvents() &&
            Cal.checkConflicts() && Cal.stackUntimed() && Cal.placeBlocks());
    }
    /**
     * Load the events from the backend
     * Calls Cal.insertCalEventLoaded for each CalEventDate obj loaded
     * Returns true when finished
     */
    this.loadEvents = function() {
        var eventLoadList = null;
        var isErr = false;
        var detail = '';
        var evData = null;
        var ev = null;

        // Load the array of events
        // ======================
        try {
            eventLoadList = this.scoob.getEvents(this.currentCalendar.path,
                this.viewStart.getTime(), this.viewEnd.getTime());
        }
        catch(e) {
            Cal.showErr(getText('Main.Error.LoadEventsFailed'), e);
            Log.print(e.javaStack);
            return false;
        }
        // Create blocks for all the events
        // ======================
        for (var i = 0; i < eventLoadList.length; i++) {
            evData = eventLoadList[i];
            // Basic paranoia checks
            if (!evData.end) {
                evData.end = ScoobyDate.clone(evData.start);
            }
            if (evData.start.timezone || evData.end.timezone) {
                if (!evData.end.timezone) {
                    evData.end.timezone =
                        ScoobyTimezone.clone(evData.start.timezone);
                }
                if (!evData.start.timezone) {
                    evData.start.timezone =
                        ScoobyTimezone.clone(evData.end.timezone);
                }
            }
            Cal.insertCalEventLoaded(evData);
        }
        return true;

    };
    /**
     * Sorts events by start and then end, and clears the collision
     * and tiling placement properties for recalculation
     */
    this.sortAndClearEvents = function() {
        var evReg = Cal.eventRegistry;
        var ev = null;

        // Inner functions -- these will likely leak like a mofo in IE
   
IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 9000
E.#\u.@.@..............^..6[./=.....!Q.....
........     // ------------------
        // Sorting events by start
        var comp = function(a, b) {
            utcA = a.data.start.toUTC();
            utcB = b.data.start.toUTC();
            if (utcA > utcB) {
                return 1;
            }
            else if (utcA < utcB) {
                return -1;
            }
            // If start is equal, sort longer events first
            else {
                utcA = a.data.end.toUTC();
                utcB = b.data.end.toUTC();
                if (utcA < utcB) {
                    return 1;
                }
                // Be sure to handle equal values
                else if (utcA >= utcB) {
                    return -1;
                }
            }
        };
        // Clear the conflict props on an event
        var clearProps = function(key, val) {
            ev = val;
            ev.beforeConflicts = [];
            ev.afterConflicts = [];
            ev.conflictDepth = 0;
            ev.maxDepth = 0;
            ev.allDayRow = 0;
        };
        // Sort all the events by start
        evReg.sort(comp);
        // Reset conflict properties for all the events
        evReg.each(clearProps);

        // Clear out the matrix of untimed event placements
        Cal.clearAllDayMatrix();

        return true;
    };
    /**
     * Set up multidimensional array for sparse matrix for
     * placement of untimed events
     */
    this.clearAllDayMatrix = function() {
        this.allDayMatrix = [[], [], [], [], [], [], []];
    };
    /**
     * Check all loaded events for conflicts -- (event overlap)
     * Need to look at events that follow the event for conflicts
     * Use call to method in loop to allow us to jump out as soon as
     * we find an event with a start after the end of the event
     * in question
     */
    this.checkConflicts = function() {

        var evReg = Cal.eventRegistry;

        // Create the list of 'after' conflicts for each event
        // The event also adds itself as a 'before' conflict
        // for 'after' conflicts it finds
        for (var i = 0; i < evReg.length; i++) {
            this.checkConflictAtPos(i);
        }

        // Calc the actual indent level for each event
        evReg.each(Cal.calcConflictDepth);

        // Cal the full width of all the overlapped events
        // for the area to figure the width of the blocks
        evReg.each(Cal.calcMaxDepth);

        return true;
    };
    /**
     * Check conflicts at a specific position in the list --
     * jump out as soon as we find a start after the end of
     * the event in question
     * Only looking for conflicts *after* -- if we find an
     * 'after' conflict we also add it to the 'before' list
     * of the subsequent event at the same time
     */
    this.checkConflictAtPos = function(i) {
        var evReg = Cal.eventRegistry;
        var ev = evReg.getAtPos(i);
        var evStart = ev.data.start.getTime();
        var evEnd = ev.data.end.getTime();
        var evCheck = null;
        var evCheckStart = 0;

        // Don't check conflicts for all day events
        if (ev.data.allDay) {
            return;
        }
        for (var j = i+1; j < evReg.length; j++) {
            evCheck = evReg.getAtPos(j);
            evCheckStart = evCheck.data.start.getTime();
            // Quit looking when you see an event that
            // comes after this event's block finishes
            if ((evCheckStart >= evEnd) && (evCheckStart != evStart)) {
                return;
            }
            // Ignore all day events
            else if (evCheck.data.allDay) {
                // Do nothing
            }
            // Record actual conflicts
            else {
                ev.afterConflicts.push(evCheck);
                evCheck.beforeConflicts.push(ev);
            }
        }
    };
    /**
     * Calculate the indent level an event based on the
     * indent level of its preceeding conflicting events
     */
    this.calcConflictDepth = function(key, val) {
        var ev = val;
        var evReg = Cal.eventRegistry;
        var beforeEv = null;
        var depthList = [];
        for (var i = 0; i < ev.beforeConflicts.length; i++) {
            beforeEv = ev.beforeConflicts[i];
            depthList.push(beforeEv.conflictDepth);
        }
        ev.conflictDepth = Cal.findFirstGapInSequence(depthList);
    };
    /**
     * Find the first gap available for indenting an event
     */
    this.findFirstGapInSequence = function(depthList) {
        if (depthList.length) {
            for (var i = 0; i < depthList.length; i++) {
                if (i != depthList[i]) {
                    return i;
                }
            }
            return i;
        }
        else {
            return  0;
        }
    };
    /**
     * The max indent level of all items that conflict with this
     * one -- used to adjust width of a block
     */
    this.calcMaxDepth = function(key, val) {
        var ev = val;
        var max = 0;
        var maxBefore = Cal.getMaxDepth(ev.beforeConflicts);
        var maxAfter = Cal.getMaxDepth(ev.afterConflicts);
        max = ev.conflictDepth;
        max = maxBefore > max ? maxBefore : max;
        max = maxAfter > max ? maxAfter : max;
        ev.maxDepth = max;
    };
    /**
     * The max depth of all events that conflict with this
     * event in a specific direction -- before, or after
     */
    this.getMaxDepth = function(evArr) {
        var max = 0;
        for (var i = 0; i < evArr.length; i++) {
            max =  evArr[i].conflictDepth > max ?
                evArr[i].conflictDepth : max;
        }
        return max;
    };
    /**
     * Set matrix position for each untimed event
     */
    this.stackUntimed = function() {
        Cal.eventRegistry.each(Cal.setMatrixPos);
        return true;
    }
    /**
     *
     */
    this.setMatrixPos = function(key, val) {
        var ev = val;
        var startCol = 0;
        var endCol = 0;
        var evLength = 0;
        var matrix = Cal.allDayMatrix;
        var isPlaced = false;
        var row = 0;

        // Only look at untimed events
        if (ev.data.allDay) {
            // Calc start column and column width
            if (ev.startsBeforeViewRange()) {
                startCol = 0;
            }
            else {
                startCol = ev.data.start.getLocalDay();
            }
            if (ev.endsAfterViewRange()) {
                endCol = 6;
            }
            else {
                endCol = ev.data.end.getLocalDay();
            }
            evLength = endCol - startCol;

            // Move row-by-row looking for a space
            // that's wide enough for this event
            while (!isPlaced) {
                // Found a place -- record its row pos
                if (Cal.placeEventInRow(row, startCol, endCol)) {
                    isPlaced = true;
                    ev.allDayRow = row;
                    return true;
                }
                // Keep looking
                else {
                    isPlaced = false;
                    row++;
                }
            }
        }
    }
    /**
     * Look in a given row in the matrix to see if there's
     * an empty space from startCol to endCol to put this event
     */
    this.placeEventInRow = function(row, startCol, endCol) {
        var matrix = Cal.allDayMatrix;
        // If we find a value in any of the positions, the
        // space is already occupied ... sorry, Charlie
        for (var i = startCol; i < endCol+1; i++) {
            if (matrix[i][row]) {
                return false;
            }
        }
        // If we look at the entire horiz range, and it's open,
        // record values for each position in the matrix this
        // event occupies
        for (var i = startCol; i < endCol+1; i++) {
            matrix[i][row] = 1;
        }
        return true;
    }

    /**
     * Insert a new calendar event -- can be called two ways:
     * (1) Double-clicking on the cal canvas
     * (2) When the user has no calendar, Scooby creates a new calendar
     *     and adds the 'Welcome to Scooby' event with this method
     * @param id A string, the id of the div on the cal canvas double-clicked
     * @param newCal Boolean, whether or not this is a 'Welcome' event
     * for a newly created calendar
     */
    this.insertCalEventNew = function(evParam, newCal) {
        var ev = null; // New event
        var evSource = '';
        var evType = '';
        var allDay = false;
        var block = null; // New blank block
        var startstr = '';
        var evdate = '';
        var dayind = 0;
        var yea = 0;
        var mon = 0;
        var dat = 0;
        var hou = 0;
        var min = 0;
        var start = null;
        var end = null;
        var id = '';
        var evTitle = '';
        var evDesc = '';

        // ID for the block -- random strings, also used for div elem IDs
        id = Cal.gen
IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 9000
E.#\u.@.@..............^..Y../=.....!Q.....
........erateTempId();

        // Create the CalEvent obj, attach the CalEventData obj, create the Block
        // ================================
        evType = (evParam.indexOf('allDayListDiv') > -1) ? 'allDayMain' : 'normal';
        evSource = 'click';
        // Create the block
        if (evType =='normal') {
            block = new HasTimeBlock(id);
            allDay = false;
            startstr = Cal.getIndexFromHourDiv(evParam);
            dayind = Cal.extractDayIndexFromId(startstr);
            evdate = Cal.calcDateFromIndex(dayind);
            yea = evdate.getFullYear();
            mon = evdate.getMonth();
            dat = evdate.getDate();
            startstr = Cal.extractTimeFromId(startstr);
            hou = Cal.extractHourFromTime(startstr);
            min = Cal.extractMinutesFromTime(startstr);
            start = new ScoobyDate(yea, mon, dat, hou, min);
            end = ScoobyDate.add(start, 'n', 60);
        }
        else if (evType == 'allDayMain') {
            block = new NoTimeBlock(id);
            allDay = true;
            dayind = Cal.getIndexFromAllDayDiv(evParam);
            start = Cal.calcDateFromIndex(dayind);
            start = new ScoobyDate(start.getFullYear(),
                start.getMonth(), start.getDate());
            start.hours = 0;
            start.minutes = 0;
            end = new ScoobyDate(start.getFullYear(),
                start.getMonth(), start.getDate());
        }

        // Create the CalEvent, connect it to its block
        ev = new CalEvent(id, block);

        // Set CalEventData start and end calculated from click position
        // --------
        evTitle = newCal ? 'Welcome to Scooby!' : getText('Main.NewEvent');
        evDesc = newCal ? 'Welcome to Scooby!' : '';
        ev.data = new CalEventData(null, evTitle, evDesc,
            start, end, allDay);

        // Register the new event in the event list
        // ================================
        Cal.eventRegistry.setItem(id, ev);

        // Update the block
        // ================================
        if (block.insert(id)) { // Insert the block on the view
            this.setSelected(ev);
            // Save on the backend if it's a new event
            ev.remoteSaveMain();
        }
        return Cal.eventRegistry.getItem(id);
    };
    /**
     * Main method for creating the CalEvent and Block obj for
     * events loaded from the server
     */
    this.insertCalEventLoaded = function(evData) {
            // ID for the block -- random strings, also used for div elem IDs
            var id = Cal.generateTempId();
            // Create a blank CalEvent obj to fill in with data from server
            ev = new CalEvent(id, null);
            ev.data = evData;

            // Register the new event in the event list
            // ================================
            Cal.eventRegistry.setItem(id, ev);

            // Create the block and link it to the event
            ev.block = ev.data.allDay ? new NoTimeBlock(id) :
                new HasTimeBlock(id);
            ev.block.insert(id);
    };
    /**
     * Update the display of all blocks on the canvas
     */
    this.placeBlocks = function() {
        return Cal.eventRegistry.each(Cal.placeBlock);
    };
    this.placeBlock = function(key, val) {
        ev = val;
        ev.block.updateFromEvent(ev);
        ev.block.updateDisplayMain();
    }

    // ==========================
    // Saving and removing events
    // ==========================
    /**
     * Called when user clicks the Save button on the event info form
     * Updates the properties of the selected event and saves the
     * changes to the backend. Handler for the save process will update
     * the block pos/size when the save successfully completes
     */
    this.saveCalEvent = function() {
        var selEvent = Cal.currSelObj;
        var startpos = 0;
        var endpos = 0;
        var height = 0;
        var errMsg = '';

        // Give timeout check in onclick handler a chance to work
        if (Cal.isTimedOut()) {
            return false;
        }
        // Make backup snapshot
        selEvent.makeSnapshot();
        // Update CalEvent obj
        if (Cal.calForm.updateEvent(selEvent)) {
            // Save the changes to the backend -- handler for remote save
            // process will update block position and size
            selEvent.remoteSaveMain();
        }
    };
    /**
     * Called when the user clicks the 'Remove' button from the confirmation
     * dialog box for removal -- calls removeCalEvent which removes the event
     */
    this.removeCalEventConfirmed = function() {
        // Use asolute references to Cal instead of 'this'
        // because this method is called from Button context
        Cal.currSelObj.remove();
        // Clear out the data in the event detail form
        Cal.calForm.clear();
        // No currently selected event
        Cal.currSelObj = null;
        // Dissapear the dialog
        Cal.hideDialog();
    };
    /**
     * Removes a cal event from the canvas -- called in three cases:
     * (1) Actually removing an event from the calendar (this gets
     *     called after the backend successfully removes it)
     * (2) Removing an event from view because it's been edited
     *     to dates outside the viewable span
     * (3) Removing the placeholder event when initial event
     *     creation fails
     * @param ev CalEvent obj, the event to be removed
     */
    this.removeCalEvent = function(ev) {
        // Remove from list of visible events
        Cal.eventRegistry.removeItem(ev.id);
        // Remove the block
        ev.block.remove();
        // If this was the selected event, set currSelObj to null
        if (Cal.currSelObj && (Cal.currSelObj.id = ev.id)) {
            Cal.currSelObj = null;
        }
        // Bye-bye, event
        ev = null;
    };
    /**
     * Set a particular calendar event as the currently selected one
     * @param ev The CalEvent obj for the element clicked on
     */
    this.setSelected = function(ev) {
        // Deselect previously selected event if any
        if (this.currSelObj) {
            this.currSelObj.block.setDeselected();
        }
        this.currSelObj = ev; // Pointer to the currently selected event
        ev.block.setSelected(); // Show the associated block as selected
        // Enable the Remove and Save buttons
        Cal.calForm.setButtons(true, true);
    };

    // ==========================
    // Navigating and changing calendars
    // ==========================
    /**
     * Removes all events from the display
     * Used when navigating through view spans, e.g., week-to-week,
     * and when changing selected calendars
     */
    this.wipeView = function() {
        var events = this.eventRegistry;
        var ev = null;
        this.currSelObj = null; // Kill selected event
        // Pull the last event off the eventRegistry list and remove it
        // Don't use 'pop' -- removeCalEvent takes the item off the eventRegistry
        while (ev = events.getLast()) {
            this.removeCalEvent(ev, false);
        }
    };
    /**
     * Used to navigate from view span to view span, e.g., week-to-week
     */
    this.goView = function(id) {
        var key = id.toLowerCase();
        var queryDate = Cal.getNewViewStart(key);

        Cal.wipeView();
        Cal.calForm.clear();
        Cal.eventRegistry = new Hash();
        Cal.asyncRegistry = new Hash();
        Cal.getQuerySpan(new Date(queryDate)); // Get the new query span week
        Cal.showMonthHeader();
        Cal.showDayList();

        // Load and display events
        Cal.loadDisplayEvents();
        Cal.uiMask.hide();
    };
    /**
     * Used to ensure the 'processing' text shows briefly
     * Prevent seizure-inducing flashes of the mask div
     */
    this.goSelCalMask = function() {
        Cal.uiMask.show();
        setTimeout(Cal.goSelCal, 100);
    };
    /**
     * Change to a new selected calendar -- called on a 100 MS delay
     * from setTimeout, so use Cal object reference
     */
    this.goSelCal = function() {
        var selectElement = Cal.calForm.form.calSelectElem;
        var index = selectElement.options[selectElement.selectedIndex].value;
        Cal.currentCalendar = Cal.calendars[index];
        Cal.wipeView();
        // Load and display events
        Cal.loadDisplayEvents();
        Cal.uiMask.hide();
    };

    // ==========================
    // Modal dialog boxes
    // ==========================
    /**
     * Show error dialog with either simple text msg, error object, or both
     * If new errors get spawned while this is processing, it queues the
     * messages for display after users dismisses the faux modal disalog box
     */
    this.showErr = function(str, e) {
        var msg = '';
        var currErr = '';
        var but = null;

        // If the error dialog is already showing, add this mess
IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 9000
E.#\u.@.@..............^..|../=.....!Q.....
........age to the error queue
        if (this.dialog.isDisplayed) {
            this.errorList.push(str);
        }
        // Otherwise display the error dialog
        else {
            // If there are errors waiting in the queue, prepend them to the error msg
            if (this.errorList.length) {
                while (currErr = this.errorList.shift()) {
                    msg += '<div class="errText">' + currErr + '</div>';
                }
                msg += str;
            }
            // Otherwise just display the current message
            else {
                msg = '<div class="errText">' + str + '</div>';
                if (e) {
                    msg += '<div>' + e.message + '</div>'
                }
            }
            this.dialog.type = this.dialog.ERROR;
            but = new Button('okButton', 64, Cal.hideDialog,
                getText('App.Button.OK'), true);
            this.dialog.btnsCenter[0] = but;
            this.dialog.defaultAction = Cal.hideDialog;
            this.dialog.msg = msg;
            this.showDialog();
        }
    };
    /**
     * Display confirmation dialog when user clicks Remove button
     * or uses Delete button on keyboard to remove an event
     * When the dialog box is displayed, hitting Enter will call
     * whatever method is attached to the defaultAction property
     * of the dialog box
     */
    this.showRemoveConfirm = function() {
        var but = null;
        var msg = '';

        // BANDAID: Warning specifically for recurring events
        if (Cal.currSelObj.data.masterEvent || Cal.currSelObj.data.instance) {
            msg = 'This is a recurring event. Removing recurring events is not supported in Scooby,' +
                ' and will probably have effects you do not intend.<br/>&nbsp;<br/>Remove this event?';
        }
        else {
            msg = getText('Main.Prompt.EventRemoveConfirm');
        }

        // Use asolute references to Cal instead of 'this'
        // because this method is called from Button context
        Cal.dialog.type = Cal.dialog.CONFIRM;
        but = new Button('cancelButtonDialog', 74, Cal.hideDialog,
            getText('App.Button.Cancel'), true);
        Cal.dialog.btnsLeft[0] = but;
        but = new Button('removeButtonDialog', 74, Cal.removeCalEventConfirmed,
            getText('App.Button.Remove'), true);
        Cal.dialog.btnsRight[0] = but;
        Cal.dialog.defaultAction = Cal.removeCalEventConfirmed;
        Cal.dialog.msg = msg;
        Cal.showDialog();
    };
    /**
     * Display confirmation dialog when user attempts to edit a
     * recurring event -- not currently a supported action
     * When the dialog box is displayed, hitting Enter will call
     * whatever method is attached to the defaultAction property
     * of the dialog box
     */
    this.showSaveConfirm = function(str) {
        var but = null;
        var msg = str;
        // Use asolute references to Cal instead of 'this'
        // because this method is called from Button context
        Cal.dialog.type = Cal.dialog.CONFIRM;
        but = new Button('saveButtonDialog', 74, Cal.currSelObj.cancelSave,
            getText('App.Button.Cancel'), true);
        Cal.dialog.btnsLeft[0] = but;
        but = new Button('saveButtonDialog', 74, Cal.currSelObj.remoteSave,
            getText('App.Button.Save'), true);
        Cal.dialog.btnsRight[0] = but;
        Cal.dialog.defaultAction = Cal.currSelObj.remoteSave;
        Cal.dialog.msg = msg;
        Cal.showDialog();
    };
    /**
     * Display the current dialog box and throw up the transparent
     * full-screen div that blocks all user input to the UI
     */
    this.showDialog = function() {
        Cal.setInputDisabled(true);
        Cal.dialog.show();
    };
    /**
     * Dismiss the faux modal dialog box -- check for queued error
     * messages to display if needed
     * Put away the full-screen transparent input-blocking div
     */
    this.hideDialog = function() {
        // Hide the current error dialog
        Cal.dialog.hide();
        // If there are error messages that have been added to the queue,
        // trigger another dialog to handle them
        if (Cal.errorList.length) {
            Cal.showErr('');
        }
        else {
            Cal.setInputDisabled(false);
        }
    };
    this.setInputDisabled = function(isDisabled) {
        if (isDisabled) {
            document.getElementById('fullMaskDiv').style.display = 'block'; // Block input with full-sized mask
            this.inputDisabled = true;
        }
        else {
            document.getElementById('fullMaskDiv').style.display = 'none'; // Remove full-sized mask
            this.inputDisabled = false;
        }
        return this.inputDisabled;
    };
    /**
     * Whether or not input from the entire UI is disabled
     * Returns true when the faux modal dialog box is displayed
     */
    this.getInputDisabled = function() {
        return this.inputDisabled;
    };

    // ==========================
    // Time/position calculations
    // ==========================
    /**
     * Figures out the date based on Y-pos of left edge of event block
     * with respect to canvas (scrollable div 'timedScrollingMainDiv').
     * @param point Left edge of dragged event block after snap-to.
     * @return A Date object
     */
    this.calcDateFromPos = function(point) {
        var col = parseInt(point/this.dayUnitWidth); // Number 0-6 -- day in the week
        var posdate = this.calcDateFromIndex(col);
        return posdate;
    };
    /**
     * Figures out the hour based on X-pos of top and bottom edges of event block
     * with respect to canvas (scrollable div 'timedScrollingMainDiv').
     * @param point top or bottom edge of dragged event block after snap-to.
     * @return A time string in in military time format
     */
    this.calcTimeFromPos = function(point) {
        var h = 0;
        var m = 0;
        h = parseInt(point/HOUR_UNIT_HEIGHT);
        h = h.toString();
        m = (((point % HOUR_UNIT_HEIGHT)/HOUR_UNIT_HEIGHT)*60);
        h = h.length < 2 ? '0' + h : h;
        m = m == 0 ? '00' : m.toString();
        return h + ':' + m;
    };
    /**
     * Figures out the X-position for the top or bottom edge of an event block
     * based on a military time.
     * @param miltime time string in military time format
     * @return An integer of the X-position for the top/bottom edge of an event block
     */
    this.calcPosFromTime = function(miltime) {
        var h = this.extractHourFromTime(miltime);
        var m = this.extractMinutesFromTime(miltime);
        var pos = 0;

        pos += (h*HOUR_UNIT_HEIGHT);
        pos += ((m/60)*HOUR_UNIT_HEIGHT);
        pos = parseInt(pos);
        return pos;
    };
    /**
     * Calculate the date based on the day position clicked on
     * @param n Number representing day of the week
     * @return A date object representing the date clicked on
     */
    this.calcDateFromIndex = function(n) {
        var incr = parseInt(n);
        var st = this.viewStart.getDate();
        var ret = null;
        st += incr;
        ret = new Date(this.viewStart.getFullYear(), this.viewStart.getMonth(), st);
        return ret;
    };

    // ==========================
    // DOM-element info extraction and IDs
    // ==========================
    /**
     * Takes the ID of any of the component DOM elements that collectively make up
     * an event block, and look up which event the block belongs to.
     * Event block components are all similarly named, beginning with 'eventDiv',
     * then followed by some indentifying text, a separator, and then the ID.
     * (e.g., 'eventDivBottom__12' or 'eventDivContent__8').
     * @return A string representing the event identifier for the event block clicked on
     */
    this.getIndexEvent = function(strId) {
        // Use regex to pull out the actual ID number
        var pat = new RegExp('^eventDiv[\\D]*' + Cal.ID_SEPARATOR);
        var id = strId.replace(pat, '');
        return id;
    };
    /**
     * Takes the ID of any of the component DOM elements that collectively make up
     * an hour container, and look up which date/time the div belongs to.
     * Hour-div components are all similarly named, beginning with 'hourDiv',
     * then followed by some indentifying text, and then the date and hour
     * separated by a hyphen (e.g., 'hourDiv20051223-13' or 'hourDivSub20051016-2').
     * @return A string representing the date/time of the div clicked on
     */
    this.getIndexFromHourDiv = function(strId) {
        var ind = strId.replace(/^hourDiv[\D]*/i, '');
        return ind;
    };
    /**
     * Takes the ID of any of the component DOM elements that collectively make up
     * an all-day event container, and look up which date the div belongs to.
     * All-day-div components are all similarly named, beginning with 'allDayListDiv',
     * then followed by some indentify
IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 425
E.....@.@.so.........`.....J.9.............
.......JGET /scooby/js/scooby/ui/draggable.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 9000
E.#\..@.@.$............`.9..........!Q.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"14546-1147764336000"
Last-Modified: Tue, 16 May 2006 07:25:36 GMT
Content-Type: text/javascript
Content-Length: 14546
Date: Sun, 08 Oct 2006 23:34:05 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

/**
 * @fileoverview An event block being dragged or resized
 * @author Matthew Eernisse mailto:mde@osafoundation.org
 * @license Apache License 2.0
 *
 * Has two sub-classes, HasTimeDraggable and NoTimeDraggable to represent
 * the two main areas where blocks get displayed. HasTime are normal
 * events in the scrolling area, and NoTime are the all-day events
 * in the resizeable area at the top.
 * 
 * Lots of annoying math in here for calculating size/pos of the block.
 * Multiple levels of vertical offset from translation of absolute XY
 * for mouse pos to relative placement of dragged divs, in addition to
 * vert offset when area scrolls.
 */

/**
 * @object An event block being dragged or resized
 */
function Draggable(id) {
    // Unused -- TO-DO: Get rid of this property
    this.id = id;
    // Unused -- TO-DO: Get rid of this property
    this.div = null;
    // Dragged/resized or not
    this.dragged = false;
    // Used in calcluations for resizing
    this.height = 0;
    // The top of the block being dragged -- essentially div.offsetTop
    // because now Cal.top is always set to zero 
    this.absTop = 0;
    // The offset of the left/top edges of the dragged block from the 
    // mouse click position -- lets you keep the block a fixed pos 
    // from the location of the pointer while dragging
    this.clickOffsetX = 0;
    this.clickOffsetY = 0;
    // Dragging vs. resizing -- 'drag', 'resizetop', or 'resizebottom'
    this.dragMode = null;
    // 30 min. increment size -- subtract 2px for stupid CSS borders
    this.unit = ((HOUR_UNIT_HEIGHT/2)-2);
    // Minimum height for resizing a single-day normal event
    this.min = 0;
    // Right-hand side limit for dragging
    this.rLimit = 0;
    /**
     * Sets up the Draggable -- putting in 'init' func allows code
     * to be shared between the sub-classes
     */
    this.init = function(dragMode) {
        // Reference to the main block and div for this Draggable
        var block = Cal.currSelObj.block;
        var div = block.div;
        
        this.dragMode = dragMode;
        // Snapshot measurements
        this.origDivLeft = div.offsetLeft;
        this.origDivWidth = div.offsetWidth;
        this.plantonicBlockLeft = block.getPlatonicLeft();
        this.platonicBlockWidth = block.getPlatonicWidth();
        this.height = div.offsetHeight;
        this.absTop = div.offsetTop + Cal.top;
        this.bottom = div.offsetTop + this.height;
        this.min = this.bottom-(HOUR_UNIT_HEIGHT/2)+2;
        this.clickOffsetX = xPos - this.origDivLeft;
        this.clickOffsetY = yPos - div.offsetTop;
        this.rLimit = (Cal.midColWidth - Cal.dayUnitWidth - 
            SCROLLBAR_SPACER_WIDTH - 2); 
        
        // FIXME: This causes a really irritating CSS bug in Mozilla/Firefox
        // Set opacity if moving
        //if (this.dragMode == 'drag') {
        //    Cal.currSelObj.block.setOpacity(60);
        //}
    };
    /**
     * Move the Draggable's block
     * Note that this moves the block's visual position, but the
     * properties (.top, .left, .height, .width)  don't get updated
     * until the actual drop occurs
     */
    this.move = function() {
        // Compensate for crack-fiend clicking -- make sure there's
        // a selected block
        if (!Cal.currSelObj) {
            return false;
        }
        
        // Not just a simple click
        this.dragged = true;
        
        // Block associated with this Draggable
        var moveBlock = Cal.currSelObj.block;
        // Subtract the offset to get where the left/top
        // of the block should be
        var moveX = (xPos - this.clickOffsetX);
        var moveY = (yPos - this.clickOffsetY);
        // Right-hand move constraint
        var rLimit = this.rLimit;
        // Bottom move constraint -- need to know height of block
        var bLimit = this.getBLimit(moveBlock);
        // Add drag constraints
        // -----------------------------
        moveX = moveX < 0 ? 0 : moveX; // Left bound
        moveX = moveX > rLimit ? rLimit : moveX; // Right bound
        moveY = moveY < 0 ? 0 : moveY; // Top bound
        moveY = moveY > bLimit ? bLimit : moveY; // bottom bound
        // Move the block -- don't actually reset block properties
        // until drop
        moveBlock.setLeft(moveX);
        // Don't move multi-day events vertically yet
        if (!moveBlock.auxDivList.length) {
            moveBlock.setTop(moveY);
        }
    };
    /**
     * Convenience method for ensuring the size set for the
     * associated block is at least the minimum of a 30-min. chunk
     */
    this.getSize = function(size) {
        var ret = 0;
        ret = parseInt(size);
        // Single-day events only
        // Min size is 30 minutes -- two 15-min. chunks
        if (!Cal.currSelObj.block.auxDivList.length) {
            ret = ret < this.unit ? this.unit : ret;
        }
        return ret;
    };
    /**
     * Main method for updating block/event, called after drop
     * The block properties get updated one-by-one in the drop
     * and here is where we update the event data based on the
     * updated block
     * This method also calls remoteSaveMain which save the
     * changes to the event to the backend
     */
    this.doUpdate = function() {
        var selObj = Cal.currSelObj;
        // Make backup snapshot of event data in case save/remove
        // operation fails
        selObj.makeSnapshot();
        // Update the event properties based on the block pos/size
        if (selObj.block.updateEvent(selObj, this.dragMode)) {
            // Check against the backup to make sure the event has
            // actually been edited
            if (selObj.hasChanged()) {
                selObj.setInputDisabled(true); // Disable input while processing
                // Save the changes
                // ==========================
                selObj.remoteSaveMain();
            }
            // If no real edit, then just reposition the block
            // With conflict calculations and snap-to
            else {
                selObj.block.updateFromEvent(selObj);
                selObj.block.updateElements();
            }
        }
    };
    /**
     * Abort if the Draggable object does not point to an actual div
     * or doesn't have a valid dragMode
     */
    this.paranoia = function() {
        if (!Cal.currSelObj || !Cal.dragElem.dragMode || 
            Cal.currSelObj.getInputDisabled()) {
            return false;
        }
        else {
            return true;
        }
    };
}

/**
 * HasTimeDraggable -- sub-class of Draggable
 * Normal events, 'at-time' events -- these sit in the scrollable 
 * area of the main viewing area
 */
function HasTimeDraggable(id) {
    this.id = id;
}
HasTimeDraggable.prototype = new Draggable();

/**
 * Resizes both upward and downward -- irritating math calculations
 * caused by the fact that the position measurements for the block
 * have to be set relative to the scrolling div they sit in, but the
 * X and Y for the mouse pos are absolute.
 * Of course the div they sit in can also scroll, so that adds another
 * nice layer of vertical offset.
 * Resizing up means moving the block up while simultaneously 
 * growing the height
 * Resizing down means just growing the height
 */
HasTimeDraggable.prototype.resize = function() {
    
    this.dragged = true;

    // Do intense math stuff (waves hands) 
    // to figure out top and bottom positions
    // ====================================
    // Grabbing the top resize handle, resizing upward
    // Observe min. size constraints for block size and top constraints
    // for sizing upward
    if (this.dragMode == 'resizetop') {
        this.resizeTop(yPos);
    }
    // Grabbing the bottom resize handle, resizing downward
    // Observe min. size constraints for block size and bottom
    // constraints for sizing downward 
    else if (this.dragMode == 'resizebottom') {
        this.resizeBottom(yPos);
    }
}

/**
 * Resize for top edge of normal event block -- moves div
 * element up at same rate it resizes. 
 */
HasTimeDraggable.prototy
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 5760
E.....@.@.1............`.9=7...............
........pe.resizeTop = function(y) {
    // The selected event
    var selObj = Cal.currSelObj;
    // Where the top edge of the block should go, given any offset for the
    // top of the calendar, and any scrolling in the scrollable area
    // Used when resizing up
    var t = (y-Cal.top)+Cal.getMainViewScrollTop();
    var size = 0;
    
    t = t > this.min ? this.min : t;
    t = t < 0 ? 0 : t;
    selObj.block.setTop(t);
    //if (!selObj.block.auxDivList.length) {
        size = this.getSize((this.absTop-yPos-Cal.getMainViewScrollTop())
            + this.height);
        
        selObj.block.setHeight(size, true);
   // }
}

/**
 * Resize for bottom edge of normal event block -- simply grows
 * height of div
 */
HasTimeDraggable.prototype.resizeBottom = function(y) {
   // The selected event
    var selObj = Cal.currSelObj;
    // Where the bottom edge of the block should go -- this is a
    // relative measurement based on pos on the scrollable area
    var b = (y-this.absTop)+Cal.getMainViewScrollTop();
    var max = (VIEW_DIV_HEIGHT - this.absTop);
    b = b > max ? max : b;
    size = this.getSize(b);
    selObj.block.setHeight(size);

}

/**
 * Happens on mouseup -- does snap-to in 15-min. increments, 
 * and updates block properties based on final block position
 * The annoying CSS 1px border thing is a major irritation here --
 * it adds to the actual size, but not to the style width/height
 * After updating pos/size properties for the block, this method
 * calls doUpdate to save the changes to block/event and do the 
 * visual update
 */
HasTimeDraggable.prototype.drop = function() {

    if (!this.dragged || !this.paranoia()) {
        return false;
    }
    
    var selObj = Cal.currSelObj;
    var unit = HOUR_UNIT_HEIGHT/4; // 15-min. increments
    var top = 0;
    var size = 0;
    var left = 0;
    var deltaX = 0;
    var deltaY = 0;
    var y = 0;
    var m = 0;
    var d = 0;
    var starttime = 0;
    var endtime = 0;
    
    // Abstract away getting top and bottom -- multi-day events
    // have multiple divs, treat as a composite here
    top = selObj.block.getTop();
    left = selObj.block.getLeft();
    size = selObj.block.getBottom()-top;
    
    // Snap-to for top position: both simple move and resize up
    if (this.dragMode == 'drag' || this.dragMode == 'resizetop') {
        deltaY = top % unit;
        // Drag/resize is just above 15-min. increment -- snap downwards
        if (deltaY > (unit/2)) {
            top = top+(unit-deltaY);
        }
        // Drag/resize is just below 15-min. increment -- snap upwards
        else {
            top = top-deltaY;
        }
        selObj.block.top = top;
    }
    // Snap-to for size: both resize up and resize down
    if (this.dragMode.indexOf('resize') > -1) {
        deltaY = size % unit; // Do snap-to
        // Subtract one pixel for 1px border on background
        size = size - 3;
        // consistent with initial creation code
        // Resize is just above 15-min. increment -- snap downwards
        if (deltaY > (unit/2)) {
            size = size+(unit-deltaY);
        }
        // Resize is just above 15-min. increment -- snap downwards
        else {
            size = size-deltaY;
        }
        selObj.block.height = size;
    }
    // 1px border -- add the 1px taken out for the 1px border 
    // back to the size
    // Make an even number divisible by HOUR_UNIT_HEIGHT
    size = size + 3;
    
    // Snap-to for lateral position
    if (this.dragMode == 'drag') {
        deltaX = left % Cal.dayUnitWidth;
        // Drag is just right of day-width size -- snap left
        if (deltaX > (Cal.dayUnitWidth/2)) {
            left = left+(Cal.dayUnitWidth-deltaX);
        }
        // Drag is just left of day-width size -- snap right
        else {
            left = left-deltaX;
        }
        selObj.block.left = left;
    }
    // Do the actual update
    // =================
    this.doUpdate();
}

/**
 * Get the bottom constraint for moving/resizing a block
 */
HasTimeDraggable.prototype.getBLimit = function(moveblock) {
    return (VIEW_DIV_HEIGHT - moveblock.height);
}

HasTimeDraggable.prototype.setDragWidth = function() {}

/**
 * NoTimeDraggable -- sub-class of Draggable
 * All-day events, 'any-time' events -- these sit up in the 
 * resizable area at the top of the UI
 */
function NoTimeDraggable(id) {
    this.id = id;
}
NoTimeDraggable.prototype = new Draggable();

/**
 * Happens on mouseup -- does snap-to in 15-min. increments, 
 * and updates block properties based on final block position
 * After updating pos/size properties for the block, this method
 * calls doUpdate to save the changes to block/event and do the 
 * visual update
 */
NoTimeDraggable.prototype.drop = function() {

    if (!this.dragged || !this.paranoia()) {
        return false;
    }
    var top = 0;
    var left = 0;
    var deltaX = 0;
    var deltaY = 0;
    var selObj = Cal.currSelObj;
    top = selObj.block.getTop();
    left = selObj.block.getLeft();
    
    // Side-to-side snap
    if (this.dragMode == 'drag') {
        deltaX = left % Cal.dayUnitWidth;
        // Drag is just right of day-width size -- snap left
        if (deltaX > (Cal.dayUnitWidth/2)) {
            left = left+(Cal.dayUnitWidth-deltaX);
        }
        // Drag is just left of day-width size -- snap right
        else {
            left = left-deltaX;
        }
        selObj.block.left = left;
    }
    selObj.block.top = top;
    
    // Do the actual update
    // =================
    this.doUpdate();
}

/**
 * Get the bottom constraint for moving/resizing a block
 */
NoTimeDraggable.prototype.getBLimit = function(moveblock) {
    return (Cal.allDayArea.dragSize - moveblock.height - 8);

}


IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 9000
E.#\u.@.@..............^...P./?}....!Q.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"9472-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: text/javascript
Content-Length: 9472
Date: Sun, 08 Oct 2006 23:34:05 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Event handlers for Scooby. Most all UI input is handled here.
 * @author Matthew Eernisse mailto:mde@osafoundation.org
 * @license Apache License 2.0
 *
 * Rather than putting event listeners directly on the DOM elements
 * themselves, Scooby uses meaningful IDs for all clickable elements.
 * All UI input goes through these global listeners and dispatches 
 * events to the appropriate handler based on the ID of the element 
 * being manipulated.
 * 
 * It can become a little tricky identifying exactly what element 
 * got clicked (i.e., requiring irritating regex filtering), but 
 * allows more more centralized control of the event flow.
 */
 
/**
 * Generic function to get the source elem of a UI event
 * @param e A DOM event
 * @return A DOM element that was the source of the event
 * Returns the parent node if the source was a text node
 */
function getSrcElem(e) {
    var ret = null;
    
    if (e.srcElement) ret = e.srcElement;
    else if (e.target) ret = e.target;
    
    // Disabled form elements in IE return a bogus object
    if (typeof ret.id == 'undefined') {
        return document.body;
    }
    // Look up the id of the elem or its parent
    else {
        // Look for something with an id -- not a text node
        while (!ret.id || ret.nodeType == 3) {
            ret = ret.parentNode;
        }
    }
    return ret;
};

/**
 * Double-clicks -- if event source is in the scrolling area
 * for normal events, or in the resizeable all-day event area
 * calls insertCalEventNew to create a new event
 * Eventually will also be used for edit-in-place of event info
 */
function dblClickHandler(e) {
    var strId = '';
    var objElem = null;

    e = !e ? window.event : e;
    objElem = getSrcElem(e);
    strId = objElem.id

    switch (true) {
        // On hour column -- create a new event
        case (strId.indexOf('hourDiv') > -1):
        // On all-day column -- create new all-day event
        case (strId.indexOf('allDayListDiv') > -1):
            Cal.insertCalEventNew(strId);
            break;
        // On event title -- edit-in-place
        case (strId.indexOf('eventDiv') > -1):
            // Edit-in-place will go here
            break;
    }
}

/**
 * Single-clicks -- do event dispatch based on ID of event's
 * DOM-element source. Includes selecting/moving/resizing event blocks
 * and resizing all-day event area
 * Mousedown on an event always creates a Draggable in anticipation
 * of a user dragging the block. Draggable is destroyed on mousedown.
 * The Cal's dragElem may either be set to this Draggable, or to
 * the ResizeArea for all-day events
 */
function mouseDownHandler(e) {

    var strId = '';
    var dragElem = null;
    var objElem = null;
    var selObj = null;
    var id = '';

    // =================
    // Check for client-side timeout on all mouse clicks
    // =================
    Cal.checkTimeout();

    e = !e ? window.event : e;
    objElem = getSrcElem(e);
    strId = objElem.id;

    // ======================================
    // Event dispatch
    // ======================================
    switch (true) {
        // Mouse down on the hour columns -- exit to prevent text selection
        case (strId.indexOf('hourDiv') > -1):
            return false;
            break;
        
        // On event block -- simple select, or move/resize
        case (strId.indexOf('eventDiv') > -1):
            // Get the clicked-on event
            id = Cal.getIndexEvent(strId);
            selObj = Cal.eventRegistry.getItem(id);

            // If this object is currently in 'processing' state, ignore any input
            if (selObj.getInputDisabled()) {
                return false;
            }
            
            Cal.setSelected(selObj);
            // Set up Draggable and save dragMode -- user may be dragging
            if (strId.indexOf('AllDay') > -1) {
                dragElem = new NoTimeDraggable(id);
            }
            else {
                dragElem = new HasTimeDraggable(id);
            }
            // Set the Cal draggable to the dragged block
            Cal.dragElem = dragElem;
            switch(true) {
                // Main content area -- drag entire event
                case strId.indexOf('Content') > -1:
                case strId.indexOf('Title') > -1:
                case strId.indexOf('Start') > -1:
                    dragElem.init('drag');
                    break;
                // Top lip -- resize top
                case strId.indexOf('Top') > -1:
                    dragElem.init('resizetop');
                    break;
                // Bottom lip -- resize bottom
                case strId.indexOf('Bottom') > -1:
                    dragElem.init('resizebottom');
                    break;
                default:
                    // Do nothing
                    break;
            }

            // Update event detail form
            // ------------------------------------
            // Show clicked-on event's details in detail form
            Cal.calForm.updateFromEvent(selObj);
            // Enable both Remove and Save buttons
            Cal.calForm.setButtons(true, true);

            break;
        
        case (strId.indexOf('allDayResizeHandleDiv') > -1):
            // Set the Cal draggable to the resize area's resize handle
            Cal.dragElem = Cal.allDayArea;
            break;
    }
}

/**
 * Moving the mouse -- Used for dragging event blocks, 
 * or for resizing the all-day event area.
 */
function mouseMoveHandler(e) {
    var dragElem = Cal.dragElem;
    
    // Set global x-y coords
    xPos = e ? e.pageX : window.event.x;
    yPos = e ? e.pageY : (window.event.y + document.body.scrollTop);
    
    // Dragging
    if (dragElem) {
        // No dragMode -- it's the ResizeArea for all-day events
        if (!dragElem.dragMode) {
            dragElem.resize();
        }
        // Event block
        else {
            // Hand off to Draggable methods based on dragMode
            // Set by mouseDownHandler based on location of click
            switch (dragElem.dragMode) {
                case 'drag':
                    dragElem.move();
                    break;
                case 'resizetop':
                case 'resizebottom':
                    dragElem.resize();
                    break;
                default:
                    // Do nothing
                    break;
            }
        }
    }
}

/**
 * Releasing the mouse -- clicking on nav arrows, or dropping
 * after a drag
 */
function mouseUpHandler(e) {
    e = !e ? window.event : e;
    objElem = getSrcElem(e);
    strId = objElem.id;
    // Drop after dragging -- both ResizeArea or event Block
    if (Cal.dragElem) {
        Cal.dragElem.drop();
        Cal.dragElem = null;
    }
}

/**
 * All keyboard input -- includes fu for modal dialog box
 * Also had to do some hacky stuff to suppress Enter and Delete key
 * input when user is typing event detail form fields
 */
function keyUpHandler(e) {

    // =================
    // Check for client-side timeout on all keyboard input
    // =================
    // Have to return false to keep event from continuing to bubble
    // otherwise it actually ends up refreshing the session before
    // we have a chance to time out
    if (Cal.checkTimeout()) {
        return false;
    }

    e = !e ? window.event : e;
    
    // UI input is disabled, check for modal dialog box
    if (Cal.getInputDisabled()) {
        // Execute dialog's default action if user hits Enter key
        if (Cal.dialog.isDisplayed && Cal.dialog.defaultAction && 
            e.keyCode == 13) {            
            Cal.dialog.defaultAction();
        }
    }
    // Normal UI input -- Saving changes and removing events
    // (1) Needs an event to be selected
    // (2) Selected event can't be in 'procssing' state
    // (3) Don't trigger Save/Remove when typing in form fields
    // ---------------
    // TO-DO: Move redundant conditionals up a level
    else {
        switch (e.keyCode) {
            // Enter key
            case 13:
                if (Cal.currSelObj && 
IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 684
E...u.@.@..A...........^...x./?}...........
........
                    !Cal.currSelObj.getInputDisabled() && 
                    !Cal.calForm.textAreaHasFocus) {
                    
                    Cal.saveCalEvent();
                }
                break;
            // Delete key
            case 46:
                if (Cal.currSelObj && 
                    !Cal.currSelObj.getInputDisabled() && 
                    !Cal.calForm.textInputHasFocus) {
                    
                    Cal.showRemoveConfirm();
                }
                break;
        }
    }
}

/**
 * Do cleanup of DOM-element refs to avoid memleak in IE
 */
function cleanup() {
    Cal.wipeView();
    Cal.cleanup();
    Cal = null;
}


IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 432
E.....@.@.se.........`.......9S............
........GET /scooby/js/scooby/ui/contentcontainer.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 429
E....F@.@............_....m..(.............
........GET /scooby/js/scooby/ui/widget/Layout.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 206
E...N*@.@.............._.(....om...........
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"0-1147935930000"
Last-Modified: Thu, 18 May 2006 07:05:30 GMT
Content-Type: text/javascript
Content-Length: 0
Date: Sun, 08 Oct 2006 23:34:05 GMT


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 1276
E..06t@.@..R...........[..Qe.~)......%.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"1064-1147764336000"
Last-Modified: Tue, 16 May 2006 07:25:36 GMT
Content-Type: text/javascript
Content-Length: 1064
Date: Sun, 08 Oct 2006 23:34:05 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var Pref = new function() {
    this.timezoneName = '';

    this.init = function() {
        var prefDate = new Date();
        var tz = ScoobyTimezone.getLocalTimezone(prefDate.getTime());
        this.timezoneName = tz.name;
    };
    this.getTimezoneOffset = function(dt) {
        return ScoobyDate.getBrowserTimezoneOffset(dt.getYear(), dt.getMonth(),
            dt.getDate(), dt.getHours(), dt.getMinutes(), dt.getSeconds());
    };
}

IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 433
E.....@.@.Q..........^.../?}...$...........
........GET /scooby/js/scooby/service/service_stub.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 5844
E...u.@.@..............^...$./A............
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"5632-1147764336000"
Last-Modified: Tue, 16 May 2006 07:25:36 GMT
Content-Type: text/javascript
Content-Length: 5632
Date: Sun, 08 Oct 2006 23:34:05 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/**
 * The ScoobyService is the place where all client/server communcations are
 * brokered. The methods here are all empty because the intent is that you
 * can have different implementations for different types of RPC (eg. JSON-RPC, XML-RPC.)
 * Whichever implementation is loaded will replace the methods here with concrete implementations.
 *
 * All methods here can be called two ways - synchronously or asynchronously. For synchcronous
 * calls just call them method as declared below. For example:
 *
 *    <code> var events = scoobyService.getEvents("Scooby", beginDate, endDate);</code>
 *
 * For asynchronous calls you simply pass a callback as the first argument to any of the methods.
 * The callback is of the form
 *
 *    <code>function callback(result, exception)</code>
 *
 * Note that for synchronous calls exceptions are thrown, while for asynch calls they are simply
 * passed to the callback. In other words, for asycnh calls you don't catch exceptions, just check
 * for the presence of an exception. If it's not null, a problem occured.
 */
ScoobyService = function() {};
ScoobyService.prototype = {

    serviceAccessTime: 0,

     /**
      * Initializes the service. Must be called before using the service.
      */
     init: function() {},

     getServiceAccessTime: function() {},
     resetServiceAccessTime: function() {},
     refreshServerSession: function() {},

     /**
      * Gets all the events between the two dates, sorted in ascending order on
      * begin date
      *
      * @param calendarPath the path to the calendar to save to
      * @param beginDate the date from which to start the search
      * @param endDate the date to end the search on
      * @return an array of CalEvents
      */
    getEvents: function(calendarPath, beginDate, endDate){},

     /**
      * Persists an event to the calendar. If there is no id specified in the given Event
      * a new Event object will be persisted and the id will be returned. If there is an
      * id, the event with the same id in the same calendar will be updated. If there is an
      * id but there is no matching event, a ScoobyService exception will be thrown
      *
      * @param calendarPath
      * @param event
      * @return unique id
      */
     saveEvent: function(event){},

     /**
      * Removes an event with the specified id from the calendar.
      *
      * @param calendarPath path to the calendar containing the Event to be deleted
      * @param id the id of the Event to be deleted
      * @throws ScoobyServiceException
      */
    removeEvent: function(calendarName, id){},

    /**
     * Returns the Event with the given id.
     *
     * @param calendarPath path to the calendar containing the desired Event
     * @param id the id of the desired Event
     * @return the Event with the given id, or null if none found.
     */
    getEvent: function(calendarName, id){},

    /**
     * Moves an event from one calendar to another.
     *
     * @param sourceCalendar the path to the calendar currently containing the event to be moved
     * @param id the id of the event to be moved
     * @param destinationCalendar the desired destination
     */
    moveEvent: function(sourceCalendar, id, destinationCalendar){},

    /**
     * Returns all the calendar names for the current users.
     *
     * @return an array of the calendar names that exist for the current user
     */
    getCalendars: function() {},

    /**
     * Creates a calendar with the given name
     * @param calendarName
     */
    createCalendar: function(calendarName, calendarPath){},
    
    /**
     * Gets the preference for the given name for the current user.
     * @param preferenceName the name of the preference to retrieve
     * @return the preference
     */
    getPreference: function(preferenceName){},
    
    /**
     * Sets the preference for the given name for the current user
     * with the value specified.
     * @param preferenceName the name of the preference to set
     * @param value the value to set it to
     */
    setPreference: function(preferenceName, value){},
    
    /**
     * Returns the version of Scooby
     */
    getVersion: function(){}
}

/**
 * This is the root of all exceptions thrown by the ScoobyService
 */
ScoobyServiceException = function(){};
ScoobyServiceException.prototype = new Error();

/**
 * This exception is the root for all exceptions that occur on the client side
 */
ScoobyServiceClientException = function(){};
ScoobyServiceClientException.prototype = new ScoobyServiceException();

/**
 * This exception is the root for all exceptions that occur on the server side
 */
ScoobyServiceRemoteException = function(){};
ScoobyServiceRemoteException.prototype = new ScoobyServiceException();

/**
 * This exception is thrown when the client makes a call on the server side but is not
 * authenticated.
 */
NotAuthenticatedException = function(){};
NotAuthenticatedException.prototype = new ScoobyServiceRemoteException();

IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 438
E.....@.@.s].........`.......9^B...........
...!....GET /scooby/js/scooby/service/json_service_impl.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 9000
E.#\N+@.@..n..........._.(.w..q.....!Q.....
...C...%HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"14855-1147764336000"
Last-Modified: Tue, 16 May 2006 07:25:36 GMT
Content-Type: text/javascript
Content-Length: 14855
Date: Sun, 08 Oct 2006 23:34:05 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

/**
 * @fileoverview Calendar events -- links the Block to the CalEventData
 * @author Matthew Eernisse mailto:mde@osafoundation.org
 * @license Apache License 2.0
 */

/**
 * @object CalEvent -- an event on the Calendar, links to the event's
 * Block and CalEventDate objects
 */
function CalEvent(id, block) {
    // Randomly generated ID for each CalEvent
    // Block div elements get their id suffixes from this
    this.id = id;
    // Points to this event's Block obj
    this.block = block;
    // Points to this event's CalEventData obj
    this.data = null;
    // A backup copy (clone) of the .data property made 
    // before trying to edit
    this.dataOrig = null;
    // If the event has a edit/remove call processing, don't allow
    // user to move/resize
    this.inputDisabled = false;
    // List of conflicting events that come before this event
    this.beforeConflicts = [];
    // List of conflicting events that come after this event
    this.afterConflicts = [];
    // Indent level
    this.conflictDepth = 0;
    // Total indent level
    this.maxDepth = 0;
    // Row occupied for an untimed event
    this.allDayRow = 0;
    
    /**
     * Indicates if an event has actually been edited or not --
     * used when dragging or moving to make sure event has really
     * changed before saving
     */
    this.hasChanged = function() {
        ret = false;
        if ((this.data.start.toUTC() != this.dataOrig.start.toUTC()) ||
            (this.data.end.toUTC() != this.dataOrig.end.toUTC())) {
            ret = true;
        }
        else {
            ret = false;
        }
        return ret;
    }
    /**
     * Gateway function for saving changes to events. Currently 
     * has bandaid of throwing up confirmation dialog for editing
     * recurring events. Non-recurring events pass right through to
     * remoteSave method.
     */
    this.remoteSaveMain = function() {
        // Deal with loss of scope from callback
        var ev = Cal.currSelObj;

        // BANDAID: Keep people from shooting themselves in the foot
        // with reurring events
        if (ev.data.masterEvent || ev.data.instance) {
            msg = 'This is a recurring event. Editing recurring events is not supported in Scooby,' +
                ' and will probably have effects you do not intend.<br/>&nbsp;<br/>Save this change?';
            Cal.showSaveConfirm(msg);
        }
        else {
            ev.remoteSave();
        }
    };
    /**
     * Main function for saving changes to events.
     * Call to the Scooby service happens on a short timeout to
     * give time for the processing state to display. This avoids
     * horrible, Pokemon-seizure-inducing flashes on screen
     * Does the visual stuff first, then hands off to remoteSaveDelay
     */
    this.remoteSave = function() {
        // Deal with loss of scope from callback
        var ev = Cal.currSelObj;

        // Block stuff
        // =====================
        // Reset the block because we may be changing to the new type --
        // e.g., between all-day and normal, or between normal single
        // and normal composite
        if (ev.dataOrig && !(ev.data.allDay && ev.dataOrig.allDay)) {
            ev.replaceBlock();
        }
        // Reset the block properties from the event
        ev.block.updateFromEvent(ev, true);
        // Do visual updates to size, position
        ev.block.updateElements();
        // Display processing animation
        ev.block.showProcessing();

        // Call remote save process
        // =====================
        // Paranoia check to ensure element is actually there
        if (Cal.eventRegistry.getItem(ev.id)) {
            setTimeout('Cal.eventRegistry.getItem(\'' + ev.id +
                '\').remoteSaveDelay();', 500);
        }

        if (Cal.dialog.isDisplayed) {
            Cal.hideDialog();
        }

    };
    /**
     * Allows the user to cancel before saving changes to an event
     * Restores event to its position previous to the aborted save
     */
    this.cancelSave = function() {
        // Put the block back where it was
        Cal.currSelObj.restoreEvent();
        // Hide the confirmation dialog
        if (Cal.dialog) {
            Cal.hideDialog();
        }
    };
    /**
     * Creates a brand-new block for the event
     * Resets the block because we may be changing to the new type --
     * e.g., between all-day and normal, or between normal single
     * and normal composite
     */
    this.replaceBlock = function() {
        // Remove the current block
        this.block.remove();
        // Replace with new one
        if (this.data.allDay) {
            this.block = new NoTimeBlock(this.id);
        }
        else {
            this.block = new HasTimeBlock(this.id);
        }
        this.block.insert(this.id);
    };
    /**
     * Puts an event block back where it originally was using the
     * snapshot backup clone of its data in the dataOrig property
     * Used when canceling a save from the confirmation dialog
     * and when a save operation fails
     */
    this.restoreEvent = function() {
        if (this.restoreFromSnapshot()) {
            this.block.updateFromEvent(this);
            // Update block and event detail form display
            Cal.currSelObj.block.updateDisplayMain();
            this.setInputDisabled(false);
        }
    };
    /**
     * Called from remoteSave -- this method actually calls the
     * Scooby service to save the changes
     * Since the handler for the XHR request will have no idea
     * what event is just saved, we save all that info in the
     * asyncRegistry referenced by the requestId. The info about the
     * processing request is stored in an Async object.
     * The handler for the XHR response is handleRemoteSaveResult
     */
    this.remoteSaveDelay = function() {
        var requestId = Cal.scoob.saveEvent(
            this.handleRemoteSaveResult, Cal.currentCalendar.path, this.data);
        // New Async obj to track this request
        var saveAsync = new Async(requestId, this, 'saveEvent');
        // Add to stack of processing requests
        Cal.asyncRegistry.setItem(requestId, saveAsync);
    };
    /**
     * Calls the remote service method that removes the event on 
     * the backend
     * The remote call works the same was as the save -- the info
     * about the XHR request is saved in the asyncRegistry to be
     * retrieved by the response handler
     * The handler for the XHR response is handleRemoveResult
     */
    this.remove = function() {
        var requestId = Cal.scoob.removeEvent(
            this.handleRemoveResult, Cal.currentCalendar.path, this.data.id);
        // New Async obj to track this request
        var removeAsync = new Async(requestId, this, 'removeEvent');
        // Add to stack of processing requests
        Cal.asyncRegistry.setItem(requestId, removeAsync);

    };
    /**
     * Handler for XHR request from remoteSaveDelay. Looks up info
     * about the XHR request from the asyncRegistry to find the event
     * @param str ID of the CalEventData object -- the ID of the event
     * on the backend 
     * @param e Error if any
     * @param requestId Int the ID of the request, used to look up
     * which event was being processed
     */
    this.handleRemoteSaveResult = function(str, e, requestId) {
        // Look up the Async process
        var saveAsync = Cal.asyncRegistry.getItem(requestId);
        // Look up the event
        var saveObj = saveAsync.procObj;
        // Simple error message to go along with details from Error obj
        var errMsg = '';
        
        // Failure -- display exception info
        // ============
        if (e) {
            // Failed update -- fall back to original state
            if (saveObj.dataOrig) {
                // Restore from backup snapshot
                saveObj.restoreEvent();
                // Re-enable user input on this event
                saveObj.setInputDisabled(false);
                errMsg = getText('Main.Error.EventEditSaveFailed');
            }
            // Failed create -- remove fake 
IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 9000
E.#\u.@.@..............^...../B.....!Q.....
...a...MHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"13249-1147668702000"
Last-Modified: Mon, 15 May 2006 04:51:42 GMT
Content-Type: text/javascript
Content-Length: 13249
Date: Sun, 08 Oct 2006 23:34:05 GMT

/*
 * JSON-RPC JavaScript client
 *
 * $Id: jsonrpc.js,v 1.36.2.3 2006/03/08 15:09:37 mclark Exp $
 *
 * Copyright (c) 2003-2004 Jan-Klaas Kollhof
 * Copyright (c) 2005 Michael Clark, Metaparadigm Pte Ltd
 *
 * This code is based on Jan-Klaas' JavaScript o lait library (jsolait).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */


/* escape a character */

escapeJSONChar =
function escapeJSONChar(c)
{
    if(c == "\"" || c == "\\") return "\\" + c;
    else if (c == "\b") return "\\b";
    else if (c == "\f") return "\\f";
    else if (c == "\n") return "\\n";
    else if (c == "\r") return "\\r";
    else if (c == "\t") return "\\t";
    var hex = c.charCodeAt(0).toString(16);
    if(hex.length == 1) return "\\u000" + hex;
    else if(hex.length == 2) return "\\u00" + hex;
    else if(hex.length == 3) return "\\u0" + hex;
    else return "\\u" + hex;
};


/* encode a string into JSON format */

escapeJSONString =
function escapeJSONString(s)
{
    /* The following should suffice but Safari's regex is b0rken
       (doesn't support callback substitutions)
       return "\"" + s.replace(/([^\u0020-\u007f]|[\\\"])/g,
       escapeJSONChar) + "\"";
    */

    /* Rather inefficient way to do it */
    var parts = s.split("");
    for(var i=0; i < parts.length; i++) {
	var c =parts[i];
	if(c == '"' ||
	   c == '\\' ||
	   c.charCodeAt(0) < 32 ||
	   c.charCodeAt(0) >= 128)
	    parts[i] = escapeJSONChar(parts[i]);
    }
    return "\"" + parts.join("") + "\"";
};


/* Marshall objects to JSON format */

toJSON = function toJSON(o)
{
    if(o == null) {
	return "null";
    } else if(o.constructor == String) {
	return escapeJSONString(o);
    } else if(o.constructor == Number) {
	return o.toString();
    } else if(o.constructor == Boolean) {
	return o.toString();
    } else if(o.constructor == Date) {
	return '{javaClass: "java.util.Date", time: ' + o.valueOf() +'}';
    } else if(o.constructor == Array) {
	var v = [];
	for(var i = 0; i < o.length; i++) v.push(toJSON(o[i]));
	return "[" + v.join(", ") + "]";
    } else {
	var v = [];
	for(attr in o) {
	    if(o[attr] == null) v.push("\"" + attr + "\": null");
	    else if(typeof o[attr] == "function"); /* skip */
	    else v.push(escapeJSONString(attr) + ": " + toJSON(o[attr]));
	}
	return "{" + v.join(", ") + "}";
    }
};


/* JSONRpcClient constructor */

JSONRpcClient =
function JSONRpcClient_ctor(serverURL, user, pass, objectID)
{
    this.serverURL = serverURL;
    this.user = user;
    this.pass = pass;
    this.objectID = objectID;

    /* Add standard methods */
    if(this.objectID) {
	this._addMethods(["listMethods"]);
	var req = this._makeRequest("listMethods", []);
    } else {
	this._addMethods(["system.listMethods"]);
	var req = this._makeRequest("system.listMethods", []);
    }
    var m = this._sendRequest(req);
    this._addMethods(m);
};


/* JSONRpcCLient.Exception */

JSONRpcClient.Exception =
function JSONRpcClient_Exception_ctor(code, message, javaStack)
{
    this.code = code;
    var name;
    if(javaStack) {
	this.javaStack = javaStack;
	var m = javaStack.match(/^([^:]*)/);
	if(m) name = m[0];
    }
    if(name) this.name = name;
    else this.name = "JSONRpcClientException";
    this.message = message;
};

JSONRpcClient.Exception.CODE_REMOTE_EXCEPTION = 490;
JSONRpcClient.Exception.CODE_ERR_CLIENT = 550;
JSONRpcClient.Exception.CODE_ERR_PARSE = 590;
JSONRpcClient.Exception.CODE_ERR_NOMETHOD = 591;
JSONRpcClient.Exception.CODE_ERR_UNMARSHALL = 592;
JSONRpcClient.Exception.CODE_ERR_MARSHALL = 593;

JSONRpcClient.Exception.prototype = new Error();

JSONRpcClient.Exception.prototype.toString =
function JSONRpcClient_Exception_toString(code, msg)
{
    return this.name + ": " + this.message;
};


/* Default top level exception handler */

JSONRpcClient.default_ex_handler =
function JSONRpcClient_default_ex_handler(e) { alert(e); };


/* Client settable variables */

JSONRpcClient.toplevel_ex_handler = JSONRpcClient.default_ex_handler;
JSONRpcClient.profile_async = false;
JSONRpcClient.max_req_active = 1;
JSONRpcClient.requestId = 1;


/* JSONRpcClient implementation */

JSONRpcClient.prototype._createMethod =
function JSONRpcClient_createMethod(methodName)
{
    var fn=function()
    {
	var args = [];
	var callback = null;
	for(var i=0;i<arguments.length;i++) args.push(arguments[i]);
	if(typeof args[0] == "function") callback = args.shift();
	var req = fn.client._makeRequest.call(fn.client, fn.methodName,
					      args, callback);
	if(callback == null) {
	    return fn.client._sendRequest.call(fn.client, req);
	} else {
	    JSONRpcClient.async_requests.push(req);
	    JSONRpcClient.kick_async();
	    return req.requestId;
	}
    };
    fn.client = this;
    fn.methodName = methodName;
    return fn;
};

JSONRpcClient.prototype._addMethods =
function JSONRpcClient_addMethods(methodNames)
{
    for(var i=0; i<methodNames.length; i++) {
	var obj = this;
	var names = methodNames[i].split(".");
	for(var n=0; n<names.length-1; n++) {
	    var name = names[n];
	    if(obj[name]) {
		obj = obj[name];
	    } else {
		obj[name]  = new Object();
		obj = obj[name];
	    }
	}
	var name = names[names.length-1];
	if(!obj[name]) {
	    var method = this._createMethod(methodNames[i]);
	    obj[name] = method;
	}
    }
};

JSONRpcClient._getCharsetFromHeaders =
function JSONRpcClient_getCharsetFromHeaders(http)
{
    try {
	var contentType = http.getResponseHeader("Content-type");
	var parts = contentType.split(/\s*;\s*/);
	for(var i =0; i < parts.length; i++) {
	    if(parts[i].substring(0, 8) == "charset=")
		return parts[i].substring(8, parts[i].length);
	}
    } catch (e) {}
    return "UTF-8"; /* default */
};

/* Async queue globals */
JSONRpcClient.async_requests = [];
JSONRpcClient.async_inflight = {};
JSONRpcClient.async_responses = [];
JSONRpcClient.async_timeout = null;
JSONRpcClient.num_req_active = 0;

JSONRpcClient._async_handler =
function JSONRpcClient_async_handler()
{
    JSONRpcClient.async_timeout = null;

    while(JSONRpcClient.async_responses.length > 0) {
	var res = JSONRpcClient.async_responses.shift();
	if(res.canceled) continue;
	if(res.profile) res.profile.dispatch = new Date();
	try {
	    res.cb(res.result, res.ex, res.profile);
	} catch(e) {
	    JSONRpcClient.toplevel_ex_handler(e);
	}
    }

    while(JSONRpcClient.async_requests.length > 0 &&
	  JSONRpcClient.num_req_active < JSONRpcClient.max_req_active) {
	var req = JSONRpcClient.async_requests.shift();
	if(req.canceled) continue;
	req.client._sendRequest.call(req.client, req);
    }
};

JSONRpcClient.kick_async =
function JSONRpcClient_kick_async()
{
    if(JSONRpcClient.async_timeout == null)
	JSONRpcClient.async_timeout =
	    setTimeout(JSONRpcClient._async_handler, 0);
};

JSONRpcClient.cancelRequest =
function JSONRpcClient_cancelRequest(requestId)
{
    /* If it is in flight then mark it as canceled in the inflight map
       and the XMLHttpRequest callback will discard the reply. */
    if(JSONRpcClient.async_inflight[requestId]) {
	JSONRpcClient.async_inflight[requestId].canceled = true;
	return true;
    }

    /* If its not in flight yet then we can just mark it as canceled in
       the the request queue and it will get discarded before being sent. */
    for(var i in JSONRpcClient.async_requests) {
	if(JSONRpcClient.async_requests[i].requestId == requestId) {
	    JSONRpcClient.async_requests[i].canceled = true;
	    return true;
	}
    }

    /* It may have returned from the network and be waiting for its callback
       to be dispatched, so mark it as canceled in the response queue
       and the response will get discarded before calling the callback. */
    for(var i in JSONRpcClient.async_responses) {
	if(JSONRpcClient.async_responses[i].requestId == requestId) {
	    JSONRpcClient.async_responses[i].canceled = true;
	    return true;
	}
    }

    return false;
};

JSONRpcClient.prototype._makeRequest =
function JSONRpcClient_makeRequest(methodName, args, cb)
{
    var req = {};
    req.client = this;
    req.requestId = JSONRpcClient.requestId++;

    var obj = {};
    obj.id = req.requestId;
    if (this.objectID)
	obj.method = ".obj#" + this.objectID + "." + methodName;
    else
	obj.method = methodName;
    obj.params = args;

    if (cb) req.cb = cb;
    
IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 4463
E...u.@.@..{...........^... ./B............
...a...aif (JSONRpcClient.profile_async)
	req.profile = { "submit": new Date() };
    req.data = toJSON(obj);

    return req;
};

JSONRpcClient.prototype._sendRequest =
function JSONRpcClient_sendRequest(req)
{
    if(req.profile) req.profile.start = new Date();

    /* Get free http object from the pool */
    var http = JSONRpcClient.poolGetHTTPRequest();
    JSONRpcClient.num_req_active++;

    /* Send the request */
    if (typeof(this.user) == "undefined") {
	http.open("POST", this.serverURL, (req.cb != null));
    } else {
	http.open("POST", this.serverURL, (req.cb != null), this.user, this.pass);
    }

    /* setRequestHeader is missing in Opera 8 Beta */
    try { http.setRequestHeader("Content-type", "text/plain"); } catch(e) {}

    /* Construct call back if we have one */
    if(req.cb) {
	var self = this;
	http.onreadystatechange = function() {
	    if(http.readyState == 4) {
		http.onreadystatechange = function () {};
		var res = { "cb": req.cb, "result": null, "ex": null};
		if (req.profile) {
		    res.profile = req.profile;
		    res.profile.end = new Date();
		}
		try { res.result = self._handleResponse(http); }
		catch(e) { res.ex = e; }
		if(!JSONRpcClient.async_inflight[req.requestId].canceled)
		    JSONRpcClient.async_responses.push(res);
		delete JSONRpcClient.async_inflight[req.requestId];
		JSONRpcClient.kick_async();
	    }
	};
    } else {
	http.onreadystatechange = function() {};
    }

    JSONRpcClient.async_inflight[req.requestId] = req;
	
    try {
	http.send(req.data);
    } catch(e) {
	JSONRpcClient.poolReturnHTTPRequest(http);
	JSONRpcClient.num_req_active--;
	throw new JSONRpcClient.Exception
	    (JSONRpcClient.Exception.CODE_ERR_CLIENT, "Connection failed");
    }

    if(!req.cb) return this._handleResponse(http);
};

JSONRpcClient.prototype._handleResponse =
function JSONRpcClient_handleResponse(http)
{
    /* Get the charset */
    if(!this.charset) {
	this.charset = JSONRpcClient._getCharsetFromHeaders(http);
    }

    /* Get request results */
    var status, statusText, data;
    try {
	status = http.status;
	statusText = http.statusText;
	data = http.responseText;
    } catch(e) {
	JSONRpcClient.poolReturnHTTPRequest(http);
	JSONRpcClient.num_req_active--;
	JSONRpcClient.kick_async();
	throw new JSONRpcClient.Exception
	    (JSONRpcClient.Exception.CODE_ERR_CLIENT, "Connection failed");
    }

    /* Return http object to the pool; */
    JSONRpcClient.poolReturnHTTPRequest(http);
    JSONRpcClient.num_req_active--;

    /* Unmarshall the response */
    if(status != 200) {
	throw new JSONRpcClient.Exception(status, statusText);
    }
    var obj;
    try {
	eval("obj = " + data);
    } catch(e) {
	throw new JSONRpcClient.Exception(550, "error parsing result");
    }
    if(obj.error)
	throw new JSONRpcClient.Exception(obj.error.code, obj.error.msg,
					  obj.error.trace);
    var res = obj.result;

    /* Handle CallableProxy */
    if(res && res.objectID && res.JSONRPCType == "CallableReference")
	return new JSONRpcClient(this.serverURL, this.user,
				 this.pass, res.objectID);

    return res;
};


/* XMLHttpRequest wrapper code */

/* XMLHttpRequest pool globals */
JSONRpcClient.http_spare = [];
JSONRpcClient.http_max_spare = 8;

JSONRpcClient.poolGetHTTPRequest =
function JSONRpcClient_pool_getHTTPRequest()
{
    if(JSONRpcClient.http_spare.length > 0) {
	return JSONRpcClient.http_spare.pop();
    }
    return JSONRpcClient.getHTTPRequest();
};

JSONRpcClient.poolReturnHTTPRequest =
function JSONRpcClient_poolReturnHTTPRequest(http)
{
    if(JSONRpcClient.http_spare.length >= JSONRpcClient.http_max_spare)
	delete http;
    else
	JSONRpcClient.http_spare.push(http);
};

JSONRpcClient.msxmlNames = [ "MSXML2.XMLHTTP.5.0",
			     "MSXML2.XMLHTTP.4.0",
			     "MSXML2.XMLHTTP.3.0",
			     "MSXML2.XMLHTTP",
			     "Microsoft.XMLHTTP" ];

JSONRpcClient.getHTTPRequest =
function JSONRpcClient_getHTTPRequest()
{
    /* Mozilla XMLHttpRequest */
    try {
	JSONRpcClient.httpObjectName = "XMLHttpRequest";
	return new XMLHttpRequest();
    } catch(e) {}

    /* Microsoft MSXML ActiveX */
    for (var i=0;i < JSONRpcClient.msxmlNames.length; i++) {
	try {
	    JSONRpcClient.httpObjectName = JSONRpcClient.msxmlNames[i];
	    return new ActiveXObject(JSONRpcClient.msxmlNames[i]);
	} catch (e) {}
    }

    /* None found */
    JSONRpcClient.httpObjectName = null;
    throw new JSONRpcClient.Exception(0, "Can't create XMLHttpRequest object");
};


IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 431
E.....@.@.sb.........`.....Y.9.............
...e..."GET /scooby/js/scooby/ui/event/listeners.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 1421
E.....@.@.B............`.9.................
...k...eHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"1209-1148472026000"
Last-Modified: Wed, 24 May 2006 12:00:26 GMT
Content-Type: text/javascript
Content-Length: 1209
Date: Sun, 08 Oct 2006 23:34:05 GMT

/*
 * Copyright 2006 Open Source Applications Foundation
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Event listeners
// ==============================
document.ondblclick = dblClickHandler;
document.onmousemove = mouseMoveHandler;
document.onmousedown = mouseDownHandler;
document.onmouseup = mouseUpHandler;
document.onkeyup = keyUpHandler;
// IE and Safari -- prevent entire area and text getting selected while you drag
document.ondrag = function () { return false; };
document.onselectstart = function () { return false; };


dojo.event.browser.addListener(window, "onload", init, false);
window.onunload = cleanup;
window.onresize = function() {
    location = location.href;
};

IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 486
E....J@.@............_....q..(.T...........
.......CGET /scooby/js/scooby/ui/widget/templates/Debug.css HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 484
E...N-@.@.............._.(.T..s......
.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"281-1148472832000"
Last-Modified: Wed, 24 May 2006 12:13:52 GMT
Content-Type: text/css
Content-Length: 281
Date: Sun, 08 Oct 2006 23:34:05 GMT

#debugDiv{
   position: absolute; 
   left: 250px; 
   top: 200px; 
   z-index : 1000; 
   font-size: 12pt; 
   visibility:hidden; 
   background:#aaeeaa; 
   height:300px; 
   width:500px;	
   padding: 4px;
}

#dojoDebug{
  height:275px;
  overflow:auto;
  background:#FFFFFF; 
}

IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 487
E...$.@.@..2.........[...~+N..Zj...........
.......KGET /scooby/js/scooby/ui/widget/templates/Debug.html HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 496
E..$6w@.@..[...........[..Zj.~-5...........
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"292-1148472832000"
Last-Modified: Wed, 24 May 2006 12:13:52 GMT
Content-Type: text/html
Content-Length: 292
Date: Sun, 08 Oct 2006 23:34:05 GMT

<div id="debugDiv" dojoAttachPoint="debugDiv">
  <table width="100%">
    <tr>
       <td dojoAttachEvent="onClick: toggleHideShow;">Hide</td>
       <td dojoAttachEvent="onClick: clearContent;">Clear</td>
    </tr>
  </table>
  <div id="dojoDebug" dojoAttachPoint="content">
  </div>
</div>

IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 545
E..U..@.@.Q..........^.../B...,......J.....
...(...aPOST /scooby/JSON-RPC HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Content-Type: text/plain
Content-Length: 55
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew
Pragma: no-cache
Cache-Control: no-cache


IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 55
E..k..@.@.R..........^.../E...,......_.....
...P...P{"id": 1, "method": "system.listMethods", "params": []}
IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 535
E..Ku.@.@..............^..,../E7.....@.....
...T...PHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=utf-8
Content-Length: 391
Date: Sun, 08 Oct 2006 23:34:05 GMT

{"result":["scoobyService.saveEvent","scoobyService.getPreference","scoobyService.removeCalendar","scoobyService.getVersion","scoobyService.getCalendars","scoobyService.createCalendar","scoobyService.removePreference","scoobyService.moveEvent","scoobyService.getEvent","scoobyService.setPreference","scoobyService.getTestString","scoobyService.removeEvent","scoobyService.getEvents"],"id":1}
IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 459
E.....@.@.Qi.........^.../E7...............
...m...TGET /scooby/templates/default/images/button_left_sm.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 460
E...$.@.@..K.........[...~-5..\Z...........
...m....GET /scooby/templates/default/images/nav_arrow_right.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 459
E....L@.@............_....s..(.8...........
...m....GET /scooby/templates/default/images/nav_arrow_left.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 464
E.....@.@.s?.........`.......9.............
...m...kGET /scooby/templates/default/images/resize_handle_image.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.47458 > 127.0.0.1.8080: tcp 460
E....A@.@.)..........b.....S.
.N...........
...m...mGET /scooby/templates/default/images/button_right_sm.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.47457 > 127.0.0.1.8080: tcp 461
E.....@.@.Gb.........a...w...,.F...........
...m...mGET /scooby/templates/default/images/button_center_sm.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.47459 > 127.0.0.1.8080: tcp 470
E..
3
@.@............c.....................
........GET /scooby/templates/default/images/day_col_header_background.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 557
E..au.@.@..............^...../G......V.....
.......mHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"353-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 353
Date: Sun, 08 Oct 2006 23:34:05 GMT

GIF89a	....:.xxx........................yyy...................................................}}~............................................................................................................!.....:.,....	.....~@...x. .......	.$6...W.
{U....G....	.Z.P.
.8.`....{"0...............	.....2)..+.....*	...4
.7
.....!..6"%.,.#...' 0.|.:.9..QOA.;
IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 452
E.....@.@.Qo.........^.../G...0............
........GET /scooby/templates/default/images/logo_sm.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew


IP 127.0.0.1.47460 > 127.0.0.1.8080: tcp 575
E..sJB@.@..@.........d...............h.....
........POST /scooby/JSON-RPC HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Content-Type: text/plain
Content-Length: 63
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598
Pragma: no-cache
Cache-Control: no-cache


IP 127.0.0.1.47460 > 127.0.0.1.8080: tcp 63
E..sJC@.@..?.........d...............g.....
........{"id": 2, "method": "scoobyService.getCalendars", "params": []}
IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 311
E..kN/@.@..[..........._.(.8..t......_.....
.......mHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"107-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 107
Date: Sun, 08 Oct 2006 23:34:05 GMT

GIF89a.......DDD.........XXX^^^NNNZZZbbblllhhhrrrnnn.........!.....
.,.............9.l..#3....C1...R&waJ..;
IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 4543
E...u.@.@..%...........^..0../H............
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"4337-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 4337
Date: Sun, 08 Oct 2006 23:34:05 GMT

GIF89a..(.......7X.x..............N..Uu.<............X..9..k....Wz.7k....;d....*e.E..h.....Gk.<|.....z.......8.....)j..%.Ig.:v..t..Z.r..~.....(Z.e..`.....&u.m.........M..e....s..p..g...p...........b....f..q......8.X..\..-...A....w..I|.3{.d|........U..B..t....K..\}..f..........]..Fw....#]............................Xs..W....*s....Ku.y..W......0.......^...........`..8...M.............$m.v...........3\.......d...<....?...........Wj..k.u.....r...K.hw..|.+.....Um.............B.....l......G.0g..j.@...]..........f..%|..r..B.Bp.......?..A..A..M......]....N.................Da.i.........{....-.................D...........A...........
n.....P.+q............................I..0........t.....2b....	z.................1....&...n....L..
.....n...?..............d...z...+O.3p..G..O.\k.Bd.!.......,......(........H......*\..!...">.......3j..."...X..0A.C.(S.\.PA.i...\.E.I.8s...A\3....@1T....v*]......F	.:j
...n2...#.bp"t{..Q7.>(}....A.....k....q......^._..uD..........n..q..E(......^..7.....~...n1...;".C..+.H..<.r.....+...o.$M...Q...x.S.
 .n....,].9...Y......).......O.(.|'/.;....?sy.Jzh... ".....^xA..A.3]n. .....W.2.hc.u.M../:4h......f.Y........:....
(....."c5..P...$...Q|..=..a.-..)M=..d
.E.I.5....+...@.SL...Z".....0.....B.Xj.e..Pr.6l..G0.....iF...pDQ.p.....W..f.e...=..1..1...?.d1.-...F.p.bK%0..*..T..[
\C....P.=...V....c. L..+..P...^D.BH..@.'b<.k.. 3.?.,.....BB.....3.pB...\..Id.qe..2!.
...C.A ...2.R...`..).t..#.H.A..p2
5.4..&...... .02.. ..w}T....#....a.2.d"F&.p..&W.....+...4...'.lR.=2......Sr.4.#.*e..eH..+3.4g.H.$.Q.!.d..-\....[s-..@X@G....q&.H@..5............L;m....=epr.
......Hp...#!A%.Da.
H.`..q....H....U..C . A...`.E..k.O......C.~.../.E%V..A	@.A.!..............'6...-ed..
.D....S`.Qm1pN....9.....0.T....P@A $d...l..:...#.8.`..2..p &.e...2...X...P.....!..	cx.*..:)....@..)..b...h......:...E(.1.G.:.N.}X..8...."......,..8x.....V...`P.....,J..H...~......
....X...@F)(a.}`..(..M...+...6..LE..h.F9...2.b..(..j..M.Q
 .B$...*.Q
.....0..\...8.&'..x.`......p.%.c.u|..>..f$C.~.G$.8..0..+P.2...V(.
..@ . .#..
.h..$...8..f.......lb....	*..-Xa	.TF3.p.9L.
!.F....e.@.!...v........f..@..(...T..<..
Rf...(..0A.`d...T...@..... ......,(......vp...G.Q...^..P.....C.Nq.....?..........--...`.fX......Bp.....; A	4.	..!.~......5....x.+...-,A..`iLEP.34..y0...........d&....Pb..0.7..Ic...~....Z."...V@....
4T,.A..\&`.Zl../..3.......4}A...	9.B.|..(N..2..Q....=...&.C.W.B.^+.*....P..\.......(.h.....@."@.(.{.
.a.5..0N...PA..$...;._L..>8C.....x.....),..4H....!....(...TX.|.......^0..<..Q...4..	....8 .<.G:
.	h8..9....p.c.....B=...}.A.$..(....M......=a.!......'f..
..	.*.*NLd..!...p..`......x.. ......4...6!.7....=......8..	F...v........B)...	X@..f.	r.....	B...).g'..
.`E..a	2...c../...'...<..+....d.	.0r.H..So.
<........`.w.r..q..........5......8.....J.`.3..ii.		...=0..x..z(`2.).".........A.6...>...F..f..{G..<.E.^.
4. ...F.4P..8....5..1..t...v@*...Z.........5......(.<|..bsy.5....P.+....x.....4..	....:.m........|`.. %3....r...l..G8..*..{."..X..EQZ.>....C.La.;{".......c.t`..@;3t..h.;.$ .<Lmado#
a..%&q...\.Gx..D...x..1.G2.0.....9.......F......k..K\b.=...\`.....x.A:&\wTx1.<@A.rpo'...s0..; ..o...pE..A.;...< .$
...0..5x..;R..u.....A!.pLU.....A.\A.I....^B..0.. [.....p.mP..p~..ft... hl 
....0..(	'..!..|3...n...3@y..e..
.0.]frN..>p.j`.'Py5.s1P...|..	O@......	..s.X.D.zf&
.F.;....
..s10.D8..wqH..G ..p..gy......bh.U(
..]_.....=0.`8.1....f..g......[..&py1....z.`f4.	.@.g.	..	vW...8..X....xV...........p	o(...z.0.......W..k0y&..D8.....`.<.........7.. ....1........>..a..&.....9.
..D.......`.. 
}..aX..(g.p.;..M.	..
v..DX....Np.e .
x..@.w..9..QX..8..H.......j..o .=........[....5.8|.........9p.e@.p......@.5..m0.5@.(@Kvx........p..... 	.. 
.......@c......7h.=.Z....0,.@...
.f.....gr;..i..k..2`
a.	I.
. 	17.F)....g.....z`.F.p'p.j .% .. 	&'.? .]0..0....h..Q..rX.a6....
P..@	^..0...p....?@..e.K......0..`u..
...ky=...M`..i.......!.........`
. ..@.D..I...y.>y.....`....o......7....Zp.U.["!..@	h..e...4.....-.....y....|<..L%..........\%....!.....;c ...A \.p......WW0,..".`s...i..........
..[a..M...z...W.P..@..`.c..l...Pf9...`...`	. .`.W 
....P.]`
.R....2.
.p..P.U..Dj
A .q"%..._p....Dz.Gzn.........Ez.I
.2........*`.GU....* .\`.......".
..h'`.H...I..A...p.=.3....z......
#0bt'
q.a..........	..z........0.B`j.6.x..~@RW........	!>c..#pcO`jpY..._{sn...J...@.#..n0.. ...~.g{...+.........@.....@.V.;......!b...Oip.V.....A.....)."*... 
w...0!..........q.	..
!..1.
...;
IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 270
E..B..@.@.G............`.9...........6.....
.......mHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"68-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 68
Date: Sun, 08 Oct 2006 23:34:05 GMT

GIF89a...............................!.......,..........	...
...FH.;
IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 311
E..k6y@.@..............[..\Z.~/......_.....
.......mHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"107-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 107
Date: Sun, 08 Oct 2006 23:34:05 GMT

GIF89a.......DDD.........XXX^^^NNNZZZbbblllhhhrrrnnn.........!.....
.,...........p..j%.L.A.......&....YM..;
IP 127.0.0.1.8080 > 127.0.0.1.47457: tcp 450
E... ,@.@..............a.,.F.w
............
.......mHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"246-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 246
Date: Sun, 08 Oct 2006 23:34:05 GMT

GIF89a
...............xxx...............yyy..................................................................!.......,....
.....s`.(."..8$...-A@.<.2t........	
...r.\"...s:...XkV...
...@....t..n.'.........c..;..K=L..
..Q....X........a`b].,,+	.	!.;
IP 127.0.0.1.8080 > 127.0.0.1.47458: tcp 767
E..3?$@.@..............b.
.N.........(.....
.......mHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"563-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 563
Date: Sun, 08 Oct 2006 23:34:05 GMT

GIF89a	......xxx.........yyy............................................................................................................................................................................................................}}~..................................................................................................................................................................!.......,....	.......2..);@.......IH5.....4<:.....%3.$.1?K..
....8..	..>0/....!
.
.+........9.....D....*...76(. ....A.,.G.......'.......&
#EB.J"C.=..p!0....F..... ..;
IP 127.0.0.1.8080 > 127.0.0.1.47459: tcp 408
E...(]@.@..............c.......W...........
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"204-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 204
Date: Sun, 08 Oct 2006 23:34:05 GMT

GIF89a
......................................................................................................!.......,....
.....I.1..`.I..	.....p..x .7..>_ 8.....q.\...(4.x......`0..."I>.w....>8.m..T....;
IP 127.0.0.1.58204 > 127.0.0.1.8080: tcp 228
E.....@.@..?.........\...~`j...Z...'.......
...!....PROPFIND /cosmo/home/andrew HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
Content-Type: text/xml; charset=utf-8
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080
Content-Length: 129
Depth: infinity


IP 127.0.0.1.58204 > 127.0.0.1.8080: tcp 129
E.....@.@............\...~aN...Z...'.......
...$...!<?xml version="1.0" encoding="utf-8" ?><D:propfind xmlns:D="DAV:"><D:prop><D:resourcetype/><D:displayname/></D:prop></D:propfind>
IP 127.0.0.1.8080 > 127.0.0.1.58204: tcp 656
E.....@.@..Z...........\...Z.~a....T.......
...+...$HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 480
Date: Sun, 08 Oct 2006 23:34:05 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/</D:href>
        <D:propstat>
            <D:prop>
                <D:displayname>andrew</D:displayname>
                <D:resourcetype>
                    <D:collection/>
                </D:resourcetype>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.8080 > 127.0.0.1.47460: tcp 163
E...'e@.@..............d...................
.../....HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=utf-8
Content-Length: 20
Date: Sun, 08 Oct 2006 23:34:05 GMT

{"result":[],"id":2}
IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 575
E..s.N@.@..4........._....t..(.o.....h.....
...4....POST /scooby/JSON-RPC HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Content-Type: text/plain
Content-Length: 83
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598
Pragma: no-cache
Cache-Control: no-cache


IP 127.0.0.1.47455 > 127.0.0.1.8080: tcp 83
E....O@.@............_....w
.(.o.....{.....
...4...4{"id": 3, "method": "scoobyService.createCalendar", "params": ["Scooby", "Scooby"]}
IP 127.0.0.1.58204 > 127.0.0.1.8080: tcp 181
E.....@.@..k.........\...~a........=.......
...A...+MKCALENDAR /cosmo/home/andrew/Scooby HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080
Content-Length: 218


IP 127.0.0.1.58204 > 127.0.0.1.8080: tcp 218
E.....@.@..E.........\...~b........=.......
...B...+<?xml version="1.0"?>
<C:mkcalendar xmlns:C="urn:ietf:params:xml:ns:caldav">
    <D:set xmlns:D="DAV:">
        <D:prop>
            <D:displayname>Scooby</D:displayname>
        </D:prop>
    </D:set>
</C:mkcalendar>

IP 127.0.0.1.8080 > 127.0.0.1.58204: tcp 131
E.....@.@.!e...........\.....~c^...v.......
.......AHTTP/1.1 201 Created
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Length: 0
Date: Sun, 08 Oct 2006 23:34:05 GMT


IP 127.0.0.1.8080 > 127.0.0.1.47455: tcp 165
E...N2@.@.............._.(.o..w]...........
.......4HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=utf-8
Content-Length: 22
Date: Sun, 08 Oct 2006 23:34:05 GMT

{"result":null,"id":3}
IP 127.0.0.1.47451 > 127.0.0.1.8080: tcp 495
E..#$.@.@..&.........[...~/...]............
........GET /scooby/templates/default/images/block_gradient_unsel.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598


IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 575
E..s..@.@.r..........`.......9.......h.....
........POST /scooby/JSON-RPC HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Content-Type: text/plain
Content-Length: 98
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598
Pragma: no-cache
Cache-Control: no-cache


IP 127.0.0.1.47456 > 127.0.0.1.8080: tcp 98
E.....@.@.t..........`....!..9.............
........{"id": 4, "method": "scoobyService.getEvents", "params": ["Scooby", 1160132400000, 1160909999000]}
IP 127.0.0.1.8080 > 127.0.0.1.47451: tcp 1296
E..D6{@.@..7...........[..]..~0......9.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"1090-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 1090
Date: Sun, 08 Oct 2006 23:34:05 GMT

GIF89ad.......M..~..|..K..J..x..{..}..w..v..y..d..U..Q..X..f..m..O..Z..t..}..l..o..N..[..h..c..f..p..p..r..Y..Q..X..z..Z..R..L..s..a..M..z..y..V..O..n.._..]..\..b..o..e..R..g..P..t..u..l..h..K..s..r..W..S..k..j..[..a..S..`..c..J..j..N..i..|..T..d..U.._..^..]..q..m..L..T..I..W..P......................................................................................................................!.......,....d.......V..G..;%T..(I..,6X
 4$D?LUN.+W>!..#.B.0/QPO.E'C1F.M.3..5.:JHA@9.S.-2...R=.<&.78		....

*)"....K.................................t...L.1c..1s.M.5k..q..N.9s..%Z..Q.I..e...S.Q.N.Z..U.Y.n....W.a..%[..Y.i..e...[.q..eL....w".......}....H....:..l.....F-&=.Q].v ....g.^.|,...'S`..8..\..a..D)......u....G...{*........6
.L.....B%.....c.N%..j..U.z.z..U.`..
.U.X...M#..Z.n..y.v..5t.....M.8m...!..J..U.x.r...4`..	.E..-k.l...:y...w]..^[}..W.
6.i....bk-..[....y..&.{..v]|.
..}......1g...E.^..Y....mW....wbl....m..G.n... p...`.q..7!...W.s.
..u.!.[v.5H.w.I..rQ...z..6]{....|....}....x...a.B^...3"...$....xZ.".U..a.Fn	..72........hj.".Y.	b.J~I..O....,.i....x$.q..c.b.Y..?R...0....!..$.....;
IP 127.0.0.1.58204 > 127.0.0.1.8080: tcp 187
E.....@.@..b.........\...~c^...m...S.......
...C....REPORT /cosmo/home/andrew/Scooby HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080
Content-Length: 412
Depth: 1


IP 127.0.0.1.58204 > 127.0.0.1.8080: tcp 412
E.....@.@............\...~d....m...S.......
...C...C<?xml version="1.0"?>
<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
    <D:prop xmlns:D="DAV:">
        <D:getetag/>
    </D:prop>
    <C:filter>
        <C:comp-filter name="VCALENDAR">
            <C:comp-filter name="VEVENT">
                <C:time-range end="20061015T105959Z" start="20061006T110000Z"/>
            </C:comp-filter>
        </C:comp-filter>
    </C:filter>
</C:calendar-query>

IP 127.0.0.1.8080 > 127.0.0.1.58204: tcp 246
E..*..@.@. ............\...m.~e............
.......CHTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 71
Date: Sun, 08 Oct 2006 23:34:07 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:"/>

IP 127.0.0.1.8080 > 127.0.0.1.47456: tcp 163
E.....@.@.Gz...........`.9....!y...........
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=utf-8
Content-Length: 20
Date: Sun, 08 Oct 2006 23:34:07 GMT

{"result":[],"id":4}
IP 127.0.0.1.47454 > 127.0.0.1.8080: tcp 493
E..!..@.@.QD.........^.../H...B............
...M....GET /scooby/templates/default/images/block_gradient_sel.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598


IP 127.0.0.1.8080 > 127.0.0.1.47454: tcp 1296
E..Du.@.@..............^..B../J......9.....
...O...MHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"1090-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 1090
Date: Sun, 08 Oct 2006 23:34:07 GMT

GIF89ad.......X..-t.Z..2}.,r./w.S..6..0z.Z...v.W..Y..P..@..J..Y..Q..3~.R...u.A..?..D..M..D..G../x.5..L..L..5..?..T..8..;..E..1|.V..<..1{.=..U..:..I..B..N..S..>..H..;..P..H..Q..R..U..>..5..4..G..C..T..-s.4..9..B..F..3..J..F..V..8..9..C..K..0y.K..N..V..,s.O..X..<..7..M..@..:............................................................................................................................!.......,....d.........O>...

...K..(%...C:?9....S"GH@+V2#R')08. .U.-AI<..$BE.;41,D.JL...T.MP
3.5.6../=!7*N&F..Q.......		............................t...L.1c..1s.M.5k..q..N.9s..%Z..Q.I..e...S.Q.N.Z..U.Y.n....W.a..%[..Y.i..e...[.q..eL....w".......}....H....:..l.....F-&=.Q].v ....g.^.|,...'S`..8..\..a..D)......u....G...{*........6
.L.....B%.....c.N%..j..U.z.z..U.`..
.U.X...M#..Z.n..y.v..5t.....M.8m...!..J..U.x.r...4`..	.E..-k.l...:y...w]..^[}..W.
6.i....bk-..[....y..&.{..v]|.
..}......1g...E.^..Y....mW....wbl....m..G.n... p...`.q..7!...W.s.
..u.!.[v.5H.w.I..rQ...z..6]{....|....}....x...a.B^...3"...$....xZ.".U..a.Fn	..72........hj.".Y.	b.J~I..O....,.i....x$.q..c.b.Y..?R...0....!..$.	...;
IP 127.0.0.1.47457 > 127.0.0.1.8080: tcp 576
E..t..@.@.F..........a...w
..,.......i.....
........POST /scooby/JSON-RPC HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Content-Type: text/plain
Content-Length: 578
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598
Pragma: no-cache
Cache-Control: no-cache


IP 127.0.0.1.47457 > 127.0.0.1.8080: tcp 578
E..v..@.@.F..........a...w...,.......k.....
........{"id": 5, "method": "scoobyService.saveEvent", "params": ["Scooby", {"id": null, "title": "Welcome to Scooby!", "description": "Welcome to Scooby!", "start": {"year": 2006, "month": 9, "date": 11, "hours": "9", "minutes": "00", "seconds": 0, "timezone": null, "utc": false}, "end": {"year": 2006, "month": 9, "date": 11, "hours": 10, "minutes": 0, "seconds": 0, "timezone": null, "utc": false}, "allDay": false, "pointInTime": false, "anyTime": false, "recurrenceRule": null, "status": null, "masterEvent": false, "instance": false, "javaClass": "org.osaf.scooby.model.Event"}]}
IP 127.0.0.1.58204 > 127.0.0.1.8080: tcp 233
E.....@.@..1.........\...~e....c...i.......
........PUT /cosmo/home/andrew/Scooby/2c329cf7-59d9-42c3-8e44-11cce91c0f33.ics HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
If-None-Match: *
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080
Content-Length: 284


IP 127.0.0.1.58204 > 127.0.0.1.8080: tcp 284
E..P..@.@............\...~f....c...i.D.....
........BEGIN:VCALENDAR
PRODID:-//OSAF//NONSGML Scooby Server//EN
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
DESCRIPTION:Welcome to Scooby!
SUMMARY:Welcome to Scooby!
DTSTART:20061011T090000
DTEND:20061011T100000
UID:2c329cf7-59d9-42c3-8e44-11cce91c0f33
END:VEVENT
END:VCALENDAR

IP 127.0.0.1.8080 > 127.0.0.1.58204: tcp 158
E.....@.@.!D...........\...c.~g............
........HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
ETag: "284-1160350448095"
Content-Length: 0
Date: Sun, 08 Oct 2006 23:34:08 GMT


IP 127.0.0.1.8080 > 127.0.0.1.47457: tcp 199
E... /@.@..............a.,...w.M...5.......
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=utf-8
Content-Length: 56
Date: Sun, 08 Oct 2006 23:34:08 GMT

{"result":"2c329cf7-59d9-42c3-8e44-11cce91c0f33","id":5}
IP 127.0.0.1.51832 > 127.0.0.1.8080: tcp 492
E.. #v@.@..`.........x...qP................
..s...s.GET /scooby/templates/default/images/button_center_lit.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598


IP 127.0.0.1.51831 > 127.0.0.1.8080: tcp 490
E...M.@.@............w....>P.pL............
..s...s.GET /scooby/templates/default/images/button_left_lit.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598


IP 127.0.0.1.51830 > 127.0.0.1.8080: tcp 491
E...5.@.@..E.........v....F0...g...........
..s...s.GET /scooby/templates/default/images/button_right_lit.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598


IP 127.0.0.1.44817 > 127.0.0.1.8080: tcp 148
E....b@.@.f..............&...3W............
..s...s.HEAD /cosmo/home/andrew/ HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080


IP 127.0.0.1.8080 > 127.0.0.1.44817: tcp 135
E...$.@.@..^.............3W..&.T...........
..s...s.HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Transfer-Encoding: chunked
Date: Sun, 08 Oct 2006 23:36:49 GMT


IP 127.0.0.1.44817 > 127.0.0.1.8080: tcp 221
E....d@.@.f..............&.T.3W............
..s...s.PROPFIND /cosmo/home/andrew HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
Content-Type: text/xml; charset=utf-8
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080
Content-Length: 156
Depth: 0


IP 127.0.0.1.44817 > 127.0.0.1.8080: tcp 156
E....e@.@.f..............&.1.3W............
..s...s.<?xml version="1.0" encoding="utf-8" ?><D:propfind xmlns:D="DAV:"><D:prop><ZZ:scoobyprefs xmlns:ZZ="http://osafoundation.org/scooby"/></D:prop></D:propfind>
IP 127.0.0.1.8080 > 127.0.0.1.44817: tcp 697
E...$.@.@..+.............3W..&.....3.......
..t...s.HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 521
Date: Sun, 08 Oct 2006 23:36:49 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/</D:href>
        <D:propstat>
            <D:prop/>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
        <D:propstat>
            <D:prop>
                <ZZ:scoobyprefs xmlns:ZZ="http://osafoundation.org/scooby"/>
            </D:prop>
            <D:status>HTTP/1.1 404 Not Found</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.8080 > 127.0.0.1.51831: tcp 1180
E...?.@.@..~...........w.pL...@:...........
..t!..s.HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"976-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 976
Date: Sun, 08 Oct 2006 23:36:49 GMT

GIF89a
....A.xxx...........................yyy........................................................................................................................}}~....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................!.....A.,....
...............A
.[...../:4.H."..
.I.(.........o.I
&.QX...?.0c.|y.C..8...i....:
..I....H...0....LW..@U....z.......Z.v] BE..%.1Xp@..
o+...`..	.&.50Rd..=0.d	..........D..H.....@C....;
IP 127.0.0.1.40443 > 127.0.0.1.8080: tcp 148
E...n.@.@..(..................lg...........
..t&..s.HEAD /cosmo/home/andrew/ HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080


IP 127.0.0.1.8080 > 127.0.0.1.40443: tcp 135
E.....@.@.................lg...............
..t-..t&HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Transfer-Encoding: chunked
Date: Sun, 08 Oct 2006 23:36:49 GMT


IP 127.0.0.1.40443 > 127.0.0.1.8080: tcp 221
E...n.@.@.....................l............
..t6..t-PROPFIND /cosmo/home/andrew HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
Content-Type: text/xml; charset=utf-8
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080
Content-Length: 156
Depth: 0


IP 127.0.0.1.40443 > 127.0.0.1.8080: tcp 156
E...n.@.@..................l..l............
..t6..t-<?xml version="1.0" encoding="utf-8" ?><D:propfind xmlns:D="DAV:"><D:prop><ZZ:scoobyprefs xmlns:ZZ="http://osafoundation.org/scooby"/></D:prop></D:propfind>
IP 127.0.0.1.8080 > 127.0.0.1.40443: tcp 697
E.....@.@..i..............l... ....3.......
..tK..t6HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 521
Date: Sun, 08 Oct 2006 23:36:49 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/</D:href>
        <D:propstat>
            <D:prop/>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
        <D:propstat>
            <D:prop>
                <ZZ:scoobyprefs xmlns:ZZ="http://osafoundation.org/scooby"/>
            </D:prop>
            <D:status>HTTP/1.1 404 Not Found</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.8080 > 127.0.0.1.51832: tcp 605
E.....@.@..c...........x.....qR............
..t_..s.HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"401-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 401
Date: Sun, 08 Oct 2006 23:36:49 GMT

GIF89a.................................................nln...........................nnn...........................nnp.......................................................................................!.......,...........@...q....cQh2...4.."...v. x..0x..F...:.............~.............................................
................%..$.....

......
	.X......otx..|.......!...........O......A.;
IP 127.0.0.1.8080 > 127.0.0.1.51830: tcp 831
E..s..@.@..............v...g..H......h.....
..t...s.HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"627-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 627
Date: Sun, 08 Oct 2006 23:36:49 GMT

GIF89a
.........................................................///........................nnn..................lln............hhh.......................... .........BBB..................nln..............................???...............ccc...............VVV...lll...ywy..............................................................................................................................!.......,....
.......!:..!U6..
...../EF
....'=3......PS..9.W7X.@......+......V....O5$......<"T............
.
......R.....#	..	.N?....%....H..*....DL4 ..."....6tP.B..$.X4ia......h..1..Z....@......|...B.,C..A!...i.<L[."..F..EX..A .;
IP 127.0.0.1.51834 > 127.0.0.1.8080: tcp 99
E.....@.@............z...`Z..>B,...........
...p...oOPTIONS /cosmo/users/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
User-Agent: Evolution/1.8.0


IP 127.0.0.1.8080 > 127.0.0.1.51834: tcp 179
E....]@.@..............z.>B,.`[R...........
...t...pHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS
Content-Length: 0
Date: Sun, 08 Oct 2006 23:37:23 GMT


IP 127.0.0.1.51834 > 127.0.0.1.8080: tcp 99
E.....@.@............z...`[R.>B............
..;....tOPTIONS /cosmo/users/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
User-Agent: Evolution/1.8.0


IP 127.0.0.1.8080 > 127.0.0.1.51834: tcp 179
E....^@.@..............z.>B..`[............
..;...;.HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS
Content-Length: 0
Date: Sun, 08 Oct 2006 23:37:40 GMT


IP 127.0.0.1.51837 > 127.0.0.1.8080: tcp 493
E..!.s@.@. b.........}...Q...Cm............
........GET /scooby/templates/default/images/button_left_lit_sm.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598


IP 127.0.0.1.51836 > 127.0.0.1.8080: tcp 495
E..#0l@.@.
g.........|...,"....%...........
........GET /scooby/templates/default/images/button_center_lit_sm.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598


IP 127.0.0.1.51835 > 127.0.0.1.8080: tcp 494
E..".=@.@.R..........{...B...Ph............
........GET /scooby/templates/default/images/button_right_lit_sm.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598


IP 127.0.0.1.8080 > 127.0.0.1.51837: tcp 551
E..[.K@.@..O...........}.Cm..Q
......P.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"347-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 347
Date: Sun, 08 Oct 2006 23:38:35 GMT

GIF89a	....:..............................................nln///......jjj.........hhh......???...XXX........ ......nnp.........ywy.................................---CCCljl.........ccc.....................!.....:.,....	.....x@...0.........
.lfk
..I.{E.d,.
......z......Bp....E.P..............

	.9..)... 5...1
.....!.'.j$k..../..u+.*%.{.:78..QOA.;
IP 127.0.0.1.8080 > 127.0.0.1.51836: tcp 557
E..a..@.@..............|...%.,$y.....V.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"353-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 353
Date: Sun, 08 Oct 2006 23:38:35 GMT

GIF89a
....................................nln..............................nnn........................nnp...................................................................................................!.......,....
.....~..B.Q...!!.$4......N.R....r.....	....z.^....|.0..v.......~........................................

k...
v...........|NLLK	.	A.;
IP 127.0.0.1.8080 > 127.0.0.1.51835: tcp 767
E..3z.@.@..............{.Ph..B.......(.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"563-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 563
Date: Sun, 08 Oct 2006 23:38:35 GMT

GIF89a	.............................................///.........nnn...........................ywyVVV.....................hhh....................................nln.....................lln......ccc..... ..................lll......???BBB..................................................................................................................................................................!.......,....	.......1..9.........>H/.....%6I.....@E.(.:"8.'
....B=.....2&.....4.....
.+....#.....).
..,...0F..5........D.......*......;
C.A.37G?$		-.).....F...x. ..;
IP 127.0.0.1.51837 > 127.0.0.1.8080: tcp 576
E..t.u@.@. 
.........}...Q
..Co......i.....
........POST /scooby/JSON-RPC HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Content-Type: text/plain
Content-Length: 110
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598
Pragma: no-cache
Cache-Control: no-cache


IP 127.0.0.1.51837 > 127.0.0.1.8080: tcp 110
E....v@.@.!..........}...Q...Co............
...*...*{"id": 6, "method": "scoobyService.removeEvent", "params": ["Scooby", "2c329cf7-59d9-42c3-8e44-11cce91c0f33"]}
IP 127.0.0.1.46849 > 127.0.0.1.8080: tcp 187
E.....@.@.r{.................._+...........
...@...=REPORT /cosmo/home/andrew/Scooby HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080
Content-Length: 535
Depth: 1


IP 127.0.0.1.46849 > 127.0.0.1.8080: tcp 535
E..K..@.@.q................p.._+.....@.....
...@...@<?xml version="1.0"?>
<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
    <D:prop xmlns:D="DAV:">
        <D:getetag/>
        <C:calendar-data/>
    </D:prop>
    <C:filter>
        <C:comp-filter name="VCALENDAR">
            <C:comp-filter name="VEVENT">
                <C:prop-filter name="UID">
                    <C:text-match caseless="no">2c329cf7-59d9-42c3-8e44-11cce91c0f33</C:text-match>
                </C:prop-filter>
            </C:comp-filter>
        </C:comp-filter>
    </C:filter>
</C:calendar-query>

IP 127.0.0.1.8080 > 127.0.0.1.46849: tcp 1029
E..9F.@.@................._+.......".......
...]...@HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 853
Date: Sun, 08 Oct 2006 23:38:37 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/2c329cf7-59d9-42c3-8e44-11cce91c0f33.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"284-1160350448095"</D:getetag>
                <C:calendar-data xmlns:C="urn:ietf:params:xml:ns:caldav">BEGIN:VCALENDAR&#xd;
PRODID:-//OSAF//NONSGML Scooby Server//EN&#xd;
VERSION:2.0&#xd;
CALSCALE:GREGORIAN&#xd;
BEGIN:VEVENT&#xd;
DESCRIPTION:Welcome to Scooby!&#xd;
SUMMARY:Welcome to Scooby!&#xd;
DTSTART:20061011T090000&#xd;
DTEND:20061011T100000&#xd;
UID:2c329cf7-59d9-42c3-8e44-11cce91c0f33&#xd;
END:VEVENT&#xd;
END:VCALENDAR&#xd;
</C:calendar-data>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.46849 > 127.0.0.1.8080: tcp 197
E.....@.@.rn..................c0...!.......
.......]DELETE /cosmo/home/andrew/Scooby/2c329cf7-59d9-42c3-8e44-11cce91c0f33.ics HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080


IP 127.0.0.1.8080 > 127.0.0.1.46849: tcp 115
E...F.@.@..4..............c0...L...3.......
...1....HTTP/1.1 204 No Content
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Date: Sun, 08 Oct 2006 23:38:37 GMT


IP 127.0.0.1.8080 > 127.0.0.1.51837: tcp 165
E....N@.@..............}.Co..Q
1...#.......
...2...*HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=utf-8
Content-Length: 22
Date: Sun, 08 Oct 2006 23:38:37 GMT

{"result":null,"id":6}
IP 127.0.0.1.51837 > 127.0.0.1.8080: tcp 491
E....x@.@. _.........}...Q
1.Cpz...#.......
...A...2GET /scooby/templates/default/images/button_right_dim.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598


IP 127.0.0.1.51836 > 127.0.0.1.8080: tcp 490
E...0n@.@.
j.........|...,$y...R...........
...A....GET /scooby/templates/default/images/button_left_dim.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598


IP 127.0.0.1.51835 > 127.0.0.1.8080: tcp 492
E.. .?@.@.R..........{...B...Pk............
...A....GET /scooby/templates/default/images/button_center_dim.gif HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/scooby/main.page
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598


IP 127.0.0.1.8080 > 127.0.0.1.51837: tcp 831
E..s.P@.@..2...........}.Cpz.Q.....5.h.....
...H...AHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"627-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 627
Date: Sun, 08 Oct 2006 23:38:37 GMT

GIF89a
.........xxx............yyy.............................................................................................................................................}}~...........................................................................................................................................................................................................................!.......,....
........H...=" .
.....6,?
....>.@......R;..5.OWB.%......F......#....T+K......LJD....
......
.........8 ....$	..	.)2....I....9
.4....YEG*...!@...:lP.D...!LT...G....h...
.Z....`......p......-.....I.i.<Lc0.

F..A.baB .;
IP 127.0.0.1.8080 > 127.0.0.1.51835: tcp 673
E...z.@.@../...........{.Pk..B.....".......
...I...AHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"469-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 469
Date: Sun, 08 Oct 2006 23:38:37 GMT

GIF89a...............................xxx...yyy...............................................................................................................................................................!.......,...........@.b(\..E.p.P .Ng.	."..l...b.\.`.%.................q~<...c..........................................................$#....&.....	..	%....!.. ........................*...........+..
..
.........>...B..."...B.81....4l..@#.	 7...KV+Z([E....../....;
IP 127.0.0.1.8080 > 127.0.0.1.51836: tcp 811
E.._..@.@..............|...R.,&c...".T.....
...J...AHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/"607-1149667320000"
Last-Modified: Wed, 07 Jun 2006 08:02:00 GMT
Content-Type: image/gif
Content-Length: 607
Date: Sun, 08 Oct 2006 23:38:37 GMT

GIF89a
....A.xxx...........................yyy........................................................................................................................}}~....................................................................................................................................................................................................................................!.....A.,....
.......AA=.&1.>..A.!3..
../9(....-....+%2...4........
...............
.............
...........		.........	....
....
7.....?"
.
.......<P..o...5 @..A....l.0..T...n.......#...6....*.=..D....$A....;
IP 127.0.0.1.51834 > 127.0.0.1.8080: tcp 99
E.....@.@............z...`[..>C....".......
...{..&.OPTIONS /cosmo/users/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
User-Agent: Evolution/1.8.0


IP 127.0.0.1.51839 > 127.0.0.1.8080: tcp 99
E...(.@.@.....................h@...........
...|...{OPTIONS /cosmo/users/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
User-Agent: Evolution/1.8.0


IP 127.0.0.1.8080 > 127.0.0.1.51839: tcp 179
E...n=@.@.................h@../............
...~...|HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS
Content-Length: 0
Date: Sun, 08 Oct 2006 23:39:37 GMT


IP 127.0.0.1.51840 > 127.0.0.1.8080: tcp 98
E...p.@.@................z.....M...........
..^Q..^QOPTIONS /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
User-Agent: Evolution/1.8.0


IP 127.0.0.1.8080 > 127.0.0.1.51840: tcp 1416
E.....@.@..................M.z/-...........
..^U..^QHTTP/1.1 401 An Authentication object was not found in the SecurityContext
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
WWW-Authenticate: Basic realm="Cosmo Sharing Server"
Content-Type: text/html;charset=utf-8
Content-Length: 1135
Date: Sun, 08 Oct 2006 23:40:00 GMT

<html><head><title>Apache Tomcat/5.5.9 - Error report</title><style><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 401 - An Authentication object was not found in the SecurityContext</h1><HR size="1" noshade="noshade"><p><b>type</b> Status report</p><p><b>message</b> <u>An Authentication object was not found in the SecurityContext</u></p><p><b>description</b> <u>This request requires HTTP authentication (An Authentication object was not found in the SecurityContext).</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/5.5.9</h3></body></html>
IP 127.0.0.1.51840 > 127.0.0.1.8080: tcp 141
E...p.@.@................z/-.......-.......
..^V..^UOPTIONS /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0


IP 127.0.0.1.8080 > 127.0.0.1.51840: tcp 293
E..Y..@.@..~.................z/......M.....
..^b..^VHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
DAV: 1, calendar-access, ticket
Allow: OPTIONS, GET, HEAD, POST, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, PUT, DELETE, MOVE, MKTICKET, DELTICKET
MS-Author-Via: DAV
Content-Length: 0
Date: Sun, 08 Oct 2006 23:40:00 GMT


IP 127.0.0.1.51840 > 127.0.0.1.8080: tcp 195
E...p.@.@..].............z/........Y.......
..^...^bREPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.51840 > 127.0.0.1.8080: tcp 301
E..ap.@.@................z0}.......Y.U.....
..^...^b<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.51840: tcp 246
E..*..@.@....................z1....3.......
..^...^.HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 71
Date: Sun, 08 Oct 2006 23:40:00 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:"/>

IP 127.0.0.1.51840 > 127.0.0.1.8080: tcp 195
E...p.@.@..Z.............z1................
..IE..I9REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.51842 > 127.0.0.1.8080: tcp 195
E...V0@.@..................l...............
..IF..IEREPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.51842 > 127.0.0.1.8080: tcp 301
E..aV1@.@..c.............. /.........U.....
..IM..IF<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.51842: tcp 246
E..*..@.@.c...................!\...".......
..Ic..IMHTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 71
Date: Sun, 08 Oct 2006 23:41:00 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:"/>

IP 127.0.0.1.51842 > 127.0.0.1.8080: tcp 195
E...V3@.@.................!\...............
..3...3.REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.51566 > 127.0.0.1.8080: tcp 195
E...>O@.@............n...-
................
..3...3.REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.51566 > 127.0.0.1.8080: tcp 301
E..a>P@.@..D.........n...-.F.........U.....
..3...3.<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.51566: tcp 246
E..*.}@.@..N...........n.....-.s...".......
..4...3.HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 71
Date: Sun, 08 Oct 2006 23:42:00 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:"/>

IP 127.0.0.1.51566 > 127.0.0.1.8080: tcp 195
E...>R@.@............n...-.s...............
...p...fREPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.51569 > 127.0.0.1.8080: tcp 195
E....V@.@............q.....%...S...........
...p...pREPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.51569 > 127.0.0.1.8080: tcp 301
E..a.W@.@..=.........q.........S.....U.....
...x...p<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.51569: tcp 246
E..*.%@.@.;............q...S.......".......
.......xHTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 71
Date: Sun, 08 Oct 2006 23:43:00 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:"/>

IP 127.0.0.1.51570 > 127.0.0.1.8080: tcp 575
E..s.[@.@..'.........r.......+l'.....h.....
..m...m.POST /scooby/JSON-RPC HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Content-Type: text/plain
Content-Length: 98
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598
Pragma: no-cache
Cache-Control: no-cache


IP 127.0.0.1.51570 > 127.0.0.1.8080: tcp 98
E....\@.@............r.......+l'...........
..m...m.{"id": 7, "method": "scoobyService.getEvents", "params": ["Scooby", 1159531200000, 1160305199000]}
IP 127.0.0.1.59620 > 127.0.0.1.8080: tcp 148
E.....@.@..................o.mC............
..m...m.HEAD /cosmo/home/andrew/ HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080


IP 127.0.0.1.8080 > 127.0.0.1.59620: tcp 135
E.....@.@../.............mC................
..m...m.HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Transfer-Encoding: chunked
Date: Sun, 08 Oct 2006 23:43:20 GMT


IP 127.0.0.1.59620 > 127.0.0.1.8080: tcp 221
E.....@.@..].................mDm...........
..m...m.PROPFIND /cosmo/home/andrew HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
Content-Type: text/xml; charset=utf-8
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080
Content-Length: 156
Depth: 0


IP 127.0.0.1.59620 > 127.0.0.1.8080: tcp 156
E.....@.@....................mDm...........
..m...m.<?xml version="1.0" encoding="utf-8" ?><D:propfind xmlns:D="DAV:"><D:prop><ZZ:scoobyprefs xmlns:ZZ="http://osafoundation.org/scooby"/></D:prop></D:propfind>
IP 127.0.0.1.8080 > 127.0.0.1.59620: tcp 697
E.....@.@................mDm...|...3.......
..nC..m.HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 521
Date: Sun, 08 Oct 2006 23:43:20 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/</D:href>
        <D:propstat>
            <D:prop/>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
        <D:propstat>
            <D:prop>
                <ZZ:scoobyprefs xmlns:ZZ="http://osafoundation.org/scooby"/>
            </D:prop>
            <D:status>HTTP/1.1 404 Not Found</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.59620 > 127.0.0.1.8080: tcp 187
E.....@.@..}...............|.mG&...'.......
..nT..nCREPORT /cosmo/home/andrew/Scooby HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080
Content-Length: 412
Depth: 1


IP 127.0.0.1.59620 > 127.0.0.1.8080: tcp 412
E.....@.@..................7.mG&...'.......
..nU..nT<?xml version="1.0"?>
<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
    <D:prop xmlns:D="DAV:">
        <D:getetag/>
    </D:prop>
    <C:filter>
        <C:comp-filter name="VCALENDAR">
            <C:comp-filter name="VEVENT">
                <C:time-range end="20061008T105959Z" start="20060929T120000Z"/>
            </C:comp-filter>
        </C:comp-filter>
    </C:filter>
</C:calendar-query>

IP 127.0.0.1.8080 > 127.0.0.1.59620: tcp 246
E..*..@.@.
..............mG&.......T.......
..n...nUHTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 71
Date: Sun, 08 Oct 2006 23:43:20 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:"/>

IP 127.0.0.1.8080 > 127.0.0.1.51570: tcp 163
E.....@.@.:N...........r.+l'...S...........
..n...m.HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=utf-8
Content-Length: 20
Date: Sun, 08 Oct 2006 23:43:20 GMT

{"result":[],"id":7}
IP 127.0.0.1.51570 > 127.0.0.1.8080: tcp 575
E..s.^@.@..$.........r.....S.+l......h.....
..uO..n.POST /scooby/JSON-RPC HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Content-Type: text/plain
Content-Length: 98
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598
Pragma: no-cache
Cache-Control: no-cache


IP 127.0.0.1.51570 > 127.0.0.1.8080: tcp 98
E...._@.@............r.......+l............
..uO..uO{"id": 8, "method": "scoobyService.getEvents", "params": ["Scooby", 1160132400000, 1160909999000]}
IP 127.0.0.1.59620 > 127.0.0.1.8080: tcp 187
E.....@.@..z.................mH....=.......
..u\..n.REPORT /cosmo/home/andrew/Scooby HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080
Content-Length: 412
Depth: 1


IP 127.0.0.1.59620 > 127.0.0.1.8080: tcp 412
E.....@.@....................mH....=.......
..u\..u\<?xml version="1.0"?>
<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
    <D:prop xmlns:D="DAV:">
        <D:getetag/>
    </D:prop>
    <C:filter>
        <C:comp-filter name="VCALENDAR">
            <C:comp-filter name="VEVENT">
                <C:time-range end="20061015T105959Z" start="20061006T110000Z"/>
            </C:comp-filter>
        </C:comp-filter>
    </C:filter>
</C:calendar-query>

IP 127.0.0.1.8080 > 127.0.0.1.59620: tcp 246
E..*..@.@.
..............mH....*...v.......
..u...u\HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 71
Date: Sun, 08 Oct 2006 23:43:22 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:"/>

IP 127.0.0.1.8080 > 127.0.0.1.51570: tcp 163
E.....@.@.:K...........r.+l........$.......
..u...uOHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=utf-8
Content-Length: 20
Date: Sun, 08 Oct 2006 23:43:22 GMT

{"result":[],"id":8}
IP 127.0.0.1.51569 > 127.0.0.1.8080: tcp 195
E....Y@.@............q.........J...........
..	.....REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.51571 > 127.0.0.1.8080: tcp 195
E...1.@.@.
..........s....8....k...........
..	...	.REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.51571 > 127.0.0.1.8080: tcp 301
E..a1.@.@.	..........s....9....k.....U.....
..	...	.<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.51571: tcp 246
E..*.l@.@./`...........s...k..:....".......
..	...	.HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 71
Date: Sun, 08 Oct 2006 23:44:00 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:"/>

IP 127.0.0.1.51573 > 127.0.0.1.8080: tcp 576
E..t.N@.@..3.........u....$F.
.......i.....
...
...
POST /scooby/JSON-RPC HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Content-Type: text/plain
Content-Length: 554
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598
Pragma: no-cache
Cache-Control: no-cache


IP 127.0.0.1.51573 > 127.0.0.1.8080: tcp 554
E..^.O@.@..H.........u....&..
.......S.....
...
...
{"id": 9, "method": "scoobyService.saveEvent", "params": ["Scooby", {"id": null, "title": "New Event", "description": null, "start": {"year": 2006, "month": 9, "date": 10, "hours": "13", "minutes": "00", "seconds": 0, "timezone": null, "utc": false}, "end": {"year": 2006, "month": 9, "date": 10, "hours": 14, "minutes": 0, "seconds": 0, "timezone": null, "utc": false}, "allDay": false, "pointInTime": false, "anyTime": false, "recurrenceRule": null, "status": null, "masterEvent": false, "instance": false, "javaClass": "org.osaf.scooby.model.Event"}]}
IP 127.0.0.1.37994 > 127.0.0.1.8080: tcp 233
E....E@.@.5..........j.....^...............
........PUT /cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
If-None-Match: *
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080
Content-Length: 257


IP 127.0.0.1.37994 > 127.0.0.1.8080: tcp 257
E..5.F@.@.5{.........j.....G.........).....
........BEGIN:VCALENDAR
PRODID:-//OSAF//NONSGML Scooby Server//EN
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
DESCRIPTION:
SUMMARY:New Event
DTSTART:20061010T130000
DTEND:20061010T140000
UID:01c38ac1-3606-4310-890e-f39acee4e812
END:VEVENT
END:VCALENDAR

IP 127.0.0.1.8080 > 127.0.0.1.37994: tcp 158
E.....@.@.:_...........j.......H...".......
........HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
ETag: "257-1160351080470"
Content-Length: 0
Date: Sun, 08 Oct 2006 23:44:40 GMT


IP 127.0.0.1.8080 > 127.0.0.1.51573: tcp 199
E...C.@.@..N...........u.
....(....$.......
.......
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=utf-8
Content-Length: 56
Date: Sun, 08 Oct 2006 23:44:40 GMT

{"result":"01c38ac1-3606-4310-890e-f39acee4e812","id":9}
IP 127.0.0.1.51571 > 127.0.0.1.8080: tcp 195
E...1.@.@.
..........s....:....b...........
.......~REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.51574 > 127.0.0.1.8080: tcp 195
E...M.@.@............v...>...d t...........
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.51574 > 127.0.0.1.8080: tcp 301
E..aM.@.@..|.........v...>.`.d t.....U.....
........<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.51574: tcp 605
E.....@.@.fe...........v.d t.>.....".......
........HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 429
Date: Sun, 08 Oct 2006 23:45:00 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"257-1160351080470"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.51574 > 127.0.0.1.8080: tcp 177
E...M.@.@............v...>...d"............
........GET /cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics HTTP/1.1
Host: localhost:8080
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0


IP 127.0.0.1.8080 > 127.0.0.1.51574: tcp 487
E.....@.@.f............v.d"..>.>...3.......
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
ETag: "257-1160351080470"
Last-Modified: Sun, 08 Oct 2006 23:44:40 GMT
Content-Type: text/calendar
Content-Length: 257
Date: Sun, 08 Oct 2006 23:45:00 GMT

BEGIN:VCALENDAR
PRODID:-//OSAF//NONSGML Scooby Server//EN
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
DESCRIPTION:
SUMMARY:New Event
DTSTART:20061010T130000
DTEND:20061010T140000
UID:01c38ac1-3606-4310-890e-f39acee4e812
END:VEVENT
END:VCALENDAR

IP 127.0.0.1.51573 > 127.0.0.1.8080: tcp 576
E..t.Q@.@..0.........u....(..
.g.....i.....
..u-....POST /scooby/JSON-RPC HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
Content-Type: text/plain
Content-Length: 834
Cookie: JSESSIONID=E472942E4056EA912A7504A13680E6E8; username=andrew; inputTimestamp=1160350446598
Pragma: no-cache
Cache-Control: no-cache


IP 127.0.0.1.51573 > 127.0.0.1.8080: tcp 834
E..v.R@.@..-.........u....*..
.g.....k.....
..u-..u-{"id": 10, "method": "scoobyService.saveEvent", "params": ["Scooby", {"id": "01c38ac1-3606-4310-890e-f39acee4e812", "title": "A New Scooby Event", "description": "This is the description of the new scooby event that was added by Andrew McMillan from within Scooby.  I wonder if this gets to be wrapped at the right place?  I bet it does.  What happens when I run out of room in this text box though?", "start": {"year": 2006, "month": 9, "date": 10, "hours": 13, "minutes": 0, "seconds": 0, "timezone": null, "utc": false}, "end": {"year": 2006, "month": 9, "date": 10, "hours": 14, "minutes": 0, "seconds": 0, "timezone": null, "utc": false}, "allDay": false, "pointInTime": false, "anyTime": false, "recurrenceRule": null, "status": "CONFIRMED", "masterEvent": false, "instance": false, "javaClass": "org.osaf.scooby.model.Event"}]}
IP 127.0.0.1.37994 > 127.0.0.1.8080: tcp 195
E....H@.@.5..........j.....H...{...........
..u@....HEAD /cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080


IP 127.0.0.1.8080 > 127.0.0.1.37994: tcp 230
E.....@.@.:............j...{.......3.......
..uI..u@HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
ETag: "257-1160351080470"
Last-Modified: Sun, 08 Oct 2006 23:44:40 GMT
Content-Type: text/calendar
Content-Length: 257
Date: Sun, 08 Oct 2006 23:45:33 GMT


IP 127.0.0.1.37994 > 127.0.0.1.8080: tcp 195
E....J@.@.5..........j.........a...".......
..u]..uIHEAD /cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080


IP 127.0.0.1.8080 > 127.0.0.1.37994: tcp 230
E.....@.@.:............j...a.......C.......
..uf..u]HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
ETag: "257-1160351080470"
Last-Modified: Sun, 08 Oct 2006 23:44:40 GMT
Content-Type: text/calendar
Content-Length: 257
Date: Sun, 08 Oct 2006 23:45:33 GMT


IP 127.0.0.1.37994 > 127.0.0.1.8080: tcp 246
E..*.K@.@.5..........j.........G...3.......
..uk..ufPUT /cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics HTTP/1.1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
If-Match: "257-1160351080470"
User-Agent: Jakarta Commons-HttpClient/2.0.2
Host: localhost:8080
Content-Length: 529


IP 127.0.0.1.37994 > 127.0.0.1.8080: tcp 529
E..E.L@.@.4e.........j.........G...3.:.....
..uk..ufBEGIN:VCALENDAR
PRODID:-//OSAF//NONSGML Scooby Server//EN
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:01c38ac1-3606-4310-890e-f39acee4e812
DESCRIPTION:This is the description of the new scooby event that was adde
 d by Andrew McMillan from within Scooby.  I wonder if this gets to be wr
 apped at the right place?  I bet it does.  What happens when I run out o
 f room in this text box though?
SUMMARY:A New Scooby Event
STATUS:CONFIRMED
DTSTART:20061010T130000
DTEND:20061010T140000
END:VEVENT
END:VCALENDAR

IP 127.0.0.1.8080 > 127.0.0.1.37994: tcp 142
E.....@.@.:j...........j...G.......e.......
..u...ukHTTP/1.1 204 No Content
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
ETag: "529-1160351133801"
Date: Sun, 08 Oct 2006 23:45:33 GMT


IP 127.0.0.1.8080 > 127.0.0.1.51573: tcp 200
E...C.@.@..J...........u.
.g...2...Q.......
..u...u-HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=utf-8
Content-Length: 57
Date: Sun, 08 Oct 2006 23:45:33 GMT

{"result":"01c38ac1-3606-4310-890e-f39acee4e812","id":10}
IP 127.0.0.1.51574 > 127.0.0.1.8080: tcp 195
E...M.@.@............v...>.>.d$....'.......
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.51576 > 127.0.0.1.8080: tcp 195
E.....@.@..{.........x...nT'...............
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.51576 > 127.0.0.1.8080: tcp 301
E..a..@.@............x...nT..........U.....
........<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.51576: tcp 605
E..."Y@.@..............x.....nV....".......
........HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 429
Date: Sun, 08 Oct 2006 23:46:00 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.51576 > 127.0.0.1.8080: tcp 177
E.....@.@............x...nV....M...........
........GET /cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics HTTP/1.1
Host: localhost:8080
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0


IP 127.0.0.1.8080 > 127.0.0.1.51576: tcp 759
E..+"[@.@..p...........x...M.nV....3. .....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
ETag: "529-1160351133801"
Last-Modified: Sun, 08 Oct 2006 23:45:33 GMT
Content-Type: text/calendar
Content-Length: 529
Date: Sun, 08 Oct 2006 23:46:00 GMT

BEGIN:VCALENDAR
PRODID:-//OSAF//NONSGML Scooby Server//EN
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:01c38ac1-3606-4310-890e-f39acee4e812
DESCRIPTION:This is the description of the new scooby event that was adde
 d by Andrew McMillan from within Scooby.  I wonder if this gets to be wr
 apped at the right place?  I bet it does.  What happens when I run out o
 f room in this text box though?
SUMMARY:A New Scooby Event
STATUS:CONFIRMED
DTSTART:20061010T130000
DTEND:20061010T140000
END:VEVENT
END:VCALENDAR

IP 127.0.0.1.51576 > 127.0.0.1.8080: tcp 195
E.....@.@..u.........x...nV....E...+.......
.......%REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.48034 > 127.0.0.1.8080: tcp 195
E...I.@.@..................m..G............
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.48034 > 127.0.0.1.8080: tcp 301
E..aJ.@.@..................0..G......U.....
........<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.48034: tcp 605
E....P@.@.................G....]...".......
........HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 429
Date: Sun, 08 Oct 2006 23:47:01 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.48034 > 127.0.0.1.8080: tcp 195
E...J.@.@..................]..J=...........
...(....REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.48036 > 127.0.0.1.8080: tcp 195
E...d[@.@..................r...............
...(...(REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.48036 > 127.0.0.1.8080: tcp 301
E..ad\@.@..8...............5.........U.....
...=...(<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.48036: tcp 605
E...A.@.@......................b...".......
...h...=HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 429
Date: Sun, 08 Oct 2006 23:48:01 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.48036 > 127.0.0.1.8080: tcp 195
E...d^@.@..................b...............
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.48038 > 127.0.0.1.8080: tcp 195
E....8@.@.7...................P............
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.48038 > 127.0.0.1.8080: tcp 301
E..a.9@.@.7\..................P......U.....
........<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.48038: tcp 605
E....c@.@.k...............P........".......
........HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 429
Date: Sun, 08 Oct 2006 23:49:01 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.48038 > 127.0.0.1.8080: tcp 141
E....;@.@.7...................S............
........OPTIONS /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0


IP 127.0.0.1.8080 > 127.0.0.1.48038: tcp 293
E..Y.d@.@.l8..............S....`...3.M.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
DAV: 1, calendar-access, ticket
Allow: OPTIONS, GET, HEAD, POST, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, PUT, DELETE, MOVE, MKTICKET, DELTICKET
MS-Author-Via: DAV
Content-Length: 0
Date: Sun, 08 Oct 2006 23:49:07 GMT


IP 127.0.0.1.48038 > 127.0.0.1.8080: tcp 195
E....=@.@.7................`..T7...'.......
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.48038 > 127.0.0.1.8080: tcp 301
E..a.>@.@.7W...............#..T7...'.U.....
........<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.48038: tcp 605
E....f@.@.j...............T7...P...T.......
...E....HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 429
Date: Sun, 08 Oct 2006 23:49:07 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.48038 > 127.0.0.1.8080: tcp 226
E....@@.@.7................P..V....:.
.....
...t...EPUT /cosmo/home/andrew/Scooby/20061008T234913Z.ics HTTP/1.1
Host: localhost:8080
Content-Length: 1164
If-None-Match: *
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/calendar


IP 127.0.0.1.48038 > 127.0.0.1.8080: tcp 1164
E....A@.@.3................2..V....:.......
...{...tBEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
VERSION:2.0
BEGIN:VEVENT
UID:20061008T234026Z-8293-1000-1-465@ubu
DTSTAMP:20061008T234026Z
DTSTART;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
 20061009T140000
DTEND;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
 20061009T160000
SUMMARY:An appointment
CREATED:20061008T234030
LAST-MODIFIED:20061008T234030
SEQUENCE:2
CLASS:PUBLIC
TRANSP:OPAQUE
X-EVOLUTION-CALDAV-HREF:http://localhost:
 8080/cosmo/home/andrew/Scooby/20061008T234913Z.ics
BEGIN:VALARM
X-EVOLUTION-ALARM-UID:20061008T234913Z-8291-1000-1-5@ubu
DESCRIPTION:An appointment
ACTION:DISPLAY
TRIGGER;VALUE=DURATION;RELATED=START:-PT15M
END:VALARM
END:VEVENT
BEGIN:VTIMEZONE
TZID:/softwarestudio.org/Olson_20011030_5/Pacific/Auckland
X-LIC-LOCATION:Pacific/Auckland
BEGIN:STANDARD
TZOFFSETFROM:+1300
TZOFFSETTO:+1200
TZNAME:NZST
DTSTART:19700315T030000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=3SU;BYMONTH=3
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+1200
TZOFFSETTO:+1300
TZNAME:NZDT
DTSTART:19701004T020000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=10
END:DAYLIGHT
END:VTIMEZONE
END:VCALENDAR

IP 127.0.0.1.8080 > 127.0.0.1.48038: tcp 1399
E....i@.@.g...............V................
...4...{HTTP/1.1 415 Unsupported Media Type: Unsupported Media Type
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/html;charset=utf-8
Content-Length: 1187
Date: Sun, 08 Oct 2006 23:49:13 GMT

<html><head><title>Apache Tomcat/5.5.9 - Error report</title><style><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 415 - Unsupported Media Type: Unsupported Media Type</h1><HR size="1" noshade="noshade"><p><b>type</b> Status report</p><p><b>message</b> <u>Unsupported Media Type: Unsupported Media Type</u></p><p><b>description</b> <u>The server refused this request because the request entity is in a format not supported by the requested resource for the requested method (Unsupported Media Type: Unsupported Media Type).</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/5.5.9</h3></body></html>
IP 127.0.0.1.48038 > 127.0.0.1.8080: tcp 195
E....C@.@.7...................\....e.......
.......4REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.48038 > 127.0.0.1.8080: tcp 301
E..a.D@.@.7Q..................\....e.U.....
........<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.48038: tcp 605
E....l@.@.j...............\................
........HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 429
Date: Sun, 08 Oct 2006 23:50:08 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.48038 > 127.0.0.1.8080: tcp 195
E....F@.@.7...................^i...........
...^...RREPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.42890 > 127.0.0.1.8080: tcp 195
E....U@.@................^.....3...........
...^...^REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.42890 > 127.0.0.1.8080: tcp 301
E..a.V@.@..>.............^.....3.....U.....
...^...^<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.42890: tcp 605
E...Eo@.@..................3.^.....".......
.......^HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 429
Date: Sun, 08 Oct 2006 23:51:08 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.42890 > 127.0.0.1.8080: tcp 195
E....X@.@................^.................
..z...y.REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.42892 > 127.0.0.1.8080: tcp 195
E....
@.@..................................
..z	..z	REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.42892 > 127.0.0.1.8080: tcp 301
E..a..@.@............................U.....
..z...z	<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.42892: tcp 605
E.....@.@..G.......................".......
..z0..z.HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 429
Date: Sun, 08 Oct 2006 23:52:08 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.42892 > 127.0.0.1.8080: tcp 195
E.....@.@......................p...........
..d...d.REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.42893 > 127.0.0.1.8080: tcp 195
E.....@.@.<J..................t............
..d...d.REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.42893 > 127.0.0.1.8080: tcp 301
E..a..@.@.;...................t......U.....
..d...d.<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.42893: tcp 605
E.....@.@.)...............t........".......
..e...d.HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 429
Date: Sun, 08 Oct 2006 23:53:08 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.42893 > 127.0.0.1.8080: tcp 195
E.....@.@.<G..................v^...........
..O|..OqREPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.42895 > 127.0.0.1.8080: tcp 195
E.....@.@.k2.............b.....6...........
..O|..O|REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.42895 > 127.0.0.1.8080: tcp 301
E..a..@.@.j..............b.z...6.....U.....
..O|..O|<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.42895: tcp 605
E....L@.@.p................6.b.....".......
..O...O|HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 429
Date: Sun, 08 Oct 2006 23:54:08 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.42895 > 127.0.0.1.8080: tcp 195
E.....@.@.k/.............b.................
..:...:
REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.42896 > 127.0.0.1.8080: tcp 195
E....T@.@.s................H...............
..:...:.REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.42896 > 127.0.0.1.8080: tcp 301
E..a.U@.@.s?.........................U.....
..:...:.<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.42896: tcp 605
E.....@.@.ov...................8...".......
..:`..:.HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 429
Date: Sun, 08 Oct 2006 23:55:08 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.42896 > 127.0.0.1.8080: tcp 195
E....W@.@.s................8...W...........
..%t..%oREPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.38406 > 127.0.0.1.8080: tcp 195
E....{@.@................C.
...............
..%u..%uREPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.38406 > 127.0.0.1.8080: tcp 301
E..a.|@.@................C...........U.....
..%...%u<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.38406: tcp 605
E.....@.@.x..................C.....".......
..%...%.HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 429
Date: Sun, 08 Oct 2006 23:56:08 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.38406 > 127.0.0.1.8080: tcp 195
E.....@.@................C.....=...........
...F....REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.38414 > 127.0.0.1.8080: tcp 195
E..../@.@..................l.x.............
...F...FREPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.38414 > 127.0.0.1.8080: tcp 301
E..a.0@.@..d.............../.x.......U.....
...F...F<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.38414: tcp 605
E.....@.@..r.............x.....\...".......
...m...FHTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 429
Date: Sun, 08 Oct 2006 23:57:08 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.38414 > 127.0.0.1.8080: tcp 226
E....2@.@..................\.x.......
.....
..G....mPUT /cosmo/home/andrew/Scooby/20061008T235722Z.ics HTTP/1.1
Host: localhost:8080
Content-Length: 1164
If-None-Match: *
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/calendar


IP 127.0.0.1.38414 > 127.0.0.1.8080: tcp 1164
E....3@.@..................>.x.............
..G....mBEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
VERSION:2.0
BEGIN:VEVENT
UID:20061008T234026Z-8293-1000-1-465@ubu
DTSTAMP:20061008T234026Z
DTSTART;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
 20061009T140000
DTEND;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
 20061009T160000
SUMMARY:An appointment
CREATED:20061008T234030
LAST-MODIFIED:20061008T234030
SEQUENCE:3
CLASS:PUBLIC
TRANSP:OPAQUE
X-EVOLUTION-CALDAV-HREF:http://localhost:
 8080/cosmo/home/andrew/Scooby/20061008T235722Z.ics
BEGIN:VALARM
X-EVOLUTION-ALARM-UID:20061008T235722Z-8291-1000-1-7@ubu
DESCRIPTION:An appointment
ACTION:DISPLAY
TRIGGER;VALUE=DURATION;RELATED=START:-PT15M
END:VALARM
END:VEVENT
BEGIN:VTIMEZONE
TZID:/softwarestudio.org/Olson_20011030_5/Pacific/Auckland
X-LIC-LOCATION:Pacific/Auckland
BEGIN:STANDARD
TZOFFSETFROM:+1300
TZOFFSETTO:+1200
TZNAME:NZST
DTSTART:19700315T030000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=3SU;BYMONTH=3
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+1200
TZOFFSETTO:+1300
TZNAME:NZDT
DTSTART:19701004T020000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=10
END:DAYLIGHT
END:VTIMEZONE
END:VCALENDAR

IP 127.0.0.1.8080 > 127.0.0.1.38414: tcp 1399
E.....@.@..V.............x.........W.......
..G...G.HTTP/1.1 415 Unsupported Media Type: Unsupported Media Type
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/html;charset=utf-8
Content-Length: 1187
Date: Sun, 08 Oct 2006 23:57:22 GMT

<html><head><title>Apache Tomcat/5.5.9 - Error report</title><style><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 415 - Unsupported Media Type: Unsupported Media Type</h1><HR size="1" noshade="noshade"><p><b>type</b> Status report</p><p><b>message</b> <u>Unsupported Media Type: Unsupported Media Type</u></p><p><b>description</b> <u>The server refused this request because the request entity is in a format not supported by the requested resource for the requested method (Unsupported Media Type: Unsupported Media Type).</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/5.5.9</h3></body></html>
IP 127.0.0.1.38414 > 127.0.0.1.8080: tcp 226
E....5@.@....................x.....?.
.....
...7..G.PUT /cosmo/home/andrew/Scooby/20061008T235744Z.ics HTTP/1.1
Host: localhost:8080
Content-Length: 1080
If-None-Match: *
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/calendar


IP 127.0.0.1.38414 > 127.0.0.1.8080: tcp 1080
E..l.6@.@..S.................x.....?.a.....
...7..G.BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
VERSION:2.0
BEGIN:VEVENT
UID:20061008T235739Z-8293-1000-1-475@ubu
DTSTAMP:20061008T235739Z
DTSTART;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
 20061010T150000
DTEND;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
 20061010T160000
SUMMARY:New from evolution
X-EVOLUTION-CALDAV-HREF:http://localhost:
 8080/cosmo/home/andrew/Scooby/20061008T235744Z.ics
BEGIN:VALARM
X-EVOLUTION-ALARM-UID:20061008T235739Z-8291-1000-1-8@ubu
ACTION:DISPLAY
TRIGGER;VALUE=DURATION;RELATED=START:-PT15M
DESCRIPTION:New from evolution
END:VALARM
END:VEVENT
BEGIN:VTIMEZONE
TZID:/softwarestudio.org/Olson_20011030_5/Pacific/Auckland
X-LIC-LOCATION:Pacific/Auckland
BEGIN:STANDARD
TZOFFSETFROM:+1300
TZOFFSETTO:+1200
TZNAME:NZST
DTSTART:19700315T030000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=3SU;BYMONTH=3
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+1200
TZOFFSETTO:+1300
TZNAME:NZDT
DTSTART:19701004T020000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=10
END:DAYLIGHT
END:VTIMEZONE
END:VCALENDAR

IP 127.0.0.1.8080 > 127.0.0.1.38414: tcp 159
E.....@.@. ,.............x.................
.......7HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
ETag: "1080-1160351864547"
Content-Length: 0
Date: Sun, 08 Oct 2006 23:57:44 GMT


IP 127.0.0.1.38414 > 127.0.0.1.8080: tcp 195
E....8@.@....................x.*...k.......
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.38414 > 127.0.0.1.8080: tcp 301
E..a.9@.@..[.................x.*...k.U.....
........<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.38414: tcp 928
E.....@.@..).............x.*...............
...;....HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 752
Date: Sun, 08 Oct 2006 23:58:08 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/20061008T235744Z.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"1080-1160351864547"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.38414 > 127.0.0.1.8080: tcp 195
E....;@.@....................x.............
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.38416 > 127.0.0.1.8080: tcp 195
E...3d@.@......................Z...........
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.38416 > 127.0.0.1.8080: tcp 301
E..a3e@.@..0...................Z.....U.....
........<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.38416: tcp 928
E...G.@.@..-...............Z.......".......
...	....HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 752
Date: Sun, 08 Oct 2006 23:59:08 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/20061008T235744Z.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"1080-1160351864547"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.38416 > 127.0.0.1.8080: tcp 195
E...3g@.@..................................
...v...nREPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.38418 > 127.0.0.1.8080: tcp 195
E.....@.@..$..............u...
............
...v...vREPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.38418 > 127.0.0.1.8080: tcp 301
E..a..@.@.................vr..
......U.....
...v...v<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.38418: tcp 928
E.....@.@.^...............
...w....".......
.......vHTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 752
Date: Mon, 09 Oct 2006 00:00:08 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/20061008T235744Z.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"1080-1160351864547"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.38418 > 127.0.0.1.8080: tcp 195
E.....@.@..!..............w................
...K...@REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.53181 > 127.0.0.1.8080: tcp 195
E....)@.@.................!m...............
...K...KREPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.53181 > 127.0.0.1.8080: tcp 301
E..a.*@.@..j.............."0.........U.....
...R...K<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.53181: tcp 928
E...3.@.@..q..................#]...".......
.......RHTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 752
Date: Mon, 09 Oct 2006 00:01:09 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/20061008T235744Z.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"1080-1160351864547"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.53181 > 127.0.0.1.8080: tcp 195
E....,@.@.................#]...............
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.53181 > 127.0.0.1.8080: tcp 301
E..a.-@.@..g..............$ .........U.....
........<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.53181: tcp 928
E...3.@.@..n..................%M...C.......
...R....HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 752
Date: Mon, 09 Oct 2006 00:02:09 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/20061008T235744Z.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"1080-1160351864547"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.53181 > 127.0.0.1.8080: tcp 195
E..../@.@.................%M...%...;.......
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 195
E...z.@.@..`...............$..v............
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 301
E..az.@.@.....................v......U.....
........<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.53186: tcp 928
E....p@.@.R...............v........".......
........HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 752
Date: Mon, 09 Oct 2006 00:03:09 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/20061008T235744Z.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"1080-1160351864547"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.8080 > 127.0.0.1.53186: tcp 928
E....q@.@.R...............v........".......
........HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 752
Date: Mon, 09 Oct 2006 00:03:09 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/20061008T235744Z.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"1080-1160351864547"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 141
E...z.@.@.....................zq...........
...$....OPTIONS /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0


IP 127.0.0.1.8080 > 127.0.0.1.53186: tcp 293
E..Y.s@.@.U)..............zq.......3.M.....
...+...$HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
DAV: 1, calendar-access, ticket
Allow: OPTIONS, GET, HEAD, POST, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, PUT, DELETE, MOVE, MKTICKET, DELTICKET
MS-Author-Via: DAV
Content-Length: 0
Date: Mon, 09 Oct 2006 00:03:30 GMT


IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 195
E...z.@.@..Z..................{....;.......
...,...+REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 301
E..az.@.@..................d..{....;.U.....
...,...+<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.53186: tcp 928
E....u@.@.R...............{........T.......
.......,HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 752
Date: Mon, 09 Oct 2006 00:03:30 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/20061008T235744Z.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"1080-1160351864547"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 226
E...z.@.@..8...................6...X.
.....
........PUT /cosmo/home/andrew/Scooby/20061009T000330Z.ics HTTP/1.1
Host: localhost:8080
Content-Length: 1165
If-None-Match: *
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/calendar


IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 1165
E...z.@.@..................s...6...X.......
........BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
VERSION:2.0
BEGIN:VEVENT
UID:20061008T234026Z-8293-1000-1-465@ubu
DTSTAMP:20061008T234026Z
DTSTART;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
 20061009T140000
DTEND;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
 20061009T160000
SUMMARY:An appointment
CREATED:20061008T234030
LAST-MODIFIED:20061008T234030
SEQUENCE:1
X-EVOLUTION-MOVE-CALENDAR:1
X-EVOLUTION-CALDAV-HREF:http://localhost:
 8080/cosmo/home/andrew/Scooby/20061009T000330Z.ics
BEGIN:VALARM
X-EVOLUTION-ALARM-UID:20061008T234026Z-8291-1000-1-0@ubu
ACTION:DISPLAY
TRIGGER;VALUE=DURATION;RELATED=START:-PT15M
DESCRIPTION:An appointment
END:VALARM
END:VEVENT
BEGIN:VTIMEZONE
TZID:/softwarestudio.org/Olson_20011030_5/Pacific/Auckland
X-LIC-LOCATION:Pacific/Auckland
BEGIN:STANDARD
TZOFFSETFROM:+1300
TZOFFSETTO:+1200
TZNAME:NZST
DTSTART:19700315T030000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=3SU;BYMONTH=3
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+1200
TZOFFSETTO:+1300
TZNAME:NZDT
DTSTART:19701004T020000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=10
END:DAYLIGHT
END:VTIMEZONE
END:VCALENDAR

IP 127.0.0.1.8080 > 127.0.0.1.53186: tcp 1399
E....x@.@.P................6...............
... ....HTTP/1.1 415 Unsupported Media Type: Unsupported Media Type
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/html;charset=utf-8
Content-Length: 1187
Date: Mon, 09 Oct 2006 00:03:30 GMT

<html><head><title>Apache Tomcat/5.5.9 - Error report</title><style><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 415 - Unsupported Media Type: Unsupported Media Type</h1><HR size="1" noshade="noshade"><p><b>type</b> Status report</p><p><b>message</b> <u>Unsupported Media Type: Unsupported Media Type</u></p><p><b>description</b> <u>The server refused this request because the request entity is in a format not supported by the requested resource for the requested method (Unsupported Media Type: Unsupported Media Type).</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/5.5.9</h3></body></html>
IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 141
E...z.@.@..................................
....... OPTIONS /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0


IP 127.0.0.1.8080 > 127.0.0.1.53186: tcp 293
E..Y.z@.@.U".........................M.....
........HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
DAV: 1, calendar-access, ticket
Allow: OPTIONS, GET, HEAD, POST, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, PUT, DELETE, MOVE, MKTICKET, DELTICKET
MS-Author-Via: DAV
Content-Length: 0
Date: Mon, 09 Oct 2006 00:03:45 GMT


IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 195
E...z.@.@..R...............................
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 301
E..az.@.@..................P.........U.....
........<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.53186: tcp 928
E....|@.@.R....................}...........
.. .....HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 752
Date: Mon, 09 Oct 2006 00:03:45 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/20061008T235744Z.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"1080-1160351864547"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 226
E...z.@.@..0...............}...r.....
.....
.. ... .PUT /cosmo/home/andrew/Scooby/20061009T000345Z.ics HTTP/1.1
Host: localhost:8080
Content-Length: 1165
If-None-Match: *
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/calendar


IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 1165
E...z.@.@.................._...r...........
.. ... .BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
VERSION:2.0
BEGIN:VEVENT
UID:20061008T234026Z-8293-1000-1-465@ubu
DTSTAMP:20061008T234026Z
DTSTART;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
 20061009T140000
DTEND;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
 20061009T160000
SUMMARY:An appointment
CREATED:20061008T234030
LAST-MODIFIED:20061008T234030
SEQUENCE:1
X-EVOLUTION-MOVE-CALENDAR:1
X-EVOLUTION-CALDAV-HREF:http://localhost:
 8080/cosmo/home/andrew/Scooby/20061009T000345Z.ics
BEGIN:VALARM
X-EVOLUTION-ALARM-UID:20061008T234026Z-8291-1000-1-0@ubu
ACTION:DISPLAY
TRIGGER;VALUE=DURATION;RELATED=START:-PT15M
DESCRIPTION:An appointment
END:VALARM
END:VEVENT
BEGIN:VTIMEZONE
TZID:/softwarestudio.org/Olson_20011030_5/Pacific/Auckland
X-LIC-LOCATION:Pacific/Auckland
BEGIN:STANDARD
TZOFFSETFROM:+1300
TZOFFSETTO:+1200
TZNAME:NZST
DTSTART:19700315T030000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=3SU;BYMONTH=3
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+1200
TZOFFSETTO:+1300
TZNAME:NZDT
DTSTART:19701004T020000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=10
END:DAYLIGHT
END:VTIMEZONE
END:VCALENDAR

IP 127.0.0.1.8080 > 127.0.0.1.53186: tcp 1399
E.....@.@.P................r...............
..!
.. .HTTP/1.1 415 Unsupported Media Type: Unsupported Media Type
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/html;charset=utf-8
Content-Length: 1187
Date: Mon, 09 Oct 2006 00:03:45 GMT

<html><head><title>Apache Tomcat/5.5.9 - Error report</title><style><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 415 - Unsupported Media Type: Unsupported Media Type</h1><HR size="1" noshade="noshade"><p><b>type</b> Status report</p><p><b>message</b> <u>Unsupported Media Type: Unsupported Media Type</u></p><p><b>description</b> <u>The server refused this request because the request entity is in a format not supported by the requested resource for the requested method (Unsupported Media Type: Unsupported Media Type).</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/5.5.9</h3></body></html>
IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 226
E...z.@.@..-.........................
.....
......!
PUT /cosmo/home/andrew/Scooby/20061009T000429Z.ics HTTP/1.1
Host: localhost:8080
Content-Length: 1104
If-None-Match: *
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/calendar


IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 1104
E...z.@.@............................y.....
........BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
VERSION:2.0
BEGIN:VEVENT
UID:20061009T000422Z-8293-1000-1-480@ubu
DTSTAMP:20061009T000422Z
DTSTART;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
 20061009T140000
DTEND;TZID=/softwarestudio.org/Olson_20011030_5/Pacific/Auckland:
 20061009T160000
SUMMARY:A new cosmo event this time...
X-EVOLUTION-CALDAV-HREF:http://localhost:
 8080/cosmo/home/andrew/Scooby/20061009T000429Z.ics
BEGIN:VALARM
X-EVOLUTION-ALARM-UID:20061009T000422Z-8291-1000-1-9@ubu
ACTION:DISPLAY
TRIGGER;VALUE=DURATION;RELATED=START:-PT15M
DESCRIPTION:A new cosmo event this time...
END:VALARM
END:VEVENT
BEGIN:VTIMEZONE
TZID:/softwarestudio.org/Olson_20011030_5/Pacific/Auckland
X-LIC-LOCATION:Pacific/Auckland
BEGIN:STANDARD
TZOFFSETFROM:+1300
TZOFFSETTO:+1200
TZNAME:NZST
DTSTART:19700315T030000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=3SU;BYMONTH=3
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+1200
TZOFFSETTO:+1300
TZNAME:NZDT
DTSTART:19701004T020000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=10
END:DAYLIGHT
END:VTIMEZONE
END:VCALENDAR

IP 127.0.0.1.8080 > 127.0.0.1.53186: tcp 159
E.....@.@.U................................
........HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
ETag: "1104-1160352269364"
Content-Length: 0
Date: Mon, 09 Oct 2006 00:04:29 GMT


IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 195
E...z.@.@..I...............................
..
p....REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 301
E..az.@.@............................U.....
..
v..
p<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.53186: tcp 1252
E.....@.@.QX.........................
.....
...6..
vHTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 1075
Date: Mon, 09 Oct 2006 00:04:46 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/20061008T235744Z.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"1080-1160351864547"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/20061009T000429Z.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"1104-1160352269364"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.53186 > 127.0.0.1.8080: tcp 195
E...z.@.@..E...................m...........
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.53187 > 127.0.0.1.8080: tcp 195
E...ms@.@..................................
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.53187 > 127.0.0.1.8080: tcp 301
E..amt@.@.. ...............E.........U.....
........<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.53187: tcp 1252
E....5@.@.#....................r...".
.....
...q....HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 1075
Date: Mon, 09 Oct 2006 00:05:46 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/20061008T235744Z.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"1080-1160351864547"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/20061009T000429Z.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"1104-1160352269364"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/01c38ac1-3606-4310-890e-f39acee4e812.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"529-1160351133801"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
</D:multistatus>

IP 127.0.0.1.53187 > 127.0.0.1.8080: tcp 195
E...mw@.@..................r.......(.......
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.52604 > 127.0.0.1.8080: tcp 195
E.....@.@..
.........|...6S................
........REPORT /cosmo/home/andrew/Scooby/ HTTP/1.1
Host: localhost:8080
Content-Length: 301
Depth: 1
Authorization: Basic YW5kcmV3OndlcmRuYQ==
User-Agent: Evolution/1.8.0
Content-Type: text/xml


IP 127.0.0.1.52604 > 127.0.0.1.8080: tcp 301
E..a..@.@............|...6T..........U.....
...
....<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
  <D:prop>
    <D:getetag/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:is-defined/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>
IP 127.0.0.1.8080 > 127.0.0.1.52604: tcp 1252
E.....@.@.S............|.....6U....".
.....
...E...
HTTP/1.1 207 Multi-Status
Server: Apache-Coyote/1.1
X-Cosmo-Version: 0.4.0
Content-Type: text/xml;charset=UTF-8
Content-Length: 1075
Date: Mon, 09 Oct 2006 00:06:46 GMT

<?xml version="1.0" encoding="UTF-8"?>
<D:multistatus xmlns:D="DAV:">
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/20061008T235744Z.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getetag>"1080-1160351864547"</D:getetag>
            </D:prop>
            <D:status>HTTP/1.1 200 OK</D:status>
        </D:propstat>
    </D:response>
    <D:response>
        <D:href>http://localhost:8080/cosmo/home/andrew/Scooby/20061009T000429Z.ics</D:href>
        <D:propstat>
            <D:prop>
                <D:getet