diff --git a/DEVELOPMENT.rst b/DEVELOPMENT.rst
new file mode 100644
--- /dev/null
+++ b/DEVELOPMENT.rst
@@ -0,0 +1,100 @@
+---
+title: hledger development
+---
+hledger development
+===================
+
+**Techie intro**
+
+hledger_ is a remix, in haskell_, of John Wiegley's excellent ledger_ accounting tool.
+It reads a plain text journal_ or timelog_ file describing your transactions
+and displays reports via command line, curses or web interfaces.
+
+The hledger project aims to produce:
+
+- a practical, accessible, dependable tool for end users
+- a useful library and toolbox for finance-minded haskell programmers
+- a successful, time-and-money-solvent project within a thriving ecosystem of financial software projects.
+
+hledger is free software by `Simon Michael`_ & `co.`_, released under GNU GPLv3.
+
+**Learn**
+ news_, manual_, screenshots_, `some extra tips`_
+
+**Download**
+ ``cabal install hledger``, 
+ or try these ready-to-run binaries_,
+ or see the `installing docs <MANUAL.html#installing>`_
+
+**Develop**
+ ``darcs get http://joyful.com/repos/hledger``, 
+ `browse the repo`_, 
+ `hackage page`_, 
+ `hledger haddock docs`_, 
+ benchmark_\/profile_\/heap_\/coverage_ reports,
+ `developer notes`_
+
+ .. `hledger-lib sourcegraph report`_, 
+ .. `hledger sourcegraph report`_, 
+
+.. raw:: html
+
+ <a name="support" />
+
+**Support**
+
+- chat Simon (sm) on the `#ledger`_ irc channel which we share, or `email me`_
+- report problems at `bugs.hledger.org <http://bugs.hledger.org>`_ (`grid <http://bugs.hledger.org/grid>`_)
+- share and test journal snippets at paste . hledger.org
+- .. raw:: html
+
+     <form action="http://groups.google.com/group/hledger/boxsubscribe" >
+       join the hledger mail list at <a href="http://list.hledger.org">list.hledger.org</a>. Your email:
+       <input type=text name=email><input type=submit name="sub" value="Subscribe">
+     </form>
+
+**Related projects**
+
+- John Wiegley's ledger_ inspired hledger, and we try to stay compatible. You can often use both tools on the same journal file.
+- Uwe Hollerbach's umm_ is another haskell tool inspired by h/ledger.
+- Tim Docker's ledger-reports_ uses hledger as a library to generate `html reports`_. 
+- I have a few older bits and pieces `here <http://joyful.com/Ledger>`_.
+
+.. raw:: html
+
+ <a href="http://joyful.com/darcsweb/darcsweb.cgi?r=hledger;a=shortlog"><img src=http://joyful.com/repos/hledger/commits.png border=0></a>
+ <a href="https://www.google.com/analytics/reporting/?reset=1&id=15489822" accesskey="a"></a>
+
+
+.. _hledger:              README.html
+.. _journal:              http://joyful.com/repos/hledger/data/sample.journal
+.. _timelog:              http://joyful.com/repos/hledger/data/sample.timelog
+.. _command line:         SCREENSHOTS.html#hledger-screen-1
+.. _curses:               SCREENSHOTS.html#sshot
+.. _web interface:        http://demo.hledger.org
+.. _mail list:            http://list.hledger.org
+.. _issue tracker:        http://bugs.hledger.org
+.. _binaries:             http://hledger.org/binaries/
+.. _manual:               MANUAL.html
+.. _news:                 NEWS.html
+.. _screenshots:          SCREENSHOTS.html
+.. _hledger haddock docs: http://hledger.org/api-doc/
+.. _hledger-lib sourcegraph report: http://joyful.com/repos/hledger/hledger-lib/SourceGraph/hledger-lib.html
+.. _hledger sourcegraph report: http://joyful.com/repos/hledger/SourceGraph/hledger.html
+.. _developer notes:      http://joyful.com/darcsweb/darcsweb.cgi?r=hledger;a=plainblob;f=/NOTES
+.. _benchmark:            http://hledger.org/profs/latest.bench
+.. _profile:              http://hledger.org/profs/latest.prof
+.. _heap:                 http://hledger.org/profs/latest.ps
+.. _coverage:             http://hledger.org/profs/coverage/hpc_index_fun.html
+.. _browse the repo:      http://joyful.com/darcsweb/darcsweb.cgi?r=hledger
+.. _email me:             mailto:simon@joyful.com?subject=hledger:
+.. _Simon Michael:        http://joyful.com
+.. _co.:                  http://hledger.org/CONTRIBUTORS.html
+.. _hackage page:         http://hackage.haskell.org/package/hledger
+.. _#ledger:              irc://irc.freenode.net/#ledger
+.. _haskell:              http://haskell.org
+.. _ledger:               http://wiki.github.com/jwiegley/ledger
+.. _umm:                  http://www.korgwal.com/umm/
+.. _ledger-reports:       http://dockerz.net/repos/ledger-reports
+.. _html reports:         http://dockerz.net/software/hledger_report_sample/report.html
+.. _some extra tips:      http://podcastle.org/2009/10/09/pc-miniature-38-accounting-for-dragons/
diff --git a/Hledger/Cli/Commands/Add.hs b/Hledger/Cli/Commands/Add.hs
--- a/Hledger/Cli/Commands/Add.hs
+++ b/Hledger/Cli/Commands/Add.hs
@@ -10,7 +10,7 @@
 import Hledger.Data
 import Hledger.Read.Journal (someamount)
 import Hledger.Cli.Options
-import Hledger.Cli.Commands.Register (showRegisterReport)
+import Hledger.Cli.Commands.Register (registerReport, registerReportAsText)
 #if __GLASGOW_HASKELL__ <= 610
 import Prelude hiding (putStr, putStrLn, getLine, appendFile)
 import System.IO.UTF8
@@ -44,7 +44,7 @@
 getAndAddTransactions :: Journal -> [Opt] -> [String] -> Day -> IO ()
 getAndAddTransactions j opts args defaultDate = do
   (t, d) <- getTransaction j opts args defaultDate
-  j <- journalAddTransaction j t
+  j <- journalAddTransaction j opts t
   getAndAddTransactions j opts args d
 
 -- | Read a transaction from the command line, with history-aware prompting.
@@ -134,11 +134,12 @@
 -- | Append this transaction to the journal's file. Also, to the journal's
 -- transaction list, but we don't bother updating the other fields - this
 -- is enough to include new transactions in the history matching.
-journalAddTransaction :: Journal -> Transaction -> IO Journal
-journalAddTransaction j@Journal{jtxns=ts} t = do
+journalAddTransaction :: Journal -> [Opt] -> Transaction -> IO Journal
+journalAddTransaction j@Journal{jtxns=ts} opts t = do
   appendToJournalFile j $ showTransaction t
-  putStrLn $ printf "\nAdded transaction to %s:" (filepath j)
-  putStrLn =<< registerFromString (show t)
+  when (Debug `elem` opts) $ do
+    putStrLn $ printf "\nAdded transaction to %s:" (filepath j)
+    putStrLn =<< registerFromString (show t)
   return j{jtxns=ts++[t]}
 
 -- | Append data to the journal's file, ensuring proper separation from
@@ -160,7 +161,7 @@
 registerFromString s = do
   now <- getCurrentLocalTime
   l <- readJournalWithOpts [] s
-  return $ showRegisterReport opts (optsToFilterSpec opts [] now) l
+  return $ registerReportAsText opts $ registerReport opts (optsToFilterSpec opts [] now) l
     where opts = [Empty]
 
 -- | Return a similarity measure, from 0 to 1, for two strings.
diff --git a/Hledger/Cli/Commands/All.hs b/Hledger/Cli/Commands/All.hs
--- a/Hledger/Cli/Commands/All.hs
+++ b/Hledger/Cli/Commands/All.hs
@@ -23,8 +23,6 @@
 #endif
 #if defined(WEB)
                      module Hledger.Cli.Commands.Web,
-#elif defined(WEB610)
-                     module Hledger.Cli.Commands.Web610,
 #endif
                      tests_Hledger_Commands
               )
@@ -44,8 +42,6 @@
 #endif
 #if defined(WEB)
 import Hledger.Cli.Commands.Web
-#elif defined(WEB610)
-import Hledger.Cli.Commands.Web610
 #endif
 import Test.HUnit (Test(TestList))
 
@@ -68,6 +64,4 @@
 -- #endif
 -- #if defined(WEB)
 --     ,Hledger.Cli.Commands.Web.tests_Web
--- #elif defined(WEB610)
---     ,Hledger.Cli.Commands.Web610.tests_Web
 -- #endif
diff --git a/Hledger/Cli/Commands/Balance.hs b/Hledger/Cli/Commands/Balance.hs
--- a/Hledger/Cli/Commands/Balance.hs
+++ b/Hledger/Cli/Commands/Balance.hs
@@ -95,8 +95,14 @@
 
 -}
 
-module Hledger.Cli.Commands.Balance
-where
+module Hledger.Cli.Commands.Balance (
+  BalanceReport
+ ,BalanceReportItem
+ ,balance
+ ,balanceReport
+ ,balanceReportAsText
+ -- ,tests_Balance
+) where
 import Hledger.Data.Utils
 import Hledger.Data.Types
 import Hledger.Data.Amount
@@ -110,46 +116,70 @@
 #endif
 
 
+-- | A balance report is a chart of accounts with balances, and their grand total.
+type BalanceReport = ([BalanceReportItem] -- line items, one per account
+                     ,MixedAmount         -- total balance of all accounts
+                     )
+
+-- | The data for a single balance report line item, representing one account.
+type BalanceReportItem = (AccountName  -- full account name
+                         ,AccountName  -- account name elided for display: the leaf name,
+                                       -- prefixed by any boring parents immediately above
+                         ,Int          -- account depth within this report, excludes boring parents
+                         ,MixedAmount) -- account balance, includes subs unless --flat is present
+
 -- | Print a balance report.
 balance :: [Opt] -> [String] -> Journal -> IO ()
 balance opts args j = do
   t <- getCurrentLocalTime
-  putStr $ showBalanceReport opts (optsToFilterSpec opts args t) j
+  putStr $ balanceReportAsText opts $ balanceReport opts (optsToFilterSpec opts args t) j
 
--- | Generate a balance report with the specified options for this journal.
-showBalanceReport :: [Opt] -> FilterSpec -> Journal -> String
-showBalanceReport opts filterspec j = acctsstr ++ totalstr
-    where
-      l = journalToLedger filterspec j
-      acctsstr = unlines $ map showacct interestingaccts
-          where
-            showacct = showInterestingAccount opts l interestingaccts
-            interestingaccts = filter (isInteresting opts l) acctnames
-            acctnames = sort $ tail $ flatten $ treemap aname accttree
-            accttree = ledgerAccountTree (fromMaybe 99999 $ depthFromOpts opts) l
-      totalstr | NoTotal `elem` opts = ""
-               | otherwise = printf "--------------------\n%s\n" $ padleft 20 $ showMixedAmountWithoutPrice total
-          where
-            total = sum $ map abalance $ ledgerTopAccounts l
+-- | Render a balance report as plain text suitable for console output.
+balanceReportAsText :: [Opt] -> BalanceReport -> String
+balanceReportAsText opts (items,total) =
+    unlines $
+            map (balanceReportItemAsText opts) items
+            ++
+            if NoTotal `elem` opts
+             then []
+             else ["--------------------"
+                  ,padleft 20 $ showMixedAmountWithoutPrice total
+                  ]
 
--- | Display one line of the balance report with appropriate indenting and eliding.
-showInterestingAccount :: [Opt] -> Ledger -> [AccountName] -> AccountName -> String
-showInterestingAccount opts l interestingaccts a = concatTopPadded [amt, "  ", name]
+-- | Render one balance report line item as plain text.
+balanceReportItemAsText :: [Opt] -> BalanceReportItem -> String
+balanceReportItemAsText opts (a, adisplay, adepth, abal) = concatTopPadded [amt, "  ", name]
     where
-      amt = padleft 20 $ showMixedAmountWithoutPrice bal
-      bal | Flat `elem` opts = exclusiveBalance acct
-          | otherwise = abalance acct
-      acct = ledgerAccount l a
+      amt = padleft 20 $ showMixedAmountWithoutPrice abal
       name | Flat `elem` opts = accountNameDrop (dropFromOpts opts) a
-           | otherwise        = depthspacer ++ partialname
-      parents = parentAccountNames a
-      interestingparents = filter (`elem` interestingaccts) parents
-      depthspacer = replicate (indentperlevel * length interestingparents) ' '
+           | otherwise        = depthspacer ++ adisplay
+      depthspacer = replicate (indentperlevel * adepth) ' '
       indentperlevel = 2
-      -- the partial name is the account's leaf name, prefixed by the
-      -- names of any boring parents immediately above
-      partialname = accountNameFromComponents $ reverse (map accountLeafName ps) ++ [accountLeafName a]
-          where ps = takeWhile boring parents where boring = not . (`elem` interestingparents)
+
+-- | Get a balance report with the specified options for this journal.
+balanceReport :: [Opt] -> FilterSpec -> Journal -> BalanceReport
+balanceReport opts filterspec j = (items, total)
+    where
+      items = map mkitem interestingaccts
+      interestingaccts = filter (isInteresting opts l) acctnames
+      acctnames = sort $ tail $ flatten $ treemap aname accttree
+      accttree = ledgerAccountTree (fromMaybe 99999 $ depthFromOpts opts) l
+      total = sum $ map abalance $ ledgerTopAccounts l
+      l = journalToLedger filterspec j
+      -- | Get data for one balance report line item.
+      mkitem :: AccountName -> BalanceReportItem
+      mkitem a = (a, adisplay, indent, abal)
+          where
+            adisplay | Flat `elem` opts = a
+                     | otherwise = accountNameFromComponents $ reverse (map accountLeafName ps) ++ [accountLeafName a]
+                where ps = takeWhile boring parents where boring = not . (`elem` interestingparents)
+            indent | Flat `elem` opts = 0
+                   | otherwise = length interestingparents
+            interestingparents = filter (`elem` interestingaccts) parents
+            parents = parentAccountNames a
+            abal | Flat `elem` opts = exclusiveBalance acct
+                 | otherwise = abalance acct
+                 where acct = ledgerAccount l a
 
 exclusiveBalance :: Account -> MixedAmount
 exclusiveBalance = sumPostings . apostings
diff --git a/Hledger/Cli/Commands/Chart.hs b/Hledger/Cli/Commands/Chart.hs
--- a/Hledger/Cli/Commands/Chart.hs
+++ b/Hledger/Cli/Commands/Chart.hs
@@ -44,7 +44,7 @@
 parseSize :: String -> (Int,Int)
 parseSize str = (read w, read h)
     where
-    x = fromMaybe (error "Size should be in WIDTHxHEIGHT format") $ findIndex (=='x') str
+    x = fromMaybe (error' "Size should be in WIDTHxHEIGHT format") $ findIndex (=='x') str
     (w,_:h) = splitAt x str
 
 -- | Generate pie chart
diff --git a/Hledger/Cli/Commands/Convert.hs b/Hledger/Cli/Commands/Convert.hs
--- a/Hledger/Cli/Commands/Convert.hs
+++ b/Hledger/Cli/Commands/Convert.hs
@@ -7,7 +7,7 @@
 import Hledger.Cli.Options (Opt(Debug))
 import Hledger.Cli.Version (versionstr)
 import Hledger.Data.Types (Journal,AccountName,Transaction(..),Posting(..),PostingType(..))
-import Hledger.Data.Utils (strip, spacenonewline, restofline, parseWithCtx, assertParse, assertParseEqual)
+import Hledger.Data.Utils (strip, spacenonewline, restofline, parseWithCtx, assertParse, assertParseEqual, error')
 import Hledger.Read.Common (emptyCtx)
 import Hledger.Read.Journal (someamount,ledgeraccountname)
 import Hledger.Data.Amount (nullmixedamt)
@@ -71,11 +71,11 @@
 -- using/creating a .rules file.
 convert :: [Opt] -> [String] -> Journal -> IO ()
 convert opts args _ = do
-  when (null args) $ error "please specify a csv data file."
+  when (null args) $ error' "please specify a csv data file."
   let csvfile = head args
   csvparse <- parseCSVFromFile csvfile
   let records = case csvparse of
-                  Left e -> error $ show e
+                  Left e -> error' $ show e
                   Right rs -> reverse $ filter (/= [""]) rs
   let debug = Debug `elem` opts
       rulesfile = rulesFileFor csvfile
@@ -85,7 +85,7 @@
                   writeFile rulesfile initialRulesFileContent
    else
       hPrintf stderr "using conversion rules file %s\n" rulesfile
-  rules <- liftM (either (error.show) id) $ parseCsvRulesFile rulesfile
+  rules <- liftM (either (error'.show) id) $ parseCsvRulesFile rulesfile
   when debug $ hPrintf stderr "rules: %s\n" (show rules)
   let requiredfields = max 2 (maxFieldIndex rules + 1)
       badrecords = take 1 $ filter ((< requiredfields).length) records
diff --git a/Hledger/Cli/Commands/Print.hs b/Hledger/Cli/Commands/Print.hs
--- a/Hledger/Cli/Commands/Print.hs
+++ b/Hledger/Cli/Commands/Print.hs
@@ -5,8 +5,13 @@
 
 -}
 
-module Hledger.Cli.Commands.Print
-where
+module Hledger.Cli.Commands.Print (
+  JournalReport
+ ,JournalReportItem
+ ,print'
+ ,journalReport
+ ,showTransactions
+) where
 import Hledger.Data
 import Hledger.Cli.Options
 #if __GLASGOW_HASKELL__ <= 610
@@ -15,6 +20,12 @@
 #endif
 
 
+-- | A "journal report" is just a list of transactions.
+type JournalReport = [JournalReportItem]
+
+-- | The data for a single journal report item, representing one transaction.
+type JournalReportItem = Transaction
+
 -- | Print journal transactions in standard format.
 print' :: [Opt] -> [String] -> Journal -> IO ()
 print' opts args j = do
@@ -22,8 +33,11 @@
   putStr $ showTransactions (optsToFilterSpec opts args t) j
 
 showTransactions :: FilterSpec -> Journal -> String
-showTransactions filterspec j =
-    concatMap (showTransactionForPrint effective) $ sortBy (comparing tdate) txns
-        where
-          effective = EffectiveDate == whichdate filterspec
-          txns = jtxns $ filterJournalTransactions filterspec j
+showTransactions fspec j = journalReportAsText [] fspec $ journalReport [] fspec j
+
+journalReportAsText :: [Opt] -> FilterSpec -> JournalReport -> String -- XXX unlike the others, this one needs fspec not opts
+journalReportAsText _ fspec items = concatMap (showTransactionForPrint effective) items
+    where effective = EffectiveDate == whichdate fspec
+
+journalReport :: [Opt] -> FilterSpec -> Journal -> JournalReport
+journalReport _ fspec j = sortBy (comparing tdate) $ jtxns $ filterJournalTransactions fspec j
diff --git a/Hledger/Cli/Commands/Register.hs b/Hledger/Cli/Commands/Register.hs
--- a/Hledger/Cli/Commands/Register.hs
+++ b/Hledger/Cli/Commands/Register.hs
@@ -6,9 +6,12 @@
 -}
 
 module Hledger.Cli.Commands.Register (
-  register
- ,showRegisterReport
- ,showPostingWithBalance
+  RegisterReport
+ ,RegisterReportItem
+ ,register
+ ,registerReport
+ ,registerReportAsText
+ ,showPostingWithBalanceForVty
  ,tests_Register
 ) where
 
@@ -22,35 +25,83 @@
 import Text.ParserCombinators.Parsec
 
 
+-- | A register report is a list of postings to an account or set of
+-- accounts, with a running total. Postings may be actual postings, or
+-- virtual postings aggregated over a reporting interval.
+type RegisterReport = [RegisterReportItem] -- line items, one per posting
+
+-- | The data for a single register report line item, representing one posting.
+type RegisterReportItem = (Maybe (Day, String) -- transaction date and description if this is the first posting
+                          ,Posting             -- the posting
+                          ,MixedAmount         -- balance so far
+                          )
+
 -- | Print a register report.
 register :: [Opt] -> [String] -> Journal -> IO ()
 register opts args j = do
   t <- getCurrentLocalTime
-  putStr $ showRegisterReport opts (optsToFilterSpec opts args t) j
+  putStr $ registerReportAsText opts $ registerReport opts (optsToFilterSpec opts args t) j
 
--- | Generate the register report, which is a list of postings with transaction
--- info and a running balance.
-showRegisterReport :: [Opt] -> FilterSpec -> Journal -> String
-showRegisterReport opts filterspec j = showPostingsWithBalance ps nullposting startbal
+-- | Render a register report as plain text suitable for console output.
+registerReportAsText :: [Opt] -> RegisterReport -> String
+registerReportAsText opts = unlines . map (registerReportItemAsText opts)
+
+-- | Render one register report line item as plain text. Eg:
+-- @
+-- date (10)  description (20)     account (22)            amount (11)  balance (12)
+-- DDDDDDDDDD dddddddddddddddddddd aaaaaaaaaaaaaaaaaaaaaa  AAAAAAAAAAA AAAAAAAAAAAA
+-- ^ displayed for first postings^
+--   only, otherwise blank
+-- @
+registerReportItemAsText :: [Opt] -> RegisterReportItem -> String
+registerReportItemAsText _ (dd, p, b) = concatTopPadded [datedesc, pstr, " ", bal]
     where
+      datedesc = case dd of Nothing -> replicate datedescwidth ' '
+                            Just (da, de) -> printf "%s %s " date desc
+                                where
+                                  date = showDate da
+                                  desc = printf ("%-"++(show descwidth)++"s") $ elideRight descwidth de :: String
+          where
+            descwidth = datedescwidth - datewidth - 2
+            datedescwidth = 32
+            datewidth = 10
+      pstr = showPostingForRegister p
+      bal = padleft 12 (showMixedAmountOrZeroWithoutPrice b)
+
+showPostingWithBalanceForVty showtxninfo p b = registerReportItemAsText [] $ mkitem showtxninfo p b
+
+-- | Get a register report with the specified options for this journal.
+registerReport :: [Opt] -> FilterSpec -> Journal -> RegisterReport
+registerReport opts fspec j = getitems ps nullposting startbal
+    where
       ps | interval == NoInterval = displayableps
-         | otherwise             = summarisePostings interval depth empty filterspan displayableps
+         | otherwise              = summarisePostingsByInterval interval depth empty filterspan displayableps
+      (precedingps, displayableps, _) = postingsMatchingDisplayExpr (displayExprFromOpts opts)
+                                        $ depthClipPostings depth
+                                        $ journalPostings
+                                        $ filterJournalPostings fspec{depth=Nothing} j
       startbal = sumPostings precedingps
-      (precedingps,displayableps,_) =
-          postingsMatchingDisplayExpr (displayExprFromOpts opts) $ journalPostings $ filterJournalPostings filterspec j
+      filterspan = datespan fspec
       (interval, depth, empty) = (intervalFromOpts opts, depthFromOpts opts, Empty `elem` opts)
-      filterspan = datespan filterspec
 
--- | Convert a list of postings into summary postings, one per interval.
-summarisePostings :: Interval -> Maybe Int -> Bool -> DateSpan -> [Posting] -> [Posting]
-summarisePostings interval depth empty filterspan ps = concatMap summarisespan $ splitSpan interval reportspan
+-- | Generate register report line items.
+getitems :: [Posting] -> Posting -> MixedAmount -> [RegisterReportItem]
+getitems [] _ _ = []
+getitems (p:ps) pprev b = i:(getitems ps p b')
     where
-      summarisespan s = summarisePostingsInDateSpan s depth empty (postingsinspan s)
-      postingsinspan s = filter (isPostingInDateSpan s) ps
-      dataspan = postingsDateSpan ps
-      reportspan | empty = filterspan `orDatesFrom` dataspan
-                 | otherwise = dataspan
+      i = mkitem isfirst p b'
+      isfirst = ptransaction p /= ptransaction pprev
+      b' = b + pamount p
 
+-- | Generate one register report line item, from a flag indicating
+-- whether to include transaction info, a posting, and the current running
+-- balance.
+mkitem :: Bool -> Posting -> MixedAmount -> RegisterReportItem
+mkitem False p b = (Nothing, p, b)
+mkitem True p b = (ds, p, b)
+    where ds = case ptransaction p of Just (Transaction{tdate=da,tdescription=de}) -> Just (da,de)
+                                      Nothing -> Just (nulldate,"")
+
 -- | Date-sort and split a list of postings into three spans - postings matched
 -- by the given display expression, and the preceding and following postings.
 postingsMatchingDisplayExpr :: Maybe String -> [Posting] -> ([Posting],[Posting],[Posting])
@@ -89,6 +140,19 @@
   compareop = choice $ map (try . string) ["<=",">=","==","<","=",">"]
 
 -- XXX confusing, refactor
+
+-- | Convert a list of postings into summary postings. Summary postings
+-- are one per account per interval and aggregated to the specified depth
+-- if any.
+summarisePostingsByInterval :: Interval -> Maybe Int -> Bool -> DateSpan -> [Posting] -> [Posting]
+summarisePostingsByInterval interval depth empty filterspan ps = concatMap summarisespan $ splitSpan interval reportspan
+    where
+      summarisespan s = summarisePostingsInDateSpan s depth empty (postingsinspan s)
+      postingsinspan s = filter (isPostingInDateSpan s) ps
+      dataspan = postingsDateSpan ps
+      reportspan | empty = filterspan `orDatesFrom` dataspan
+                 | otherwise = dataspan
+
 -- | Given a date span (representing a reporting interval) and a list of
 -- postings within it: aggregate the postings so there is only one per
 -- account, and adjust their date/description so that they will render
@@ -100,8 +164,8 @@
 -- When a depth argument is present, postings to accounts of greater
 -- depth are aggregated where possible.
 -- 
--- The showempty flag forces the display of a zero-posting span
--- and also zero-posting accounts within the span.
+-- The showempty flag includes spans with no postings and also postings
+-- with 0 amount.
 summarisePostingsInDateSpan :: DateSpan -> Maybe Int -> Bool -> [Posting] -> [Posting]
 summarisePostingsInDateSpan (DateSpan b e) depth showempty ps
     | null ps && (isNothing b || isNothing e) = []
@@ -124,44 +188,20 @@
       balancetoshowfor a =
           (if isclipped a then inclbalof else exclbalof) (if null a then "top" else a)
 
-{- |
-Show postings one per line, plus transaction info for the first posting of
-each transaction, and a running balance. Eg:
+-- | Clip the account names to the specified depth in a list of postings.
+depthClipPostings :: Maybe Int -> [Posting] -> [Posting]
+depthClipPostings depth = map (depthClipPosting depth)
 
-@
-date (10)  description (20)     account (22)            amount (11)  balance (12)
-DDDDDDDDDD dddddddddddddddddddd aaaaaaaaaaaaaaaaaaaaaa  AAAAAAAAAAA AAAAAAAAAAAA
-                                aaaaaaaaaaaaaaaaaaaaaa  AAAAAAAAAAA AAAAAAAAAAAA
-@
--}
-showPostingsWithBalance :: [Posting] -> Posting -> MixedAmount -> String
-showPostingsWithBalance [] _ _ = ""
-showPostingsWithBalance (p:ps) pprev bal = this ++ showPostingsWithBalance ps p bal'
-    where
-      this = showPostingWithBalance isfirst p bal'
-      isfirst = ptransaction p /= ptransaction pprev
-      bal' = bal + pamount p
+-- | Clip a posting's account name to the specified depth.
+depthClipPosting :: Maybe Int -> Posting -> Posting
+depthClipPosting Nothing p = p
+depthClipPosting (Just d) p@Posting{paccount=a} = p{paccount=clipAccountName d a}
 
--- | Show one posting and running balance, with or without transaction info.
-showPostingWithBalance :: Bool -> Posting -> MixedAmount -> String
-showPostingWithBalance withtxninfo p b = concatTopPadded [txninfo, pstr, " ", bal] ++ "\n"
-    where
-      ledger3ishlayout = False
-      datedescwidth = if ledger3ishlayout then 34 else 32
-      txninfo = if withtxninfo then printf "%s %s " date desc else replicate datedescwidth ' '
-      date = showDate da
-      datewidth = 10
-      descwidth = datedescwidth - datewidth - 2
-      desc = printf ("%-"++(show descwidth)++"s") $ elideRight descwidth de :: String
-      pstr = showPostingForRegister p
-      bal = padleft 12 (showMixedAmountOrZeroWithoutPrice b)
-      (da,de) = case ptransaction p of Just (Transaction{tdate=da',tdescription=de'}) -> (da',de')
-                                       Nothing -> (nulldate,"")
 
 tests_Register :: Test
 tests_Register = TestList [
 
-         "summarisePostings" ~: do
-           summarisePostings Quarterly Nothing False (DateSpan Nothing Nothing) [] ~?= []
+         "summarisePostingsByInterval" ~: do
+           summarisePostingsByInterval Quarterly Nothing False (DateSpan Nothing Nothing) [] ~?= []
 
         ]
diff --git a/Hledger/Cli/Commands/Vty.hs b/Hledger/Cli/Commands/Vty.hs
--- a/Hledger/Cli/Commands/Vty.hs
+++ b/Hledger/Cli/Commands/Vty.hs
@@ -117,13 +117,13 @@
 posY a = scrollY a + cursorY a
 
 setCursorY, setScrollY, setPosY :: Int -> AppState -> AppState
-setCursorY _ AppState{alocs=[]} = error "shouldn't happen" -- silence warnings
+setCursorY _ AppState{alocs=[]} = error' "shouldn't happen" -- silence warnings
 setCursorY y a@AppState{alocs=(l:locs)} = a{alocs=(l':locs)} where l' = setLocCursorY y l
 
-setScrollY _ AppState{alocs=[]} = error "shouldn't happen" -- silence warnings
+setScrollY _ AppState{alocs=[]} = error' "shouldn't happen" -- silence warnings
 setScrollY y a@AppState{alocs=(l:locs)} = a{alocs=(l':locs)} where l' = setLocScrollY y l
 
-setPosY _ AppState{alocs=[]}    = error "shouldn't happen" -- silence warnings
+setPosY _ AppState{alocs=[]}    = error' "shouldn't happen" -- silence warnings
 setPosY y a@AppState{alocs=(l:locs)} = a{alocs=(l':locs)}
     where 
       l' = setLocScrollY sy $ setLocCursorY cy l
@@ -229,8 +229,8 @@
 updateData :: LocalTime -> AppState -> AppState
 updateData t a@AppState{aopts=opts,ajournal=j} =
     case screen a of
-      BalanceScreen  -> a{abuf=lines $ showBalanceReport opts fspec j}
-      RegisterScreen -> a{abuf=lines $ showRegisterReport opts fspec j}
+      BalanceScreen  -> a{abuf=lines $ balanceReportAsText opts $ balanceReport opts fspec j}
+      RegisterScreen -> a{abuf=lines $ registerReportAsText opts $ registerReport opts fspec j}
       PrintScreen    -> a{abuf=lines $ showTransactions fspec j}
     where fspec = optsToFilterSpec opts (currentArgs a) t
 
@@ -289,7 +289,7 @@
     where
       p = headDef nullposting $ filter ismatch $ journalPostings j
       ismatch p = postingDate p == parsedate (take 10 datedesc)
-                  && take 70 (showPostingWithBalance False p nullmixedamt) == (datedesc ++ acctamt)
+                  && take 70 (showPostingWithBalanceForVty False p nullmixedamt) == (datedesc ++ acctamt)
       datedesc = take 32 $ fromMaybe "" $ find (not . (" " `isPrefixOf`)) $ headDef "" rest : reverse above
       acctamt = drop 32 $ headDef "" rest
       (above,rest) = splitAt y buf
diff --git a/Hledger/Cli/Commands/Web.hs b/Hledger/Cli/Commands/Web.hs
--- a/Hledger/Cli/Commands/Web.hs
+++ b/Hledger/Cli/Commands/Web.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE CPP, TypeFamilies, QuasiQuotes, TemplateHaskell #-}
+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses, FlexibleContexts #-}
 {-| 
 A web-based UI.
 -}
@@ -7,11 +8,15 @@
 where
 import Control.Concurrent (forkIO, threadDelay)
 import Control.Applicative ((<$>), (<*>))
+import Control.Failure
 import Data.Either
-import System.FilePath ((</>))
+import System.FilePath ((</>), takeFileName)
 import System.IO.Storage (withStore, putValue, getValue)
 import Text.ParserCombinators.Parsec (parse)
 import Yesod
+import Yesod.Helpers.Static
+import Text.Hamlet
+import Text.Hamlet.RT
 
 import Hledger.Cli.Commands.Add (journalAddTransaction)
 import Hledger.Cli.Commands.Balance
@@ -19,7 +24,8 @@
 import Hledger.Cli.Commands.Register
 import Hledger.Cli.Options hiding (value)
 import Hledger.Cli.Utils
-import Hledger.Data
+import Hledger.Cli.Version (version)
+import Hledger.Data hiding (today)
 import Hledger.Read (journalFromPathAndString)
 import Hledger.Read.Journal (someamount)
 #ifdef MAKE
@@ -29,17 +35,72 @@
 #endif
 
 
-defhost = "localhost"
-defport = 5000
-defbaseurl = printf "http://%s:%d" defhost defport :: String
+defhost           = "localhost"
+defport           = 5000
 browserstartdelay = 100000 -- microseconds
-hledgerurl = "http://hledger.org"
-manualurl = hledgerurl++"/MANUAL.html"
+hledgerorgurl     = "http://hledger.org"
+manualurl         = hledgerorgurl++"/MANUAL.html"
 
+data HledgerWebApp = HledgerWebApp {
+      appRoot    :: String
+     ,appDir     :: FilePath
+     ,appOpts    :: [Opt]
+     ,appArgs    :: [String]
+     ,appJournal :: Journal
+     ,appStatic  :: Static
+     }
+
+type Handler = GHandler HledgerWebApp HledgerWebApp
+
+mkYesod "HledgerWebApp" [$parseRoutes|
+/static          StaticR           Static appStatic
+/                IndexR            GET
+/journalonly     JournalOnlyR      GET POST
+/registeronly    RegisterOnlyR     GET
+/accounts        AccountsOnlyR     GET
+/journal         JournalR          GET POST
+/register        RegisterR         GET POST
+/addformrt       AddformRTR        GET
+|]
+
+style_css       = StaticRoute ["style.css"] []
+hledger_js      = StaticRoute ["hledger.js"] []
+jquery_js       = StaticRoute ["jquery.js"] []
+jquery_url_js   = StaticRoute ["jquery.url.js"] []
+dhtmlxcommon_js = StaticRoute ["dhtmlxcommon.js"] []
+dhtmlxcombo_js  = StaticRoute ["dhtmlxcombo.js"] []
+
+instance Yesod HledgerWebApp where approot = appRoot
+
+defaultroute = JournalR
+
+-- | A bundle of useful data passed to templates.
+data TemplateData = TD {
+     here         :: HledgerWebAppRoute -- ^ the current page's route
+    ,title        :: String             -- ^ page's title
+    ,msg          :: Maybe Html         -- ^ transient message
+    ,a            :: String             -- ^ a (acct/desc filter pattern) parameter
+    ,p            :: String             -- ^ p (period expression) parameter
+    ,j            :: Journal            -- ^ the current journal
+    ,today        :: Day                -- ^ the current day
+    }
+
+mktd = TD {
+      here = IndexR
+     ,title = "hledger"
+     ,msg = Nothing
+     ,a = ""
+     ,p = ""
+     ,j = nulljournal
+     ,today = ModifiedJulianDay 0
+     }
+
+-- | The web command.
 web :: [Opt] -> [String] -> Journal -> IO ()
 web opts args j = do
-  let baseurl = fromMaybe defbaseurl $ baseUrlFromOpts opts
-      port = fromMaybe defport $ portFromOpts opts
+  let host    = defhost
+      port    = fromMaybe defport $ portFromOpts opts
+      baseurl = fromMaybe (printf "http://%s:%d" host port) $ baseUrlFromOpts opts
   unless (Debug `elem` opts) $ forkIO (browser baseurl) >> return ()
   server baseurl port opts args j
 
@@ -53,249 +114,404 @@
 server :: String -> Int -> [Opt] -> [String] -> Journal -> IO ()
 server baseurl port opts args j = do
     printf "starting web server on port %d with base url %s\n" port baseurl
-    fp <- getDataFileName "web"
+    dir <- getDataFileName "web"
     let app = HledgerWebApp{
-               appOpts=opts
+               appRoot=baseurl
+              ,appDir=dir
+              ,appStatic=fileLookupDir (dir </> "static") $ typeByExt -- ++[("hamlet","text/plain")]
+              ,appOpts=opts
               ,appArgs=args
               ,appJournal=j
-              ,appWebdir=fp
-              ,appRoot=baseurl
               }
     withStore "hledger" $ do
      putValue "hledger" "journal" j
-     basicHandler port app
+     basicHandler' port Nothing app
 
-data HledgerWebApp = HledgerWebApp {
-      appOpts::[Opt]
-     ,appArgs::[String]
-     ,appJournal::Journal
-     ,appWebdir::FilePath
-     ,appRoot::String
-     }
+-- | Gather all the stuff we want for a typical hledger web request handler.
+getHandlerParameters :: Handler
+                       (String, String, [Opt], FilterSpec, Journal, Maybe Html, HledgerWebAppRoute)
+getHandlerParameters = do
+  Just here <- getCurrentRoute
+  (a, p, opts, fspec) <- getReportParameters
+  (j, err) <- getLatestJournal opts
+  msg <- getMessage' err
+  return (a, p, opts, fspec, j, msg, here)
+    where
+      -- | Get current report parameters for this request.
+      getReportParameters :: Handler (String, String, [Opt], FilterSpec)
+      getReportParameters = do
+          app <- getYesod
+          t <- liftIO $ getCurrentLocalTime
+          a <- fromMaybe "" <$> lookupGetParam "x"
+          p <- fromMaybe "" <$> lookupGetParam "p"
+          let opts = appOpts app ++ [Period p]
+              args = appArgs app ++ [a]
+              fspec = optsToFilterSpec opts args t
+          return (a, p, opts, fspec)
 
-mkYesod "HledgerWebApp" [$parseRoutes|
-/             IndexPage        GET
-/style.css    StyleCss         GET
-/journal      JournalPage      GET POST
-/edit         EditPage         GET POST
-/register     RegisterPage     GET
-/balance      BalancePage      GET
-|]
+      -- | Update our copy of the journal if the file changed. If there is an
+      -- error while reloading, keep the old one and return the error, and set a
+      -- ui message.
+      getLatestJournal :: [Opt] -> Handler (Journal, Maybe String)
+      getLatestJournal opts = do
+        j <- liftIO $ fromJust `fmap` getValue "hledger" "journal"
+        (jE, changed) <- liftIO $ journalReloadIfChanged opts j
+        if not changed
+         then return (j,Nothing)
+         else case jE of
+                Right j' -> do liftIO $ putValue "hledger" "journal" j'
+                               return (j',Nothing)
+                Left e  -> do setMessage $ string "error while reading" {- ++ ": " ++ e-}
+                              return (j, Just e)
 
-instance Yesod HledgerWebApp where approot = appRoot
+      -- | Helper to work around a yesod feature (can't set and get a message in the same request.)
+      getMessage' :: Maybe String -> Handler (Maybe Html)
+      getMessage' newmsgstr = do
+        oldmsg <- getMessage
+        return $ maybe oldmsg (Just . string) newmsgstr
 
-getIndexPage :: Handler HledgerWebApp ()
-getIndexPage = redirect RedirectTemporary JournalPage
+----------------------------------------------------------------------
+-- handlers & templates
 
-getStyleCss :: Handler HledgerWebApp ()
-getStyleCss = do
-    app <- getYesod
-    let dir = appWebdir app
-    sendFile "text/css" $ dir </> "style.css"
+getIndexR :: Handler ()
+getIndexR = redirect RedirectTemporary defaultroute
 
-getJournalPage :: Handler HledgerWebApp RepHtml
-getJournalPage = withLatestJournalRender (const showTransactions)
+----------------------------------------------------------------------
 
-getRegisterPage :: Handler HledgerWebApp RepHtml
-getRegisterPage = withLatestJournalRender showRegisterReport
+-- | A combined accounts and journal view.
+getJournalR :: Handler RepHtml
+getJournalR = do
+  (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
+  today <- liftIO getCurrentDay
+  -- app <- getYesod
+  -- t <- liftIO $ getCurrentLocalTime
+  let -- args = appArgs app
+      -- fspec' = optsToFilterSpec opts args t
+      br = balanceReportAsHtml opts td $ balanceReport opts fspec j
+      jr = journalReportAsHtml opts td $ journalReport opts fspec j
+      td = mktd{here=here, title="hledger journal", msg=msg, a=a, p=p, j=j, today=today}
+      editform' = editform td $ jtext j
+  hamletToRepHtml $ pageLayout td [$hamlet|
+%div.ledger
+ %div.accounts!style=float:left;  ^br^
+ ^navlinks.td^
+ ^addform.td^
+ ^editform'^
+ ^importform^
+ %div#transactions.journal
+  ^filterform.td^
+  ^jr^
+|]
 
-getBalancePage :: Handler HledgerWebApp RepHtml
-getBalancePage = withLatestJournalRender showBalanceReport
+postJournalR :: Handler RepPlain
+postJournalR = postJournalOnlyR
 
-withLatestJournalRender :: ([Opt] -> FilterSpec -> Journal -> String) -> Handler HledgerWebApp RepHtml
-withLatestJournalRender reportfn = do
-    app <- getYesod
-    t <- liftIO $ getCurrentLocalTime
-    a <- fromMaybe "" <$> lookupGetParam "a"
-    p <- fromMaybe "" <$> lookupGetParam "p"
-    let opts = appOpts app ++ [Period p]
-        args = appArgs app ++ [a]
-        fspec = optsToFilterSpec opts args t
-    -- reload journal if changed, displaying any error as a message
-    j <- liftIO $ fromJust `fmap` getValue "hledger" "journal"
-    (jE, changed) <- liftIO $ journalReloadIfChanged opts j
-    let (j', err) = either (\e -> (j,e)) (\j -> (j,"")) jE
-    when (changed && null err) $ liftIO $ putValue "hledger" "journal" j'
-    if (changed && not (null err)) then setMessage $ string "error while reading"
-                                 else return ()
-    -- run the specified report using this request's params
-    let s = reportfn opts fspec j'
-    -- render the standard template
-    msg' <- getMessage
-    -- XXX work around a bug, can't get the message we set above
-    let msg = if null err then msg' else Just $ string $ printf "Error while reading %s" (filepath j')
-    Just here <- getCurrentRoute
-    hamletToRepHtml $ template here msg a p "hledger" s
+----------------------------------------------------------------------
 
-template :: HledgerWebAppRoute -> Maybe (Html ()) -> String -> String
-         -> String -> String -> Hamlet HledgerWebAppRoute
-template here msg a p title content = [$hamlet|
-!!!
-%html
- %head
-  %title $string.title$
-  %meta!http-equiv=Content-Type!content=$string.metacontent$
-  %link!rel=stylesheet!type=text/css!href=@stylesheet@!media=all
- %body
-  ^navbar'^
-  #messages $m$
-  ^addform'^
-  #content
-   %pre $string.content$
+-- | A combined accounts and register view.
+getRegisterR :: Handler RepHtml
+getRegisterR = do
+  (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
+  today <- liftIO getCurrentDay
+  -- app <- getYesod
+  -- t <- liftIO $ getCurrentLocalTime
+  let -- args = appArgs app
+      -- opts' = Empty:opts
+      -- fspec' = optsToFilterSpec opts' args t
+      br = balanceReportAsHtml opts td $ balanceReport opts fspec j
+      rr = registerReportAsHtml opts td $ registerReport opts fspec j
+      td = mktd{here=here, title="hledger register", msg=msg, a=a, p=p, j=j, today=today}
+      editform' = editform td $ jtext j
+  hamletToRepHtml $ pageLayout td [$hamlet|
+%div.ledger
+ %div.accounts!style=float:left;  ^br^
+ ^navlinks.td^
+ ^addform.td^
+ ^editform'^
+ ^importform^
+ %div#transactions.register
+  ^filterform.td^
+  ^rr^
 |]
- where m = fromMaybe (string "") msg
-       navbar' = navbar here a p
-       addform' | here == JournalPage = addform
-                | otherwise = nulltemplate
-       stylesheet = StyleCss
-       metacontent = "text/html; charset=utf-8"
 
-nulltemplate = [$hamlet||]
+postRegisterR :: Handler RepPlain
+postRegisterR = postJournalOnlyR
 
-navbar :: HledgerWebAppRoute -> String -> String -> Hamlet HledgerWebAppRoute
-navbar here a p = [$hamlet|
- #navbar
-  %a.toprightlink!href=$string.hledgerurl$ hledger.org
-  \ $
-  %a.toprightlink!href=$string.manualurl$ manual
-  \ $
-  ^navlinks'^
-  ^searchform'^
-|]
- where navlinks' = navlinks here a p
-       searchform' = searchform here a p
+----------------------------------------------------------------------
 
-navlinks :: HledgerWebAppRoute -> String -> String -> Hamlet HledgerWebAppRoute
-navlinks here a p = [$hamlet|
- #navlinks
-  ^journallink^ $
-  (^editlink^) $
-  | ^registerlink^ $
-  | ^balancelink^ $
+-- | A simple accounts and balances view like hledger balance.
+getAccountsOnlyR :: Handler RepHtml
+getAccountsOnlyR = do
+  (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
+  today <- liftIO getCurrentDay
+  let td = mktd{here=here, title="hledger accounts", msg=msg, a=a, p=p, j=j, today=today}
+  hamletToRepHtml $ pageLayout td $ balanceReportAsHtml opts td $ balanceReport opts fspec j
+
+-- | Render a balance report as HTML.
+balanceReportAsHtml :: [Opt] -> TemplateData -> BalanceReport -> Hamlet HledgerWebAppRoute
+balanceReportAsHtml _ td@TD{here=here,a=a,p=p} (items,total) = [$hamlet|
+%table.balancereport
+ %tr
+  %td
+   ^accountsheading^
+ $forall items i
+  ^itemAsHtml' i^
+ %tr.totalrule
+  %td!colspan=2
+ %tr
+  %td
+  %td!align=right $mixedAmountAsHtml.total$
 |]
  where
-  journallink = navlink here "journal" JournalPage
-  editlink = navlink here "edit" EditPage
-  registerlink = navlink here "register" RegisterPage
-  balancelink = navlink here "balance" BalancePage
-  navlink here s dest = [$hamlet|%a.$style$!href=@?u@ $string.s$|]
-   where u = (dest, concat [(if null a then [] else [("a", a)])
-                           ,(if null p then [] else [("p", p)])])
-         style | here == dest = string "navlinkcurrent"
-               | otherwise = string "navlink"
+   accountsheading = [$hamlet|
+                      accounts
+                      \ $
+                      %span#showmoreaccounts ^showmore^ ^showall^
+                      <br />
+                      <br />
+                      |]
+       where
+         filteringaccts = not $ null a
+         showmore = case (filteringaccts, items) of
+                      -- cunning parent account logic
+                      (True, ((acct, _, _, _):_)) ->
+                          let a' = if isAccountRegex a then a else acct
+                              a'' = accountNameToAccountRegex $ parentAccountName $ accountRegexToAccountName a'
+                              parenturl = (here, [("y",a''), ("p",p)])
+                          in [$hamlet|
+                              \ | $
+                              %a!href=@?parenturl@ show more &uarr;
+                              |]
+                      _ -> nulltemplate
+         showall = if filteringaccts
+                    then [$hamlet|
+                          \ | $
+                          %a!href=@?allurl@ show all
+                          |]
+                    else nulltemplate
+             where allurl = (here, [("p",p)])
+   itemAsHtml' = itemAsHtml td
+   itemAsHtml :: TemplateData -> BalanceReportItem -> Hamlet HledgerWebAppRoute
+   itemAsHtml TD{p=p} (acct, adisplay, adepth, abal) = [$hamlet|
+     %tr.item.$current$
+      %td.account
+       $indent$
+       %a!href=$aurl$ $adisplay$
+      %td.balance!align=right $mixedAmountAsHtml.abal$
+     |] where
+       current = "" -- if not (null a) && containsRegex a acct then "current" else ""
+       indent = preEscapedString $ concat $ replicate (2 * adepth) "&nbsp;"
+       aurl = printf ".?a=%s%s" (accountNameToAccountRegex acct) p' :: String
+       p' = if null p then "" else printf "&p=%s" p
 
-searchform :: HledgerWebAppRoute -> String -> String -> Hamlet HledgerWebAppRoute
-searchform here a p = [$hamlet|
- %form#searchform!method=GET
-  filter by: $
-  %input!name=a!size=20!value=$string.a$
-  ^ahelp^ $
-  in period: $
-  %input!name=p!size=20!value=$string.p$
-  ^phelp^ $
-  %input!type=submit!value=filter
-  ^resetlink^
+accountNameToAccountRegex :: String -> String
+accountNameToAccountRegex "" = ""
+accountNameToAccountRegex a = printf "^%s(:|$)" a
+
+accountRegexToAccountName :: String -> String
+accountRegexToAccountName = gsubRegexPR "^\\^(.*?)\\(:\\|\\$\\)$" "\\1"
+
+isAccountRegex  :: String -> Bool
+isAccountRegex s = take 1 s == "^" && (take 5 $ reverse s) == ")$|:("
+
+----------------------------------------------------------------------
+
+-- | A basic journal view, like hledger print, with editing.
+getJournalOnlyR :: Handler RepHtml
+getJournalOnlyR = do
+  (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
+  today <- liftIO getCurrentDay
+  let td = mktd{here=here, title="hledger journal", msg=msg, a=a, p=p, j=j, today=today}
+      editform' = editform td $ jtext j
+      txns = journalReportAsHtml opts td $ journalReport opts fspec j
+  hamletToRepHtml $ pageLayout td [$hamlet|
+%div.journal
+ %div.nav2
+  %a#addformlink!href!onclick="return addformToggle()" add one transaction
+  \ | $
+  %a#editformlink!href!onclick="return editformToggle()" edit the whole journal
+ ^addform.td^
+ ^editform'^
+ #transactions ^txns^
 |]
- where
-  ahelp = helplink "filter-patterns" "?"
-  phelp = helplink "period-expressions" "?"
-  resetlink
-   | null a && null p = nulltemplate
-   | otherwise        = [$hamlet|%span#resetlink $
-                                  %a!href=@here@ reset|]
 
-helplink topic label = [$hamlet|%a!href=$string.u$ $string.label$|]
-    where u = manualurl ++ if null topic then "" else '#':topic
+-- | Render a journal report as HTML.
+journalReportAsHtml :: [Opt] -> TemplateData -> JournalReport -> Hamlet HledgerWebAppRoute
+journalReportAsHtml _ td items = [$hamlet|
+%table.journalreport
+ $forall number.items i
+  ^itemAsHtml' i^
+|]
+ where
+   number = zip [1..]
+   itemAsHtml' = itemAsHtml td
+   itemAsHtml :: TemplateData -> (Int, JournalReportItem) -> Hamlet HledgerWebAppRoute
+   itemAsHtml _ (n, t) = [$hamlet|
+     %tr.item.$evenodd$
+      %td.transaction
+       %pre $txn$
+     |] where
+       evenodd = if even n then "even" else "odd"
+       txn = trimnl $ showTransaction t where trimnl = reverse . dropWhile (=='\n') . reverse
 
-addform :: Hamlet HledgerWebAppRoute
-addform = [$hamlet|
- %form!method=POST
-  %table.form#addform!cellpadding=0!cellspacing=0!border=0
-   %tr.formheading
-    %td!colspan=4
-     %span#formheading Add a transaction:
+addform :: TemplateData -> Hamlet HledgerWebAppRoute
+addform td = [$hamlet|
+%script!type=text/javascript
+ $$(document).ready(function() {
+    /* dhtmlxcombo setup */
+    window.dhx_globalImgPath="../static/images/";
+    var desccombo  = new dhtmlXCombo("description");
+    var acct1combo = new dhtmlXCombo("account1");
+    var acct2combo = new dhtmlXCombo("account2");
+    desccombo.enableFilteringMode(true);
+    acct1combo.enableFilteringMode(true);
+    acct2combo.enableFilteringMode(true);
+    desccombo.setSize(300);
+    acct1combo.setSize(300);
+    acct2combo.setSize(300);
+    /* desccombo.enableOptionAutoHeight(true, 20); */
+    /* desccombo.setOptionHeight(200); */
+ });
+%form#addform!method=POST!style=display:none;
+  %table.form
    %tr
     %td!colspan=4
-     %table!cellpadding=0!cellspacing=0!border=0
+     %table
       %tr#descriptionrow
        %td
         Date:
        %td
-        %input!size=15!name=date!value=$string.date$
-       %td
+        %input.textinput!size=15!name=date!value=$date$
+       %td!style=padding-left:1em;
         Description:
        %td
-        %input!size=35!name=description!value=$string.desc$
+        %select!id=description!name=description
+         %option
+         $forall descriptions d
+          %option!value=$d$ $d$
       %tr.helprow
        %td
        %td
-        #help $string.datehelp$ ^datehelplink^ $
+        .help $datehelp$ $
        %td
        %td
-        #help $string.deschelp$
-   ^transactionfields1^
-   ^transactionfields2^
+        .help $deschelp$
+   ^postingsfields.td^
    %tr#addbuttonrow
     %td!colspan=4
-     %input!type=submit!value=$string.addlabel$
+     %input!type=hidden!name=action!value=add
+     %input!type=submit!name=submit!value="add transaction"
 |]
  where
-  datehelplink = helplink "dates" "..."
-  datehelp = "eg: 7/20, 2010/1/1, "
+  -- datehelplink = helplink "dates" "..."
+  datehelp = "eg: 2010/7/20"
   deschelp = "eg: supermarket (optional)"
-  addlabel = "add transaction"
   date = "today"
-  desc = ""
-  transactionfields1 = transactionfields 1
-  transactionfields2 = transactionfields 2
+  descriptions = sort $ nub $ map tdescription $ jtxns $ j td
 
--- transactionfields :: Int -> Hamlet String
-transactionfields n = [$hamlet|
+postingsfields :: TemplateData -> Hamlet HledgerWebAppRoute
+postingsfields td = [$hamlet|
+ ^p1^
+ ^p2^
+|]
+  where
+    p1 = postingfields td 1
+    p2 = postingfields td 2
+
+postingfields :: TemplateData -> Int -> Hamlet HledgerWebAppRoute
+postingfields td n = [$hamlet|
  %tr#postingrow
-  %td!align=right
-   $string.label$:
+  %td!align=right $acctlabel$:
   %td
-   %input!size=35!name=$string.acctvar$!value=$string.acct$
+   %select!id=$acctvar$!name=$acctvar$
+    %option
+    $forall acctnames a
+     %option!value=$a$ $a$
   ^amtfield^
  %tr.helprow
   %td
   %td
-   #help $string.accthelp$
+   .help $accthelp$
   %td
   %td
-   #help $string.amthelp$
+   .help $amthelp$
 |]
  where
-  label | n == 1    = "To account"
-        | otherwise = "From account"
-  accthelp | n == 1    = "eg: expenses:food"
-           | otherwise = "eg: assets:bank:checking"
-  amtfield | n == 1 = [$hamlet|
-                       %td
+  numbered = (++ show n)
+  acctvar = numbered "account"
+  amtvar = numbered "amount"
+  acctnames = sort $ journalAccountNamesUsed $ j td
+  (acctlabel, accthelp, amtfield, amthelp)
+       | n == 1     = ("To account"
+                     ,"eg: expenses:food"
+                     ,[$hamlet|
+                       %td!style=padding-left:1em;
                         Amount:
                        %td
-                        %input!size=15!name=$string.amtvar$!value=$string.amt$
+                        %input.textinput!size=15!name=$amtvar$!value=""
                        |]
-           | otherwise = nulltemplate
-  amthelp | n == 1    = "eg: 5, $6, €7.01"
-          | otherwise = ""
-  acct = ""
-  amt = ""
-  numbered = (++ show n)
-  acctvar = numbered "accountname"
-  amtvar = numbered "amount"
+                     ,"eg: $6"
+                     )
+       | otherwise = ("From account"
+                     ,"eg: assets:bank:checking"
+                     ,nulltemplate
+                     ,""
+                     )
 
-postJournalPage :: Handler HledgerWebApp RepPlain
-postJournalPage = do
+editform :: TemplateData -> String -> Hamlet HledgerWebAppRoute
+editform _ content = [$hamlet|
+ %form#editform!method=POST!style=display:none;
+  %table.form#editform
+   %tr
+    %td!colspan=2
+     %textarea!name=text!rows=30!cols=80
+      $content$
+   %tr#addbuttonrow
+    %td
+     %span.help ^formathelp^
+    %td!align=right
+     %span.help Are you sure ? This will overwrite the journal. $
+     %input!type=hidden!name=action!value=edit
+     %input!type=submit!name=submit!value="save journal"
+     \ or $
+     %a!href!onclick="return editformToggle()" cancel
+|]
+  where
+    formathelp = helplink "file-format" "file format help"
+
+importform :: Hamlet HledgerWebAppRoute
+importform = [$hamlet|
+ %form#importform!method=POST!style=display:none;
+  %table.form
+   %tr
+    %td
+     %input!type=file!name=file
+     %input!type=hidden!name=action!value=import
+     %input!type=submit!name=submit!value="import from file"
+     \ or $
+     %a!href!onclick="return importformToggle()" cancel
+|]
+
+postJournalOnlyR :: Handler RepPlain
+postJournalOnlyR = do
+  action <- runFormPost' $ maybeStringInput "action"
+  case action of Just "edit"   -> postEditForm
+                 Just "import" -> postImportForm
+                 _             -> postAddForm
+
+-- | Handle a journal add form post.
+postAddForm :: Handler RepPlain
+postAddForm = do
+  (_, _, opts, _, _, _, _) <- getHandlerParameters
   today <- liftIO getCurrentDay
   -- get form input values. M means a Maybe value.
   (dateM, descM, acct1M, amt1M, acct2M, amt2M) <- runFormPost'
     $ (,,,,,)
     <$> maybeStringInput "date"
     <*> maybeStringInput "description"
-    <*> maybeStringInput "accountname1"
+    <*> maybeStringInput "account1"
     <*> maybeStringInput "amount1"
-    <*> maybeStringInput "accountname2"
+    <*> maybeStringInput "account2"
     <*> maybeStringInput "amount2"
   -- supply defaults and parse date and amounts, or get errors.
   let dateE = maybe (Left "date required") (either (\e -> Left $ showDateParseError e) Right . fixSmartDateStrEither today) dateM
@@ -330,81 +546,18 @@
    Left errs -> do
     -- save current form values in session
     setMessage $ string $ intercalate "; " errs
-    redirect RedirectTemporary JournalPage
+    redirect RedirectTemporary RegisterR
 
    Right t -> do
     let t' = txnTieKnot t -- XXX move into balanceTransaction
     j <- liftIO $ fromJust `fmap` getValue "hledger" "journal"
-    liftIO $ journalAddTransaction j t'
+    liftIO $ journalAddTransaction j opts t'
     setMessage $ string $ printf "Added transaction:\n%s" (show t')
-    redirect RedirectTemporary JournalPage
-
-getEditPage :: Handler HledgerWebApp RepHtml
-getEditPage = do
-    -- app <- getYesod
-    -- t <- liftIO $ getCurrentLocalTime
-    a <- fromMaybe "" <$> lookupGetParam "a"
-    p <- fromMaybe "" <$> lookupGetParam "p"
-        -- opts = appOpts app ++ [Period p]
-        -- args = appArgs app ++ [a]
-        -- fspec = optsToFilterSpec opts args t
-    -- reload journal's text, without parsing, if changed
-    j <- liftIO $ fromJust `fmap` getValue "hledger" "journal"
-    changed <- liftIO $ journalFileIsNewer j
-    -- XXX readFile may throw an error
-    s <- liftIO $ if changed then readFile (filepath j) else return (jtext j)
-    -- render the page
-    msg <- getMessage
-    Just here <- getCurrentRoute
-    hamletToRepHtml $ template' here msg a p "hledger" s
-
-template' here msg a p title content = [$hamlet|
-!!!
-%html
- %head
-  %title $string.title$
-  %meta!http-equiv=Content-Type!content=$string.metacontent$
-  %link!rel=stylesheet!type=text/css!href=@stylesheet@!media=all
- %body
-  ^navbar'^
-  #messages $m$
-  ^editform'^
-|]
- where m = fromMaybe (string "") msg
-       navbar' = navbar here a p
-       stylesheet = StyleCss
-       metacontent = "text/html; charset=utf-8"
-       editform' = editform content
-
-editform :: String -> Hamlet HledgerWebAppRoute
-editform t = [$hamlet|
- %form!method=POST
-  %table.form#editform!cellpadding=0!cellspacing=0!border=0
-   %tr.formheading
-    %td!colspan=2
-     %span!style=float:right; ^formhelp^
-     %span#formheading Edit journal:
-   %tr
-    %td!colspan=2
-     %textarea!name=text!rows=30!cols=80
-      $string.t$
-   %tr#addbuttonrow
-    %td
-     %a!href=@JournalPage@ cancel
-    %td!align=right
-     %input!type=submit!value=$string.submitlabel$
-   %tr.helprow
-    %td
-    %td!align=right
-     #help $string.edithelp$
-|]
- where
-  submitlabel = "save journal"
-  formhelp = helplink "file-format" "file format help"
-  edithelp = "Are you sure ? All previous data will be replaced"
+    redirect RedirectTemporary RegisterR
 
-postEditPage :: Handler HledgerWebApp RepPlain
-postEditPage = do
+-- | Handle a journal edit form post.
+postEditForm :: Handler RepPlain
+postEditForm = do
   -- get form input values, or basic validation errors. E means an Either value.
   textM  <- runFormPost' $ maybeStringInput "text"
   let textE = maybe (Left "No value provided") Right textM
@@ -413,7 +566,7 @@
    Left errs -> do
     -- XXX should save current form values in session
     setMessage $ string errs
-    redirect RedirectTemporary JournalPage
+    redirect RedirectTemporary JournalR
 
    Right t' -> do
     -- try to avoid unnecessary backups or saving invalid data
@@ -427,16 +580,311 @@
     if not changed
      then do
        setMessage $ string $ "No change"
-       redirect RedirectTemporary EditPage
+       redirect RedirectTemporary JournalR
      else do
       jE <- liftIO $ journalFromPathAndString Nothing f tnew
       either
        (\e -> do
           setMessage $ string e
-          redirect RedirectTemporary EditPage)
+          redirect RedirectTemporary JournalR)
        (const $ do
           liftIO $ writeFileWithBackup f tnew
           setMessage $ string $ printf "Saved journal %s\n" (show f)
-          redirect RedirectTemporary JournalPage)
+          redirect RedirectTemporary JournalR)
        jE
+
+-- | Handle an import page post.
+postImportForm :: Handler RepPlain
+postImportForm = do
+  setMessage $ string $ "can't handle file upload yet"
+  redirect RedirectTemporary JournalR
+  -- -- get form input values, or basic validation errors. E means an Either value.
+  -- fileM <- runFormPost' $ maybeFileInput "file"
+  -- let fileE = maybe (Left "No file provided") Right fileM
+  -- -- display errors or import transactions
+  -- case fileE of
+  --  Left errs -> do
+  --   setMessage $ string errs
+  --   redirect RedirectTemporary JournalR
+
+  --  Right s -> do
+  --    setMessage $ string $ s
+  --    redirect RedirectTemporary JournalR
+
+----------------------------------------------------------------------
+
+-- | A simple postings view like hledger register.
+getRegisterOnlyR :: Handler RepHtml
+getRegisterOnlyR = do
+  (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
+  today <- liftIO getCurrentDay
+  let td = mktd{here=here, title="hledger register", msg=msg, a=a, p=p, j=j, today=today}
+  hamletToRepHtml $ pageLayout td $ registerReportAsHtml opts td $ registerReport opts fspec j
+
+-- | Render a register report as HTML.
+registerReportAsHtml :: [Opt] -> TemplateData -> RegisterReport -> Hamlet HledgerWebAppRoute
+registerReportAsHtml _ td items = [$hamlet|
+%table.registerreport
+ %tr.headings
+  ^headings^
+ $forall number.items i
+  ^itemAsHtml' i^
+|]
+ where
+   number = zip [1..]
+   headings = [$hamlet|
+               %th.date!align=left Date
+               %th.description!align=left Description
+               %th.account!align=left Account
+               %th.amount!align=right Amount
+               %th.balance!align=right Balance
+               |]
+   itemAsHtml' = itemAsHtml td
+   itemAsHtml :: TemplateData -> (Int, RegisterReportItem) -> Hamlet HledgerWebAppRoute
+   itemAsHtml TD{p=p} (n, (ds, posting, b)) = [$hamlet|
+     %tr.item.$evenodd$.$firstposting$
+      %td.date $date$
+      %td.description $desc$
+      %td.account
+       %a!href=$aurl$ $acct$
+      %td.amount!align=right $mixedAmountAsHtml.pamount.posting$
+      %td.balance!align=right $mixedAmountAsHtml.b$
+     |] where
+       evenodd = if even n then "even" else "odd"
+       (firstposting, date, desc) = case ds of Just (da, de) -> ("firstposting", show da, de)
+                                               Nothing -> ("", "", "")
+       acct = paccount posting
+       aurl = printf ".?a=%s%s" (accountNameToAccountRegex acct) p' :: String
+       p' = if null p then "" else printf "&p=%s" p
+
+mixedAmountAsHtml b = preEscapedString $ addclass $ intercalate "<br>" $ lines $ show b
+    where addclass = printf "<span class=\"%s\">%s</span>" c
+          c = case isNegativeMixedAmount b of Just True -> "negative amount"
+                                              _         -> "positive amount"
+
+----------------------------------------------------------------------
+
+-- | A standalone journal edit form page.
+getEditR :: Handler RepHtml
+getEditR = do
+  (a, p, _, _, _, msg, here) <- getHandlerParameters
+  today <- liftIO getCurrentDay
+  -- reload journal's text without parsing, if changed     -- XXX are we doing this right ?
+  j <- liftIO $ fromJust `fmap` getValue "hledger" "journal"
+  changed <- liftIO $ journalFileIsNewer j
+  s <- liftIO $ if changed then readFile (filepath j) else return (jtext j) -- XXX readFile may throw an error
+  let td = mktd{here=here, title="hledger journal edit", msg=msg, a=a, p=p, j=j, today=today}
+  hamletToRepHtml $ pageLayout td $ editform td s
+
+----------------------------------------------------------------------
+
+-- | Get the add form from template files reloaded at run-time.
+getAddformRTR :: Handler RepHtml
+getAddformRTR = do
+  (a, p, _, _, j, msg, here) <- getHandlerParameters
+  today <- liftIO getCurrentDay
+  let td = mktd{here=here, title="hledger add transaction", msg=msg, a=a, p=p, j=j, today=today}
+      descriptions = sort $ nub $ map tdescription $ jtxns j
+      acctnames = sort $ journalAccountNamesUsed j
+      postingData n = [
+                       (["acctlabel"], hdstring acctlabel)
+                      ,(["acctvar"],   hdstring acctvar)
+                      ,(["acctnames"], hdstringlist acctnames)
+                      ,(["amtfield"],  HDHtml $ renderHamlet' amtfield)
+                      ,(["accthelp"],  hdstring accthelp)
+                      ,(["amthelp"],   hdstring amthelp)
+                      ] :: HamletMap HledgerWebAppRoute
+          where
+            numbered = (++ show n)
+            acctvar = numbered "account"
+            amtvar = numbered "amount"
+            (acctlabel, accthelp, amtfield, amthelp)
+                | n == 1     = ("To account"
+                              ,"eg: expenses:food"
+                              ,[$hamlet|
+                                %td!style=padding-left:1em;
+                                 Amount:
+                                %td
+                                 %input.textinput!size=15!name=$amtvar$!value=""
+                                |]
+                              ,"eg: $6"
+                              )
+                | otherwise = ("From account"
+                              ,"eg: assets:bank:checking"
+                              ,nulltemplate
+                              ,""
+                              )
+  pfields1 <- renderHamletFile "addformpostingfields.hamlet" (postingData 1)
+  pfields2 <- renderHamletFile "addformpostingfields.hamlet" (postingData 2)
+  addform  <- renderHamletFile "addform.hamlet" ([
+                                                 (["date"], hdstring "today")
+                                                ,(["desc"], hdstring "")
+                                                ,(["descriptions"], hdstringlist descriptions)
+                                                ,(["datehelp"], hdstring "eg: 2010/7/20")
+                                                ,(["deschelp"], hdstring "eg: supermarket (optional)")
+                                                ,(["postingfields1"], HDHtml pfields1)
+                                                ,(["postingfields2"], HDHtml pfields2)
+                                                ] :: HamletMap HledgerWebAppRoute)
+  hamletToRepHtml $ pageLayout td $ htmlAsHamlet addform
+
+-- | Convert a string to a hamlet HDHtml data item.
+hdstring :: String -> HamletData HledgerWebAppRoute
+hdstring = HDHtml . string
+
+-- | Convert a simple list of strings to hamlet's complicated HDList type.
+hdstringlist :: [String] -> HamletData HledgerWebAppRoute
+hdstringlist ss = HDList [ [([], hdstring s)] | s <- ss ]
+
+instance Failure HamletException Handler
+    where failure = error' . show
+
+renderHamletFile :: FilePath -> HamletMap HledgerWebAppRoute -> Handler Html
+renderHamletFile hfile hmap = do
+  hrt <- readHamletFile hfile >>= parseHamletRT defaultHamletSettings
+  renderHamletRT hrt hmap renderurlwithparams
+
+renderurlwithparams u [] = show u
+renderurlwithparams u ps = show u ++ "?" ++ intercalate "&" [k++"="++v | (k,v) <- ps]
+
+readHamletFile :: FilePath -> Handler String
+readHamletFile hfile = do
+  dir <- ((</> "templates") . appDir) `fmap` getYesod
+  liftIO $ readFile $ dir </> hfile
+
+htmlAsHamlet :: Html -> Hamlet HledgerWebAppRoute
+htmlAsHamlet h = [$hamlet|$h$|]
+
+parseHamletRT' :: Failure HamletException m => String -> m HamletRT
+parseHamletRT' s = parseHamletRT defaultHamletSettings s
+
+renderHamletRT' :: Failure HamletException m => HamletMap HledgerWebAppRoute -> HamletRT -> m Html
+renderHamletRT' m h = renderHamletRT h m renderurlwithparams
+
+renderHamlet' :: Hamlet HledgerWebAppRoute -> Html
+renderHamlet' h = h renderurlwithparams
+
+-- hamletToHamletRT ::  Failure HamletException m => Hamlet HledgerWebAppRoute -> m HamletRT
+-- hamletToHamletRT h = stringToHamletRT $ show $ unsafeByteString $ renderHamlet show h
+
+----------------------------------------------------------------------
+
+-- | Wrap a template with the standard hledger web ui page layout.
+pageLayout :: TemplateData -> Hamlet HledgerWebAppRoute -> Hamlet HledgerWebAppRoute
+pageLayout td@TD{title=basetitle, msg=msg, p=p, j=j, today=today} content = [$hamlet|
+!!!
+%html
+ %head
+  %title $title'$
+  %meta!http-equiv=Content-Type!content=$metacontent$
+  %script!type=text/javascript!src=@StaticR.jquery_js@
+  %script!type=text/javascript!src=@StaticR.jquery_url_js@
+  %script!type=text/javascript!src=@StaticR.dhtmlxcommon_js@
+  %script!type=text/javascript!src=@StaticR.dhtmlxcombo_js@
+  %script!type=text/javascript!src=@StaticR.hledger_js@
+  %link!rel=stylesheet!type=text/css!media=all!href=@StaticR.style_css@
+ %body
+  ^navbar.td^
+  #messages $m$
+  #content
+   ^content^
+|]
+ where m = fromMaybe (string "") msg
+       metacontent = "text/html; charset=utf-8"
+       (journaltitle, _) = journalTitleInfo j p today
+       title' = basetitle ++ " - " ++ journaltitle
+
+navbar :: TemplateData -> Hamlet HledgerWebAppRoute
+navbar TD{p=p,j=j,today=today} = [$hamlet|
+ #navbar
+  %a.topleftlink!href=$hledgerorgurl$
+   hledger
+   <br />
+   $version$
+  %a.toprightlink!href=$manualurl$!target=hledgerhelp manual
+  %h1 $journaltitle$
+  \ $
+  %span#journalinfo $journalinfo$
+|]
+  where (journaltitle, journalinfo) = journalTitleInfo j p today
+
+-- | Generate journal- and context-specific title and info strings for display.
+journalTitleInfo :: Journal -> String -> Day -> (String, String)
+journalTitleInfo j p today = (journaltitle, journalinfo)
+  where
+    journaltitle = printf "%s" (takeFileName $ filepath j) :: String
+    journalinfo  = printf "%s" (showspan span) :: String
+    span = either (const $ DateSpan Nothing Nothing) snd (parsePeriodExpr today p)
+    showspan (DateSpan Nothing Nothing) = ""
+    showspan s = " (" ++ dateSpanAsText s ++ ")"
+
+navlinks :: TemplateData -> Hamlet HledgerWebAppRoute
+navlinks td = [$hamlet|
+ #navlinks
+  ^accountsjournallink^
+  \ | $
+  ^accountsregisterlink^
+  \ | $
+  %a#addformlink!href!onclick="return addformToggle()" add transaction
+  %a#importformlink!href!onclick="return importformToggle()"!style=display:none; import transactions
+  \ | $
+  %a#editformlink!href!onclick="return editformToggle()" edit journal
+|]
+--  \ | $
+ where
+   accountsjournallink  = navlink td "journal" JournalR
+   accountsregisterlink = navlink td "register" RegisterR
+
+navlink :: TemplateData -> String -> HledgerWebAppRoute -> Hamlet HledgerWebAppRoute
+navlink TD{here=here,a=a,p=p} s dest = [$hamlet|%a#$s$link.$style$!href=@?u@ $s$|]
+ where u = (dest, concat [(if null a then [] else [("z", a)])
+                         ,(if null p then [] else [("p", p)])])
+       style | dest == here = "navlinkcurrent"
+             | otherwise    = "navlink"
+
+filterform :: TemplateData -> Hamlet HledgerWebAppRoute
+filterform TD{here=here,a=a,p=p} = [$hamlet|
+ #filterformdiv
+  %form#filterform.form!method=GET!style=display:$visible$;
+   %table.form
+    %tr.$filteringperiodclass$
+     %td
+      filter by period:
+      \ $
+     %td
+      %input!name=p!size=60!value=$p$
+      ^phelp^
+      \ $
+     %td!align=right
+      ^stopfilteringperiod^
+    %tr.$filteringclass$
+     %td
+      filter by account/description:
+      \ $
+     %td
+      %input!name=a!size=60!value=$a$
+      ^ahelp^
+      \ $
+      %input!type=submit!value=filter $
+      \ $
+     %td!align=right
+      ^stopfiltering^
+|]
+ where
+  ahelp = helplink "filter-patterns" "?"
+  phelp = helplink "period-expressions" "?"
+  filtering = not $ null a
+  filteringperiod = not $ null p
+  visible = "block"
+  filteringclass = if filtering then "filtering" else ""
+  filteringperiodclass = if filteringperiod then "filtering" else ""
+  stopfiltering = if filtering then [$hamlet|%a#stopfilterlink!href=@?u@ stop filtering acct/desc|] else nulltemplate
+      where u = (here, if filteringperiod then [("p", p)] else [])
+  stopfilteringperiod = if filteringperiod then [$hamlet|%a#stopfilterlink!href=@?u@ stop filtering period|] else nulltemplate
+      where u = (here, if filtering then [("q", a)] else [])
+
+helplink :: String -> String -> Hamlet HledgerWebAppRoute
+helplink topic label = [$hamlet|%a!href=$u$!target=hledgerhelp $label$|]
+    where u = manualurl ++ if null topic then "" else '#':topic
+
+nulltemplate = [$hamlet||]
 
diff --git a/Hledger/Cli/Commands/Web610.hs b/Hledger/Cli/Commands/Web610.hs
deleted file mode 100644
--- a/Hledger/Cli/Commands/Web610.hs
+++ /dev/null
@@ -1,316 +0,0 @@
-{-# LANGUAGE CPP, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses #-}
-{-# OPTIONS_GHC -F -pgmFtrhsx #-}
-{-| 
-A web-based UI.
--}
-
-module Hledger.Cli.Commands.Web610
-where
-import Codec.Binary.UTF8.String (decodeString)
-import Control.Applicative.Error (Failing(Success,Failure))
-import Control.Concurrent
-import Control.Monad.Reader (ask)
-import Data.IORef (newIORef, atomicModifyIORef)
-import System.IO.Storage (withStore, putValue, getValue)
-import Text.ParserCombinators.Parsec (parse)
-
-import Hack.Contrib.Constants (_TextHtmlUTF8)
-import Hack.Contrib.Response (set_content_type)
-import qualified Hack (Env, http)
-import qualified Hack.Contrib.Request (inputs, params, path)
-import qualified Hack.Contrib.Response (redirect)
-import Hack.Handler.SimpleServer (run)
-
-import Network.Loli (loli, io, get, post, html, text, public)
-import Network.Loli.Type (AppUnit)
-import Network.Loli.Utils (update)
-
-import HSP hiding (Request,catch)
-import qualified HSP (Request(..))
-
-import Hledger.Cli.Commands.Add (journalAddTransaction)
-import Hledger.Cli.Commands.Balance
-import Hledger.Cli.Commands.Histogram
-import Hledger.Cli.Commands.Print
-import Hledger.Cli.Commands.Register
-import Hledger.Data
-import Hledger.Read.Journal (someamount)
-import Hledger.Cli.Options hiding (value)
-#ifdef MAKE
-import Paths_hledger_make (getDataFileName)
-#else
-import Paths_hledger (getDataFileName)
-#endif
-import Hledger.Cli.Utils
-
-
-tcpport = 5000 :: Int
-homeurl = printf "http://localhost:%d/" tcpport
-browserdelay = 100000 -- microseconds
-
-web :: [Opt] -> [String] -> Journal -> IO ()
-web opts args j = do
-  unless (Debug `elem` opts) $ forkIO browser >> return ()
-  server opts args j
-
-browser :: IO ()
-browser = putStrLn "starting web browser" >> threadDelay browserdelay >> openBrowserOn homeurl >> return ()
-
-server :: [Opt] -> [String] -> Journal -> IO ()
-server opts args j =
-  -- server initialisation
-  withStore "hledger" $ do -- IO ()
-    printf "starting web server on port %d\n" tcpport
-    t <- getCurrentLocalTime
-    webfiles <- getDataFileName "web"
-    putValue "hledger" "journal" j
-    run tcpport $            -- (Env -> IO Response) -> IO ()
-      \env -> do -- IO Response
-       -- general request handler
-       let opts' = opts ++ [Period $ unwords $ map decodeString $ reqParamUtf8 env "p"]
-           args' = args ++ map decodeString (reqParamUtf8 env "a")
-       j' <- fromJust `fmap` getValue "hledger" "journal"
-       (jE, changed) <- io $ journalReloadIfChanged opts j'
-       let (j''', err) = either (\e -> (j',e)) (\j'' -> (j'',"")) jE
-       when (changed && null err) $ putValue "hledger" "journal" j'''
-       when (changed && not (null err)) $ printf "error while reading %s\n" (filepath j')
-       -- declare path-specific request handlers
-       let command :: [String] -> ([Opt] -> FilterSpec -> Journal -> String) -> AppUnit
-           command msgs f = string msgs $ f opts' (optsToFilterSpec opts' args' t) j'''
-       (loli $                                               -- State Loli () -> (Env -> IO Response)
-         do
-          get  "/balance"   $ command [] showBalanceReport  -- String -> ReaderT Env (StateT Response IO) () -> State Loli ()
-          get  "/register"  $ command [] showRegisterReport
-          get  "/histogram" $ command [] showHistogram
-          get  "/transactions"   $ journalpage [] j''' (showTransactions (optsToFilterSpec opts' args' t))
-          post "/transactions"   $ handleAddform j'''
-          get  "/env"       $ getenv >>= (text . show)
-          get  "/params"    $ getenv >>= (text . show . Hack.Contrib.Request.params)
-          get  "/inputs"    $ getenv >>= (text . show . Hack.Contrib.Request.inputs)
-          public (Just webfiles) ["/style.css"]
-          get  "/"          $ redirect ("transactions") Nothing
-          ) env
-
-getenv = ask
-response = update
-redirect u c = response $ Hack.Contrib.Response.redirect u c
-
-reqParamUtf8 :: Hack.Env -> String -> [String]
-reqParamUtf8 env p = map snd $ filter ((==p).fst) $ Hack.Contrib.Request.params env
-
-journalpage :: [String] -> Journal -> (Journal -> String) -> AppUnit
-journalpage msgs j f = do
-  env <- getenv
-  (jE, _) <- io $ journalReloadIfChanged [] j
-  let (j'', _) = either (\e -> (j,e)) (\j' -> (j',"")) jE
-  hsp msgs $ const <div><% addform env %><pre><% f j'' %></pre></div>
-
--- | A loli directive to serve a string in pre tags within the hledger web
--- layout.
-string :: [String] -> String -> AppUnit
-string msgs s = hsp msgs $ const <pre><% s %></pre>
-
--- | A loli directive to serve a hsp template wrapped in the hledger web
--- layout. The hack environment is passed in to every hsp template as an
--- argument, since I don't see how to get it within the hsp monad.
--- A list of messages is also passed, eg for form errors.
-hsp :: [String] -> (Hack.Env -> HSP XML) -> AppUnit
-hsp msgs f = do
-  env <- getenv
-  let contenthsp = f env
-      pagehsp = hledgerpage env msgs title contenthsp
-  html =<< (io $ do
-              hspenv <- hackEnvToHspEnv env
-              (_,xml) <- runHSP html4Strict pagehsp hspenv
-              return $ addDoctype $ renderAsHTML xml)
-  response $ set_content_type _TextHtmlUTF8
-    where
-      title = ""
-      addDoctype = ("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n" ++)
-      hackEnvToHspEnv :: Hack.Env -> IO HSPEnv
-      hackEnvToHspEnv env = do
-          x <- newIORef 0
-          let req = HSP.Request (reqParamUtf8 env) (Hack.http env)
-              num = NumberGen (atomicModifyIORef x (\a -> (a+1,a)))
-          return $ HSPEnv req num
-
--- htmlToHsp :: Html -> HSP XML
--- htmlToHsp h = return $ cdata $ showHtml h
-
--- views
-
-hledgerpage :: Hack.Env -> [String] -> String -> HSP XML -> HSP XML
-hledgerpage env msgs title content =
-    <html>
-      <head>
-        <meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" />
-        <link rel="stylesheet" type="text/css" href="/style.css" media="all" />
-        <title><% title %></title>
-      </head>
-      <body>
-        <% navbar env %>
-        <div id="messages"><% intercalate ", " msgs %></div>
-        <div id="content"><% content %></div>
-      </body>
-    </html>
-
-navbar :: Hack.Env -> HSP XML
-navbar env =
-    <div id="navbar">
-      <a href="http://hledger.org" id="hledgerorglink">hledger.org</a>
-      <% navlinks env %>
-      <% searchform env %>
-      <a href="http://hledger.org/MANUAL.html" id="helplink">help</a>
-    </div>
-
-getParamOrNull p = (decodeString . fromMaybe "") `fmap` getParam p
-
-navlinks :: Hack.Env -> HSP XML
-navlinks _ = do
-   a <- getParamOrNull "a"
-   p <- getParamOrNull "p"
-   let addparams=(++(printf "?a=%s&p=%s" a p))
-       link s = <a href=(addparams s) class="navlink"><% s %></a>
-   <div id="navlinks">
-     <% link "transactions" %> |
-     <% link "register" %> |
-     <% link "balance" %>
-    </div>
-
-searchform :: Hack.Env -> HSP XML
-searchform env = do
-   a <- getParamOrNull "a"
-   p <- getParamOrNull "p"
-   let resetlink | null a && null p = <span></span>
-                 | otherwise = <span id="resetlink"><% nbsp %><a href=u>reset</a></span>
-                 where u = dropWhile (=='/') $ Hack.Contrib.Request.path env
-   <form action="" id="searchform">
-      <% nbsp %>search for:<% nbsp %><input name="a" size="20" value=a
-      /><% help "filter-patterns"
-      %><% nbsp %><% nbsp %>in reporting period:<% nbsp %><input name="p" size="20" value=p
-      /><% help "period-expressions"
-      %><input type="submit" name="submit" value="filter" style="display:none" />
-      <% resetlink %>
-    </form>
-
-addform :: Hack.Env -> HSP XML
-addform env = do
-  today <- io $ liftM showDate $ getCurrentDay
-  let inputs = Hack.Contrib.Request.inputs env
-      date  = decodeString $ fromMaybe today $ lookup "date"  inputs
-      desc  = decodeString $ fromMaybe "" $ lookup "desc"  inputs
-  <div>
-   <div id="addform">
-   <form action="" method="POST">
-    <table border="0">
-      <tr>
-        <td>
-          Date: <input size="15" name="date" value=date /><% help "dates" %><% nbsp %>
-          Description: <input size="35" name="desc" value=desc /><% nbsp %>
-        </td>
-      </tr>
-      <% transactionfields 1 env %>
-      <% transactionfields 2 env %>
-      <tr id="addbuttonrow"><td><input type="submit" value="add transaction" 
-      /><% help "file-format" %></td></tr>
-    </table>
-   </form>
-   </div>
-   <br clear="all" />
-   </div>
-
-help :: String -> HSP XML
-help topic = <a href=u>?</a>
-    where u = printf "http://hledger.org/MANUAL.html%s" l :: String
-          l | null topic = ""
-            | otherwise = '#':topic
-
-transactionfields :: Int -> Hack.Env -> HSP XML
-transactionfields n env = do
-  let inputs = Hack.Contrib.Request.inputs env
-      acct = decodeString $ fromMaybe "" $ lookup acctvar inputs
-      amt  = decodeString $ fromMaybe "" $ lookup amtvar  inputs
-  <tr>
-    <td>
-    <% nbsp %><% nbsp %>
-      Account: <input size="35" name=acctvar value=acct /><% nbsp %>
-      Amount: <input size="15" name=amtvar value=amt /><% nbsp %>
-    </td>
-   </tr>
-    where
-      numbered = (++ show n)
-      acctvar = numbered "acct"
-      amtvar = numbered "amt"
-
-handleAddform :: Journal -> AppUnit
-handleAddform j = do
-  env <- getenv
-  d <- io getCurrentDay
-  t <- io getCurrentLocalTime
-  handle t $ validate env d
-  where
-    validate :: Hack.Env -> Day -> Failing Transaction
-    validate env today =
-        let inputs = Hack.Contrib.Request.inputs env
-            date  = decodeString $ fromMaybe "today" $ lookup "date"  inputs
-            desc  = decodeString $ fromMaybe "" $ lookup "desc"  inputs
-            acct1 = decodeString $ fromMaybe "" $ lookup "acct1" inputs
-            amt1  = decodeString $ fromMaybe "" $ lookup "amt1"  inputs
-            acct2 = decodeString $ fromMaybe "" $ lookup "acct2" inputs
-            amt2  = decodeString $ fromMaybe "" $ lookup "amt2"  inputs
-            validateDate ""  = ["missing date"]
-            validateDate _   = []
-            validateDesc ""  = ["missing description"]
-            validateDesc _   = []
-            validateAcct1 "" = ["missing account 1"]
-            validateAcct1 _  = []
-            validateAmt1 ""  = ["missing amount 1"]
-            validateAmt1 _   = []
-            validateAcct2 "" = ["missing account 2"]
-            validateAcct2 _  = []
-            validateAmt2 _   = []
-            amt1' = either (const missingamt) id $ parse someamount "" amt1
-            amt2' = either (const missingamt) id $ parse someamount "" amt2
-            (date', dateparseerr) = case fixSmartDateStrEither today date of
-                                      Right d -> (d, [])
-                                      Left e -> ("1900/01/01", [showDateParseError e])
-            t = Transaction {
-                            tdate = parsedate date' -- date' must be parseable
-                           ,teffectivedate=Nothing
-                           ,tstatus=False
-                           ,tcode=""
-                           ,tdescription=desc
-                           ,tcomment=""
-                           ,tpostings=[
-                             Posting False acct1 amt1' "" RegularPosting (Just t')
-                            ,Posting False acct2 amt2' "" RegularPosting (Just t')
-                            ]
-                           ,tpreceding_comment_lines=""
-                           }
-            (t', balanceerr) = case balanceTransaction t of
-                           Right t'' -> (t'', [])
-                           Left e -> (t, [head $ lines e]) -- show just the error not the transaction
-            errs = concat [
-                    validateDate date
-                   ,dateparseerr
-                   ,validateDesc desc
-                   ,validateAcct1 acct1
-                   ,validateAmt1 amt1
-                   ,validateAcct2 acct2
-                   ,validateAmt2 amt2
-                   ,balanceerr
-                   ]
-        in
-        case null errs of
-          False -> Failure errs
-          True  -> Success t'
-
-    handle :: LocalTime -> Failing Transaction -> AppUnit
-    handle _ (Failure errs) = hsp errs addform
-    handle ti (Success t)   = do
-                    io $ journalAddTransaction j t >>= journalReload
-                    journalpage [msg] j (showTransactions (optsToFilterSpec [] [] ti))
-       where msg = printf "Added transaction:\n%s" (show t)
-
-nbsp :: XML
-nbsp = cdata "&nbsp;"
diff --git a/Hledger/Cli/Main.hs b/Hledger/Cli/Main.hs
--- a/Hledger/Cli/Main.hs
+++ b/Hledger/Cli/Main.hs
@@ -39,7 +39,7 @@
 -}
 
 module Hledger.Cli.Main where
-#if defined(WEB) || defined(WEB610)
+#if defined(WEB)
 import System.Info (os)
 #endif
 #if __GLASGOW_HASKELL__ <= 610
@@ -76,7 +76,7 @@
 #ifdef VTY
        | cmd `isPrefixOf` "vty"       = withJournalDo opts args cmd vty
 #endif
-#if defined(WEB) || defined(WEB610)
+#if defined(WEB)
        | cmd `isPrefixOf` "web"       = withJournalDo opts args cmd web
 #endif
 #ifdef CHART
@@ -86,7 +86,7 @@
        | otherwise                    = putStr help1
 
 -- in a web-enabled build on windows, run the web ui by default
-#if defined(WEB) || defined(WEB610)
+#if defined(WEB)
       defaultcmd | os=="mingw32" = Just web
                  | otherwise = Nothing
 #else
diff --git a/Hledger/Cli/Options.hs b/Hledger/Cli/Options.hs
--- a/Hledger/Cli/Options.hs
+++ b/Hledger/Cli/Options.hs
@@ -42,10 +42,10 @@
   " (DISABLED, install with -fvty)\n" ++
 #endif
   "  web       - run a simple web-based UI" ++
-#if defined(WEB) || defined(WEB610)
+#if defined(WEB)
   "\n" ++
 #else
-  " (DISABLED, install with -fweb or -fweb610)\n" ++
+  " (DISABLED, install with -fweb)\n" ++
 #endif
   "  chart     - generate balances pie charts" ++
 #ifdef CHART
@@ -87,6 +87,7 @@
  ,Option ""  ["flat"]         (NoArg  Flat)          "balance: show full account names, unindented"
  ,Option ""  ["drop"]         (ReqArg Drop "N")      "balance: with --flat, elide first N account name components"
  ,Option ""  ["no-total"]     (NoArg  NoTotal)       "balance: hide the final total"
+ ,Option "D" ["daily"]        (NoArg  DailyOpt)      "register, stats: report by day"
  ,Option "W" ["weekly"]       (NoArg  WeeklyOpt)     "register, stats: report by week"
  ,Option "M" ["monthly"]      (NoArg  MonthlyOpt)    "register, stats: report by month"
  ,Option "Q" ["quarterly"]    (NoArg  QuarterlyOpt)  "register, stats: report by quarter"
@@ -131,6 +132,7 @@
     Drop   {value::String} |
     NoTotal |
     SubTotal |
+    DailyOpt |
     WeeklyOpt |
     MonthlyOpt |
     QuarterlyOpt |
@@ -177,7 +179,7 @@
   case (as,es) of
     (cmd:args,[])   -> return (os'',cmd,args)
     ([],[])         -> return (os'',"",[])
-    (_,errs)        -> ioError (userError (concat errs ++ help1))
+    (_,errs)        -> ioError (userError' (concat errs ++ help1))
 
 -- | Convert any fuzzy dates within these option values to explicit ones,
 -- based on today's date.
@@ -198,7 +200,9 @@
 -- others are ignored.
 dateSpanFromOpts :: Day -> [Opt] -> DateSpan
 dateSpanFromOpts refdate opts
-    | not $ null popts = snd $ parsePeriodExpr refdate $ last popts
+    | not (null popts) = case parsePeriodExpr refdate $ last popts of
+                         Right (_, s) -> s
+                         Left e       -> parseerror e
     | otherwise = DateSpan lastb laste
     where
       popts = optValuesForConstructor Period opts
@@ -214,7 +218,10 @@
 intervalFromOpts :: [Opt] -> Interval
 intervalFromOpts opts =
     case (periodopts, intervalopts) of
-      ((p:_), _)            -> fst $ parsePeriodExpr d p where d = parsedate "0001/01/01" -- unused
+      ((p:_), _)            -> case parsePeriodExpr (parsedate "0001/01/01") p of
+                                Right (i, _) -> i
+                                Left e       -> parseerror e
+      (_, (DailyOpt:_))     -> Daily
       (_, (WeeklyOpt:_))    -> Weekly
       (_, (MonthlyOpt:_))   -> Monthly
       (_, (QuarterlyOpt:_)) -> Quarterly
@@ -222,7 +229,7 @@
       (_, _)                -> NoInterval
     where
       periodopts   = reverse $ optValuesForConstructor Period opts
-      intervalopts = reverse $ filter (`elem` [WeeklyOpt,MonthlyOpt,QuarterlyOpt,YearlyOpt]) opts
+      intervalopts = reverse $ filter (`elem` [DailyOpt,WeeklyOpt,MonthlyOpt,QuarterlyOpt,YearlyOpt]) opts
 
 -- | Get the value of the (last) depth option, if any.
 depthFromOpts :: [Opt] -> Maybe Int
diff --git a/Hledger/Cli/Tests.hs b/Hledger/Cli/Tests.hs
--- a/Hledger/Cli/Tests.hs
+++ b/Hledger/Cli/Tests.hs
@@ -12,10 +12,10 @@
 
 hledger's doctests are shell tests defined in literal blocks in haddock
 documentation in the source, run by doing @make doctest@ in the hledger
-source tree. They are no longer used, but here is an example:
+source tree. They hardly used, but here is an example:
 
 @
-$ hledger -f sample.journal balance o
+$ hledger -f data/sample.journal balance o
                   $1  expenses:food
                  $-2  income
                  $-1    gifts
@@ -61,8 +61,8 @@
    tests_Hledger_Commands,
 
    "account directive" ~:
-   let sameParse str1 str2 = do j1 <- readJournal Nothing str1 >>= either error return
-                                j2 <- readJournal Nothing str2 >>= either error return
+   let sameParse str1 str2 = do j1 <- readJournal Nothing str1 >>= either error' return
+                                j2 <- readJournal Nothing str2 >>= either error' return
                                 j1 `is` j2{filereadtime=filereadtime j1, jtext=jtext j1}
    in TestList
    [
@@ -109,7 +109,7 @@
    let (opts,args) `gives` es = do 
         l <- samplejournalwithopts opts args
         t <- getCurrentLocalTime
-        showBalanceReport opts (optsToFilterSpec opts args t) l `is` unlines es
+        balanceReportAsText opts (balanceReport opts (optsToFilterSpec opts args t) l) `is` unlines es
    in TestList
    [
 
@@ -243,9 +243,9 @@
              ,"2008/1/1 test           "
              ,"  a:b          10h @ $50"
              ,"  c:d                   "
-             ]) >>= either error return
+             ]) >>= either error' return
       let j' = journalCanonicaliseAmounts $ journalConvertAmountsToCost j -- enable cost basis adjustment
-      showBalanceReport [] nullfilterspec j' `is`
+      balanceReportAsText [] (balanceReport [] nullfilterspec j') `is`
        unlines
         ["                $500  a:b"
         ,"               $-500  c:d"
@@ -260,7 +260,7 @@
               ,"  test:a  1"
               ,"  test:b"
               ])
-      showBalanceReport [] nullfilterspec l `is`
+      balanceReportAsText [] (balanceReport [] nullfilterspec l) `is`
        unlines
         ["                   1  test:a"
         ,"                  -1  test:b"
@@ -292,7 +292,7 @@
                      (Mixed [dollars (-1)])
                      (case e of
                         Right e' -> (pamount $ last $ tpostings e')
-                        Left _ -> error "should not happen")
+                        Left _ -> error' "should not happen")
 
   ,"journalCanonicaliseAmounts" ~:
    "use the greatest precision" ~:
@@ -323,6 +323,7 @@
   ,"intervalFromOpts" ~: do
     let gives = is . intervalFromOpts
     [] `gives` NoInterval
+    [DailyOpt] `gives` Daily
     [WeeklyOpt] `gives` Weekly
     [MonthlyOpt] `gives` Monthly
     [QuarterlyOpt] `gives` Quarterly
@@ -384,7 +385,7 @@
     "assets:bank" `isSubAccountNameOf` "my assets" `is` False
 
   ,"default year" ~: do
-    rl <- readJournal Nothing defaultyear_journal_str >>= either error return
+    rl <- readJournal Nothing defaultyear_journal_str >>= either error' return
     tdate (head $ jtxns rl) `is` fromGregorian 2009 1 1
     return ()
 
@@ -458,7 +459,7 @@
    "register report with no args" ~:
    do 
     l <- samplejournal
-    showRegisterReport [] (optsToFilterSpec [] [] t1) l `is` unlines
+    (registerReportAsText [] $ registerReport [] (optsToFilterSpec [] [] t1) l) `is` unlines
      ["2008/01/01 income               assets:bank:checking             $1           $1"
      ,"                                income:salary                   $-1            0"
      ,"2008/06/01 gift                 assets:bank:checking             $1           $1"
@@ -476,7 +477,7 @@
    do 
     let opts = [Cleared]
     l <- readJournalWithOpts opts sample_journal_str
-    showRegisterReport opts (optsToFilterSpec opts [] t1) l `is` unlines
+    (registerReportAsText opts $ registerReport opts (optsToFilterSpec opts [] t1) l) `is` unlines
      ["2008/06/03 eat & shop           expenses:food                    $1           $1"
      ,"                                expenses:supplies                $1           $2"
      ,"                                assets:cash                     $-2            0"
@@ -488,7 +489,7 @@
    do 
     let opts = [UnCleared]
     l <- readJournalWithOpts opts sample_journal_str
-    showRegisterReport opts (optsToFilterSpec opts [] t1) l `is` unlines
+    (registerReportAsText opts $ registerReport opts (optsToFilterSpec opts [] t1) l) `is` unlines
      ["2008/01/01 income               assets:bank:checking             $1           $1"
      ,"                                income:salary                   $-1            0"
      ,"2008/06/01 gift                 assets:bank:checking             $1           $1"
@@ -508,19 +509,19 @@
         ,"  e  1"
         ,"  f"
         ]
-    registerdates (showRegisterReport [] (optsToFilterSpec [] [] t1) l) `is` ["2008/01/01","2008/02/02"]
+    registerdates (registerReportAsText [] $ registerReport [] (optsToFilterSpec [] [] t1) l) `is` ["2008/01/01","2008/02/02"]
 
   ,"register report with account pattern" ~:
    do
     l <- samplejournal
-    showRegisterReport [] (optsToFilterSpec [] ["cash"] t1) l `is` unlines
+    (registerReportAsText [] $ registerReport [] (optsToFilterSpec [] ["cash"] t1) l) `is` unlines
      ["2008/06/03 eat & shop           assets:cash                     $-2          $-2"
      ]
 
   ,"register report with account pattern, case insensitive" ~:
    do 
     l <- samplejournal
-    showRegisterReport [] (optsToFilterSpec [] ["cAsH"] t1) l `is` unlines
+    (registerReportAsText [] $ registerReport [] (optsToFilterSpec [] ["cAsH"] t1) l) `is` unlines
      ["2008/06/03 eat & shop           assets:cash                     $-2          $-2"
      ]
 
@@ -528,7 +529,7 @@
    do 
     l <- samplejournal
     let gives displayexpr = 
-            (registerdates (showRegisterReport opts (optsToFilterSpec opts [] t1) l) `is`)
+            (registerdates (registerReportAsText opts $ registerReport opts (optsToFilterSpec opts [] t1) l) `is`)
                 where opts = [Display displayexpr]
     "d<[2008/6/2]"  `gives` ["2008/01/01","2008/06/01"]
     "d<=[2008/6/2]" `gives` ["2008/01/01","2008/06/01","2008/06/02"]
@@ -541,7 +542,7 @@
     l <- samplejournal
     let periodexpr `gives` dates = do
           l' <- samplejournalwithopts opts []
-          registerdates (showRegisterReport opts (optsToFilterSpec opts [] t1) l') `is` dates
+          registerdates (registerReportAsText opts $ registerReport opts (optsToFilterSpec opts [] t1) l') `is` dates
               where opts = [Period periodexpr]
     ""     `gives` ["2008/01/01","2008/06/01","2008/06/02","2008/06/03","2008/12/31"]
     "2008" `gives` ["2008/01/01","2008/06/01","2008/06/02","2008/06/03","2008/12/31"]
@@ -550,7 +551,7 @@
     "monthly" `gives` ["2008/01/01","2008/06/01","2008/12/01"]
     "quarterly" `gives` ["2008/01/01","2008/04/01","2008/10/01"]
     let opts = [Period "yearly"]
-    showRegisterReport opts (optsToFilterSpec opts [] t1) l `is` unlines
+    (registerReportAsText opts $ registerReport opts (optsToFilterSpec opts [] t1) l) `is` unlines
      ["2008/01/01 - 2008/12/31         assets:bank:saving               $1           $1"
      ,"                                assets:cash                     $-2          $-1"
      ,"                                expenses:food                    $1            0"
@@ -560,9 +561,9 @@
      ,"                                liabilities:debts                $1            0"
      ]
     let opts = [Period "quarterly"]
-    registerdates (showRegisterReport opts (optsToFilterSpec opts [] t1) l) `is` ["2008/01/01","2008/04/01","2008/10/01"]
+    registerdates (registerReportAsText opts $ registerReport opts (optsToFilterSpec opts [] t1) l) `is` ["2008/01/01","2008/04/01","2008/10/01"]
     let opts = [Period "quarterly",Empty]
-    registerdates (showRegisterReport opts (optsToFilterSpec opts [] t1) l) `is` ["2008/01/01","2008/04/01","2008/07/01","2008/10/01"]
+    registerdates (registerReportAsText opts $ registerReport opts (optsToFilterSpec opts [] t1) l) `is` ["2008/01/01","2008/04/01","2008/07/01","2008/10/01"]
 
   ]
 
@@ -570,13 +571,18 @@
    do 
     l <- samplejournal
     let opts = [Depth "2"]
-    showRegisterReport opts (optsToFilterSpec opts [] t1) l `is` unlines
-     ["2008/01/01 income               income:salary                   $-1          $-1"
-     ,"2008/06/01 gift                 income:gifts                    $-1          $-2"
-     ,"2008/06/03 eat & shop           expenses:food                    $1          $-1"
-     ,"                                expenses:supplies                $1            0"
-     ,"                                assets:cash                     $-2          $-2"
-     ,"2008/12/31 pay off              liabilities:debts                $1          $-1"
+    (registerReportAsText opts $ registerReport opts (optsToFilterSpec opts [] t1) l) `is` unlines
+     ["2008/01/01 income               assets:bank                      $1           $1"
+     ,"                                income:salary                   $-1            0"
+     ,"2008/06/01 gift                 assets:bank                      $1           $1"
+     ,"                                income:gifts                    $-1            0"
+     ,"2008/06/02 save                 assets:bank                      $1           $1"
+     ,"                                assets:bank                     $-1            0"
+     ,"2008/06/03 eat & shop           expenses:food                    $1           $1"
+     ,"                                expenses:supplies                $1           $2"
+     ,"                                assets:cash                     $-2            0"
+     ,"2008/12/31 pay off              liabilities:debts                $1           $1"
+     ,"                                assets:bank                     $-1            0"
      ]
 
   ,"show dollars" ~: show (dollars 1) ~?= "$1.00"
@@ -586,7 +592,7 @@
   ,"unicode in balance layout" ~: do
     l <- readJournalWithOpts []
       "2009/01/01 * медвежья шкура\n  расходы:покупки  100\n  актив:наличные\n"
-    showBalanceReport [] (optsToFilterSpec [] [] t1) l `is` unlines
+    balanceReportAsText [] (balanceReport [] (optsToFilterSpec [] [] t1) l) `is` unlines
       ["                -100  актив:наличные"
       ,"                 100  расходы:покупки"
       ,"--------------------"
@@ -596,7 +602,7 @@
   ,"unicode in register layout" ~: do
     l <- readJournalWithOpts []
       "2009/01/01 * медвежья шкура\n  расходы:покупки  100\n  актив:наличные\n"
-    showRegisterReport [] (optsToFilterSpec [] [] t1) l `is` unlines
+    (registerReportAsText [] $ registerReport [] (optsToFilterSpec [] [] t1) l) `is` unlines
       ["2009/01/01 медвежья шкура       расходы:покупки                 100          100"
       ,"                                актив:наличные                 -100            0"]
 
diff --git a/Hledger/Cli/Utils.hs b/Hledger/Cli/Utils.hs
--- a/Hledger/Cli/Utils.hs
+++ b/Hledger/Cli/Utils.hs
@@ -38,7 +38,7 @@
   -- We kludgily read the file before parsing to grab the full text, unless
   -- it's stdin, or it doesn't exist and we are adding. We read it strictly
   -- to let the add command work.
-  journalFilePathFromOpts opts >>= readJournalFile Nothing >>= either error runcmd
+  journalFilePathFromOpts opts >>= readJournalFile Nothing >>= either (error'.trace "BBB") runcmd
     where
       costify = (if CostBasis `elem` opts then journalConvertAmountsToCost else id)
       runcmd = cmd opts args . costify
@@ -46,7 +46,7 @@
 -- | Get a journal from the given string and options, or throw an error.
 readJournalWithOpts :: [Opt] -> String -> IO Journal
 readJournalWithOpts opts s = do
-  j <- readJournal Nothing s >>= either error return
+  j <- readJournal Nothing s >>= either error' return
   return $ (if cost then journalConvertAmountsToCost else id) j
     where cost = CostBasis `elem` opts
 
diff --git a/Hledger/Cli/Version.hs b/Hledger/Cli/Version.hs
--- a/Hledger/Cli/Version.hs
+++ b/Hledger/Cli/Version.hs
@@ -10,7 +10,7 @@
 import Hledger.Data.Utils
 
 -- version and PATCHLEVEL are set by the makefile
-version       = "0.11.1"
+version       = "0.12.0"
 
 #ifdef PATCHLEVEL
 patchlevel = "." ++ show PATCHLEVEL -- must be numeric !
@@ -41,8 +41,8 @@
                   prettify (major:minor:bugfix:[]) = prettify [major,minor,bugfix,"0"]
                   prettify (major:minor:[])        = prettify [major,minor,"0","0"]
                   prettify (major:[])              = prettify [major,"0","0","0"]
-                  prettify []                      = error "VERSION is empty, please fix"
-                  prettify _                       = error "VERSION has too many components, please fix"
+                  prettify []                      = error' "VERSION is empty, please fix"
+                  prettify _                       = error' "VERSION has too many components, please fix"
 
 versionstr    = prettify $ splitAtElement '.' buildversion :: String
                 where
@@ -70,8 +70,6 @@
   ,"vty"
 #endif
 #if defined(WEB)
-  ,"web (using yesod/hamlet/simpleserver)"
-#elif defined(WEB610)
-  ,"web (using loli/hsp/simpleserver)"
+  ,"web"
 #endif
  ]
diff --git a/MANUAL.markdown b/MANUAL.markdown
--- a/MANUAL.markdown
+++ b/MANUAL.markdown
@@ -4,7 +4,7 @@
 
 # hledger manual
 
-This is the official hledger manual, for version 0.11.1.  You may also
+This is the official hledger manual, for version 0.12.0.  You may also
 want to visit the rest of [hledger.org](http://hledger.org), and for
 background,
 [c++ ledger's manual](http://joyful.com/repos/ledger/doc/ledger.html).
@@ -43,52 +43,57 @@
 
 ### Installing
 
-hledger works on all major platforms; here are the [release
-notes](http://hledger.org/NEWS.html). One of these pre-built
-[binaries](http://hledger.org/binaries/) might work for you, but at
-present these are not very up-to-date, so the usual thing is to build
-with the cabal-install tool:
+hledger works on all major platforms. Here are the [release
+notes](http://hledger.org/NEWS.html), and pre-built
+[binaries](http://hledger.org/binaries/) you should be able to
+download and run. If not, or if you prefer an up-to-date build,
+open a shell/terminal/command window and:
 
-1. If you don't already have the Glasgow Haskell Compiler and
-   cabal-install, download and install the
-   [Haskell Platform](http://hackage.haskell.org/platform/).  Or, you may
-   be able to use platform packages; eg on Ubuntu Lucid, do `apt-get
-   install ghc6 cabal-install happy`.
+1. **ensure you have GHC (6.10 or newer) and cabal-install**
 
-2. Install hledger with cabal-install. Make sure ~/.cabal/bin is in your
-   path; this is required while installing some cabal packages. Eg:
+        $ ghc --version
+        $ cabal --version
 
-        export PATH=$PATH:~/.cabal/bin
-        cabal update
-        cabal install hledger
+    If not, download and install the [Haskell
+    Platform](http://hackage.haskell.org/platform/).  Or, you may be
+    able to use your platform's packaging system, eg on ubuntu lucid
+    do `apt-get install ghc6 cabal-install zlib1g-dev`.  Also make
+    sure `~/.cabal/bin` is in your PATH.
 
-    You can add the following options to the install command to build
-    extra features (if you're new to cabal, I recommend you get the basic
-    install working first, then add these one at a time):
+2. **install hledger with cabal-install**
 
-    - `-fchart` builds the [chart](#chart) command, enabling simple
-      balance pie chart generation. This requires additional GTK/GHC
-      integration libraries (on ubuntu: `apt-get install libghc6-gtk-dev`)
-      and possibly other things - see the
-      [gtk2hs install docs](http://code.haskell.org/gtk2hs/INSTALL).
-      At present this add a lot of build complexity for not much gain.
+        $ cabal update
+        $ cabal install hledger
 
-    - `-fvty` builds the [vty](#vty) command, enabling a basic
-      curses-style user interface. This does not work on microsoft
-      windows, unless possibly with cygwin.
+#### Install options
 
-    - `-fweb` builds the [web](#web) command, enabling a web-based user
-      interface. This requires GHC 6.12. If you are stuck with GHC 6.10,
-      you can use `-fweb610` instead, to build an older version of the web
-      interface.
+You can add the following options to the basic `cabal install hledger`
+command to build extra features. I recommend you get the basic install
+working first, then try these one at a time:
 
-If you have any trouble, proceed at once to [Troubleshooting](#troubleshooting) for help!
+- `-fweb` builds the [web](#web) command, enabling a web-based user
+  interface. This requires GHC 6.12. If you are stuck with GHC 6.10,
+  you can use `-fweb610` instead, to build an older version of the web
+  interface.
 
+- `-fvty` builds the [vty](#vty) command, enabling a basic
+  curses-style user interface. This does not work on microsoft
+  windows, unless possibly with cygwin.
+
+- `-fchart` builds the [chart](#chart) command, enabling rudimentary
+  pie chart generation. This requires additional GTK-related libraries
+  (on ubuntu: `apt-get install libghc6-gtk-dev`) and possibly [other
+  things](http://code.haskell.org/gtk2hs/INSTALL).
+
+#### If you have trouble..
+
+proceed to [Troubleshooting](#troubleshooting) for help!
+
 ### Basic usage
 
 Basic usage is:
 
-    hledger [OPTIONS] [COMMAND [PATTERNS]]
+    $ hledger [OPTIONS] [COMMAND [PATTERNS]]
 
 [OPTIONS](#overview) may appear anywhere on the command line.
 [COMMAND](#commands) is one of: add, balance, chart, convert, histogram,
@@ -107,31 +112,42 @@
 enter a few transactions. Now you can try some of these commands, or read
 on:
 
-    hledger --help                        # show command-line help
-    hledger balance                       # all accounts with aggregated balances
-    hledger bal --depth 1                 # only top-level accounts
-    hledger register                      # transaction register
-    hledger reg income                    # transactions to/from an income account
-    hledger reg checking                  # checking transactions
-    hledger reg desc:shop                 # transactions with shop in the description
-    hledger histogram                     # transactions per day, or other interval
-    hledger add                           # add some new transactions to the journal file
-    hledger vty                           # curses ui, if installed with -fvty
-    hledger web                           # web ui, if installed with -fweb or -fweb610
-    hledger chart                         # make a balance chart, if installed with -fchart
+    $ hledger --help                        # show command-line help
+    $ hledger balance                       # all accounts with aggregated balances
+    $ hledger bal --depth 1                 # only top-level accounts
+    $ hledger register                      # transaction register
+    $ hledger reg income                    # transactions to/from an income account
+    $ hledger reg checking                  # checking transactions
+    $ hledger reg desc:shop                 # transactions with shop in the description
+    $ hledger histogram                     # transactions per day, or other interval
+    $ hledger add                           # add some new transactions to the journal file
+    $ hledger vty                           # curses ui, if installed with -fvty
+    $ hledger web                           # web ui, if installed with -fweb or -fweb610
+    $ hledger chart                         # make a balance chart, if installed with -fchart
 
 You'll find more examples below.
 
 <a name="file-format" />
 
-### Journal file
+#### Journal file
 
-hledger's data file, aka the journal, is a standard accounting
-[general journal](http://en.wikipedia.org/wiki/General_journal) in a plain
-text format. It contains a number of transactions, each describing a
-transfer of money (or another commodity) between two or more named
-accounts. Here's an example:
+hledger reads data from a plain text file, called a *journal* because
+it represents a standard accounting [general
+journal](http://en.wikipedia.org/wiki/General_journal).  It contains a
+number of transactions, each describing a transfer of money (or
+any commodity) between two or more named accounts, in a simple
+format readable by both hledger and humans.
 
+You can use hledger without learning any more about this file; just
+use the [add](#add) or [web](#web) commands.
+
+Many users, though, edit the journal file directly with a text
+editor. This is a distinguishing feature of hledger (and c++ ledger.)
+You can even do this while the web interface is running, and see the
+changes right away.
+
+Here's an example:
+
     ; A sample journal file. This is a comment.
     
     2008/01/01 income               ; <- transaction's first line starts in column 0, contains date and description
@@ -156,8 +172,8 @@
         assets:bank:checking
 
 Each transaction has a date, optional description, and two or more
-postings (of some amount to some account) which must balance to 0. As a
-convenience, one posting's amount may be left blank and will be inferred.
+postings, of some amount to some account. The amounts within a transaction must balance,
+ie add up to 0. Or, you can leave one amount blank and it will be inferred.
 
 Note that account names may contain single spaces, while the amount must
 be separated from the account name by at least two spaces.
@@ -168,9 +184,9 @@
 the minus sign next to the number (`$-1`), but it may also go before the
 currency symbol/commodity name (`-$1`).
 
-hledger's file format aims to be compatible with c++ ledger, so you can
-use both tools. For more details, see
-[File format compatibility](#file-format-compatibility).
+hledger's file format aims to be compatible with c++ ledger, so you
+can use both tools on your journal. For more details, see [File format
+compatibility](#file-format-compatibility).
 
 ## Reference
 
@@ -224,7 +240,7 @@
       -C       --cleared          report only on cleared transactions
       -U       --uncleared        report only on uncleared transactions
       -B       --cost, --basis    report cost of commodities
-               --depth=N          hide accounts/transactions deeper than this
+               --depth=N          hide sub-accounts deeper than this
       -d EXPR  --display=EXPR     show only transactions matching EXPR (where
                                   EXPR is 'dOP[DATE]' and OP is <, <=, =, >=, >)
                --effective        use transactions' effective dates, if any
@@ -233,6 +249,7 @@
                --flat             balance: show full account names, unindented
                --drop=N           balance: with --flat, elide first N account name components
                --no-total         balance: hide the final total
+      -D       --daily            register, stats: report by day
       -W       --weekly           register, stats: report by week
       -M       --monthly          register, stats: report by month
       -Q       --quarterly        register, stats: report by quarter
@@ -267,18 +284,25 @@
 
 ##### register
 
-The register command displays postings, one per line, and their running
-total. With a [reporting interval](#reporting-interval) it will aggregate
-similar postings within each interval.
-
-Examples:
+The register command displays postings, one per line, and their running total.
+With no [filter patterns](#filter-patterns), this is not all that different from [print](#print):
 
     $ hledger register
-    $ hledger register --monthly -E rent
 
-Note `--depth` doesn't work too well with `register` currently;
-it hides deeper postings rather than aggregating them.
+More typically, use it to see a specific account's activity:
 
+    $ hledger register assets:bank:checking
+
+The `--depth` option limits the amount of sub-account detail displayed:
+
+    $ hledger register assets:bank:checking --depth 2
+
+With a [reporting interval](#reporting-interval) it shows aggregated
+summary postings within each interval:
+
+    $ hledger register --monthly rent
+    $ hledger register --monthly -E food --depth 4
+
 ##### balance
 
 The balance command displays accounts and their balances, indented to show the account hierarchy.
@@ -551,49 +575,7 @@
 
 > `2010/01/31` or `2010/1/31` or `2010-1-31` or `2010.1.31`
 
-The [add](#add) command and the [web](#web) add form, as well as some
-other places, accept [smart dates](#smart-dates) - more about those below.
-
-##### Default year
-
-You can set a default year with a `Y` directive in the journal, then
-subsequent dates may be written as month/day. Eg:
-
-    Y2009
-    
-    12/15 ...
-    
-    Y2010
-    
-    1/31 ...
-
-##### Actual/effective dates
-
-Real-life transactions sometimes have two (or more) dates of interest.
-For example, you might buy a movie ticket on friday with a debit or credit
-card, and the transaction might appear in your bank account on monday.
-When you don't care about this, just record one date. When you do care,
-you can record two dates separated by `=`: the *actual date* on the left
-and the *effective date* on the right. Here's how hledger and ledger users
-use these terms:
-
-    ; The ticket purchase took EFFECT on friday 19th,
-    ; but ACTUALly appeared in bank statement on monday 23rd.
-    ; The effective date is often the earlier one, but it goes on the right.
-    ;
-    ;  ACTUAL=EFFECTIVE
-    2010/2/23=2010/2/19 movie ticket
-      expenses:cinema        $10
-      assets:bank:checking  $-10
-
-You can use the `--effective` flag to prefer the effective date in
-reports.  This can be useful, eg, to adjust your transaction dates to
-match the ones in your bank statement for easier reconciling.
-
-The year may optionally be omitted in the second date.
-
-hledger does not allow separate dates for individual postings, unlike c++
-ledger.
+The year is optional if you define a [default year](#default-year).
 
 ##### Smart dates
 
@@ -610,13 +592,74 @@
 -   `last week` (the monday of the week before this one)
 -   `today`, `yesterday`, `tomorrow`
 
-Spaces are optional, so eg: `-p lastmonth` is valid.
+Spaces are optional, so eg: `-b lastmonth` is valid.
 
+##### Actual & effective dates
+
+Real-life transactions sometimes have more than one date.  For
+example, you might buy a movie ticket on friday with a bank debit
+card, and the transaction might appear in your bank account on monday.
+hledger and ledger users call these the *effective date* and *actual
+date* respectively. We say:
+
+> *"The ticket purchase took EFFECT on friday, but ACTUALly appeared in my bank balance on monday."*
+
+You can often think of effective date as "my date" and actual date as "bank's date".
+
+When these dates differ, as in the example above, hledger's daily
+balances will not exactly match your bank's. But exact daily
+reconciling can be quite useful, to see precisely when disagreements
+arise. There are several ways you can handle this:
+
+1. don't bother with exact daily reconciling; accept temporary
+   disagreements between hledger and bank balances.
+
+2. adjust manually recorded transactions to actual bank dates when necessary.
+   Your hledger balance will match your bank's exactly, but you no longer have
+   a record of when transactions *really* happened.
+
+3. record both dates separated by an equals sign: the *actual date* on
+   the left and the *effective date* on the right. The year is
+   optional in the second date.
+
+Here's an example of the last approach: on friday 19 you record:
+
+        2010/2/19 movie
+          expenses:cinema          $10
+          assets:checking
+
+hledger shows $10 less in your checking account through saturday and
+sunday.. but your online bank statement does not. It shows the
+transaction clearing a few days later, on monday 23. So you then
+insert that actual date:
+
+        ;  ACTUAL=EFFECTIVE
+        2010/2/23=2010/2/19 movie
+          expenses:cinema          $10
+          assets:checking
+
+Now your hledger reports match your bank's daily balance exactly,
+since they use the actual date by preference. To report based on
+effective dates instead, use the `--effective` flag.
+
+##### Default year
+
+You can set a default year with a `Y` directive in the journal, then
+subsequent dates may be written as month/day. Eg:
+
+    Y2009
+    
+    12/15  ; <- equivalent to 2009/12/15
+    
+    Y2010
+    
+    1/31  ; <- equivalent to 2010/1/31
+
 #### Period expressions
 
 hledger supports flexible "period expressions" with the `-p/--period`
-option to select transactions within a period of time (like 2009) and/or
-with a reporting interval (like weekly). hledger period expressions are
+option to select transactions within a period of time (eg in 2009) and/or
+with a reporting interval (eg weekly). hledger period expressions are
 similar but not identical to c++ ledger's.
 
 Here is a basic period expression specifying the first quarter of 2009
@@ -638,7 +681,7 @@
     -p "this year to 4/1"
 
 If you specify only one date, the missing start or end date will be the
-earliest or latest transaction in your journal data:
+earliest or latest transaction in your journal:
 
     -p "from 2009/1/1"  (everything after january 1, 2009)
     -p "from 2009/1"    (the same)
@@ -652,6 +695,9 @@
     -p "2009/1"         (the month of jan; equivalent to "2009/1/1 to 2009/2/1")
     -p "2009/1/1"       (just that day;    equivalent to "2009/1/1 to 2009/1/2")
 
+The `-b/--begin` and `-e/--end` options may be used as a shorthand for
+`-p 'from ...'` and `-p 'to ...'` respectively. But note [-p overrides other flags](#p-overrides-other-flags).
+
 ##### Reporting interval
 
 You can also specify a reporting interval, which causes the "register"
@@ -664,13 +710,14 @@
     -p "monthly from 2008"
     -p "quarterly"
 
-A reporting interval may also be specified with the -W/--weekly,
--M/--monthly, -Q/--quarterly, and -Y/--yearly options. However..
+A reporting interval may also be specified with the `-D/--daily`,
+`-W/--weekly`, `-M/--monthly`, `-Q/--quarterly`, and `-Y/--yearly`
+options. But note...
 
 ##### -p overrides other flags
 
-Note: any period option on the command line will override the -b, -e, -W,
--Q and -Y flags.
+A `-p/--period` option on the command line will cause any
+`-b`/`-e`/`-D`/`-W`/`-M`/`-Q`/`-Y` flags to be ignored.
 
 #### Display expressions
 
@@ -680,27 +727,17 @@
 to be used for calculation).
 
 hledger currently supports a very small subset of c++ ledger's display
-expressions, namely: transactions before or after a date.  This is useful
-for displaying your recent check register with an accurate running
-total. Note the use of \>= here to include the first of the month:
+expressions, namely: transactions before or after a date.  This is
+useful for displaying a portion of your checking register with an
+accurate running total. Eg, to show the balance since the first of the month:
 
-    $ hledger register -d "d>=[this month]"
+    $ hledger register -d "d>=[1]"
 
 #### Depth limiting
 
 With the `--depth N` option, reports will show only the uppermost accounts
-in the account tree, down to level N. This is most useful with
-[balance](#balance) (and [chart](#chart)). For example:
-
-    $ hledger balance --depth 2
-
-shows a more concise balance report displaying only the top two levels of
-accounts. This example with [register](#register):
-
-    $ hledger register --depth 1
-
-would show only the postings to top-level accounts, which usually means
-none.
+in the account tree, down to level N. See the [balance](#balance),
+[register](#register) and [chart](#chart) examples.
 
 #### Prices
 
@@ -755,7 +792,7 @@
 
 You can pull in the content of additional journal files, by writing lines like this:
 
-    !include other/file.journal
+    !include path/to/file.journal
 
 The `!include` directive may only be used in journal files, and currently
 it may only include other journal files (eg, not timelog files.)
@@ -793,7 +830,7 @@
 
 hledger will also read timelog files in timeclock.el format. As a
 convenience, if you invoke hledger via an "hours" symlink or copy, it uses
-your timelog file (\~/.timelog or $TIMELOG) by default, rather than your
+your timelog file (`~/.timelog` or `$TIMELOG`) by default, rather than your
 journal.
 
 Timelog entries look like this:
@@ -845,6 +882,9 @@
 and c++ledger if you avoid the more esoteric syntax.  Occasionally you'll
 need to make small edits to restore compatibility for one or the other.
 
+hledger does not allow separate dates for individual postings, unlike c++
+ledger.
+
 #### Features not supported
 
 c++ ledger features not currently supported include: modifier and periodic
@@ -910,23 +950,28 @@
 
 -   hledger recognises description and negative patterns by "desc:"
     and "not:" prefixes, unlike ledger 3's free-form parser
+
 -   hledger doesn't require a space before command-line option
-    values, you can write -f-
+    values, eg either `-f-` or `-f -` is fine
+
 -   hledger's weekly reporting intervals always start on mondays
+
 -   hledger shows start and end dates of the intervals requested,
     not just the span containing data
+
 -   hledger period expressions don't support "biweekly",
     "bimonthly", or "every N days/weeks/..."
+
 -   hledger always shows timelog balances in hours
+
 -   hledger splits multi-day timelog sessions at midnight
+
 -   hledger doesn't track the value of commodities with varying
     price; prices are fixed as of the transaction date
+
 -   hledger print shows amounts for all postings, and shows unit
-    prices for amounts which have them
--   hledger assumes a transaction's actual date comes first, and is
-    required, while the optional effective date comes second (cf
-    [Actual and effective dates](#actual-and-effective-dates))
--   hledger does not support per-posting actual or effective dates
+    prices for amounts which have them. (This currently means that
+    it does not print multi-commodity transactions in valid journal format.)
 
 ### More examples and recipes
 
@@ -952,7 +997,7 @@
   Eg: join the #hledger channel with your IRC client and type: "sm: I did ... and ... happened", then leave
   that window open until you get helped.
 
-- **Did you cabal update ?** If you didn't already, ``cabal update`` and try again.
+- **Did you cabal update ?** If you didn't already, `cabal update` and try again.
 
 - **Do you have a new enough version of GHC ?** hledger supports GHC 6.10
   and 6.12. Building with the `-fweb` flag requires 6.12 or greater.
@@ -969,12 +1014,15 @@
     then try installing hledger again.
 
 - **Could not run trhsx.**
-  You are installing with `-fweb610`, which needs to run the ``trhsx`` executable.
-  It is installed by the hsx package in ~/.cabal/bin, which needs to be in
-  your path.
+  You are installing with `-fweb610`, which needs to run the `trhsx` executable.
+  It is installed by the hsx package in `~/.cabal/bin`, which needs to be in
+  your path. Do something like:
 
+        $ echo 'export PATH=$PATH:~/.cabal/bin' >> ~/.bash_profile; source ~/.bash_profile
+
+
 - **Could not run happy.**
-  A package (eg haskell-src-exts) needs to run the ``happy`` executable.
+  A package (eg haskell-src-exts) needs to run the `happy` executable.
   If not using the haskell platform, install the appropriate platform
   package which provides it (eg apt-get install happy).
 
@@ -1008,21 +1056,36 @@
     error. You could try installing the
     [previous version](http://hackage.haskell.org/package/hledger):
 
-        cabal install hledger-0.x
+        $ cabal install hledger-0.x
 
     or (preferably) the latest development version: install
     [darcs](http://darcs.net) and then:
 
-        darcs get --lazy http://joyful.com/repos/hledger
-        cd hledger/hledger-lib
-        cabal install
-        cd ..
-        cabal install [-f...]
+        $ darcs get --lazy http://joyful.com/repos/hledger
+        $ cd hledger/hledger-lib
+        $ cabal install
+        $ cd ..
+        $ cabal install [-f...]
 
 - **An error while building non-hledger packages.**
   Resolve these problem packages one at a time. Eg, cabal install pkg1.
   Look for the cause of the failure near the end of the output. If it's
   not apparent, try again with `-v2` or `-v3` for more verbose output.
+
+- **This package indirectly depends on multiple versions of the same package.**
+  You may have previously installed some of hledger's dependencies
+  depending on different versions of (eg) parsec. Then cabal install hledger gives
+  an error like this:
+
+        Warning: This package indirectly depends on multiple versions of the same
+        package. This is highly likely to cause a compile failure.
+        package yesod-0.5.0.3 requires parsec-2.1.0.1
+        package csv-0.1.1 requires parsec-3.1.0
+        ...
+
+    The above example could be resolved by, eg:
+
+        $ cabal install yesod --reinstall --constraint 'parsec == 3.1.0"
 
 - **cabal fails to resolve dependencies.**
   It's possible for cabal to get confused, eg if you have
diff --git a/NEWS.markdown b/NEWS.markdown
new file mode 100644
--- /dev/null
+++ b/NEWS.markdown
@@ -0,0 +1,443 @@
+---
+title: hledger news
+---
+
+# hledger news
+
+## 2010/9/5 hledger 0.12
+
+  * installing: fix a build error with parsec 3 (#22)
+  * installing: require exactly matching hledger-lib version for more robust builds
+  * installing: explicit data-object dependency to ensure hledger and hledger-lib use the same time version
+  * installing: build threaded and with warnings
+  * installing: drop -fweb610 flag
+  * installing: add gtk2hs-buildtools dependency needed to build with -fchart
+  * add -D/--daily flag
+  * register: with --depth, clip account names or aggregate postings rather than excluding them
+  * web: new, better web ui; accounts are now a permanent sidebar; add form uses auto-completing combo fields
+  * fix !include with deeply nested directories (#21)
+  * fix obscured date parse errors with parsec 3
+  * handle unicode better in errors
+  * fix a ghc 6.12.3 error when running interpreted
+
+## 2010/07/17 hledger 0.11.1
+
+  * fix --version output
+
+## 2010/07/17 hledger 0.11
+
+  * split --help, adding --help-options and --help-all/-H, and make
+    it the default command
+  * use "journal" instead of "ledger file"; default suffix is
+    .journal, default file is \~/.journal
+  * auto-create missing journal files rather than giving an error
+  * new format-detecting file reader (mixed journal transactions
+    and timelog entries are no longer supported)
+  * work around for first real-world rounding issue (test zero to 8
+    decimal places instead of 10)
+  * when reporting a balancing error, convert the error amount to
+    cost
+  * parsing: support double-quoted commodity symbols, containing
+    anything but a newline or double quote
+  * parsing: allow minus sign before commodity symbol as well as
+    after (also fixes a convert bug)
+  * parsing: fix wrong parse error locations within postings
+  * parsing: don't let trailing whitespace in a timelog description
+    mess up account names
+  * add: allow blank descriptions
+  * balance: --flat provides a simple non-hierarchical format
+  * balance: --drop removes leading account name components from a
+    --flat report
+  * print, register, balance: fix layout issues with
+    mixed-commodity amounts
+  * print: display non-simple commodity names with double-quotes
+  * stats: layout tweaks, add payee/description count
+  * stats: don't break on an empty file
+  * stats: -p/--period support; a reporting interval generates
+    multiple reports
+  * test: drop verbose test runner and testpack dependency
+  * web: a new web ui based on yesod, requires ghc 6.12; old ghc
+    6.10-compatible version remains as -fweb610
+  * web: allow wiki-like journal editing
+  * web: warn and keep running if reloading the journal gives an
+    error
+  * web: --port and --base-url options set the webserver's tcp port
+    and base url
+  * web: slightly better browser opening on microsoft windows,
+    should find a standard firefox install now
+  * web: in a web-enabled build on microsoft windows, run the web
+    ui by default
+
+Stats: 55 days and 136 commits since last release. Now at 5552
+lines of code with 132 tests and 54% unit test coverage.
+
+## 2010/05/23 hledger 0.10
+
+  * fix too-loose testpack dependency, missing safe dependency
+  * fix ghc 6.12 compatibility with -fweb
+  * fix handling of non-ascii arguments with ghc 6.12
+  * fix "0.8" in --version output
+  * fix an occasional stack overflow error due to infinite
+    recursion in Posting/Transaction equality tests
+  * the -fwebhappstack build flag is gone for now, to avoid a cabal
+    problem
+  * parsing: if there is no description, don't require a space
+    after the transaction date
+  * parsing: balance balanced-virtual postings separately, allow
+    them to have an implicit amount
+  * parsing: timelog entries now generate balanced transactions,
+    using virtual postings
+  * parsing: simpler high-level parse error message
+  * parsing: clearer bad date errors
+  * add: fix wrongful program exit on bad dates
+  * print: negative account patterns now exclude transactions
+    containing any posting to a matched account
+  * vty: rename the ui command to vty for consistency
+  * vty: fix restricted account scope when backing up to top level
+  * web: fix non-ascii handling with ghc 6.12
+  * web: fix a bug possibly affecting reload-on-change
+  * consolidate module namespace under Hledger, api cleanups
+
+Stats: 44 days, 81 commits since last release. Now at 4904 lines of
+code including tests, 144 tests, 53% coverage.
+
+## 2010/04/10 hledger 0.9
+
+  * ghc 6.12 support
+  * split off hledger-lib package, containing core types & utils
+  * parsing: ignore D, C, N, tag, end tag directives; we should now
+    accept any ledger 2.6 file
+  * parsing: allow numbers in commodities if double-quoted, like
+    ledger
+  * parsing: allow transactions with empty descriptions
+  * parsing: show a better error for illegal month/day numbers in
+    dates
+  * parsing: don't ignore trailing junk in a smart date, eg in web
+    add form
+  * parsing: don't ignore unparsed text following an amount
+  * parsing: @ was being treated as a currency symbol
+  * add: fix precision handling in default amounts (\#19)
+  * add: elide last amount in added transactions
+  * convert: keep original description by default, allow
+    backreferences in replace pattern
+  * convert: basic csv file checking, warn instead of dying when it
+    looks wrong
+  * convert: allow blank/comment lines at end of rules file
+  * print: always show zero amounts as 0, hiding any
+    commodity/decimal places/price, like ledger
+  * register: fix bad layout with years < 1000
+  * register: fix a Prelude.head error with reporting interval,
+    --empty, and --depth
+  * register: fix a regression, register should not show posting
+    comments
+  * register: with --empty, intervals should continue to ends of
+    the specified period
+  * stats: better output when last transaction is in the future
+  * stats: show commodity symbols, account tree depth, reorder
+    slightly
+  * web: -fweb now builds with simpleserver; to get happstack, use
+    -fwebhappstack instead
+  * web: pre-fill the add form with today's date
+  * web: help links, better search form wording
+  * web: show a proper error for a bad date in add form (\#17)
+  * web: fix for unicode search form values
+  * web: fix stack overflow caused by regexpr, and handle requests
+    faster (\#14)
+  * web: look for more-generic browser executables
+  * web: more robust browser starting (\#6)
+  * error message cleanups
+  * more tests, refactoring, docs
+
+Stats: 58 days, 2 contributors, 102 commits since last release. Now
+at 3983 lines of non-test code, 139 tests, 53% coverage.
+
+## 2010/02/11 hledger 0.8
+
+  * parsing: in date=date2, use first date's year as a default for
+    the second
+  * add: ctrl-d doesn't work on windows, suggest ctrl-c instead
+  * add: --no-new-accounts option disallows new accounts (Roman
+    Cheplyaka)
+  * add: re-use the previous transaction's date as default (Roman
+    Cheplyaka)
+  * add: a command-line argument now filters by account during
+    history matching (Roman Cheplyaka)
+  * chart: new command, generates balances pie chart (requires
+    -fchart flag, gtk2hs) (Roman Cheplyaka, Simon Michael)
+  * register: make reporting intervals honour a display expression
+    (\#18)
+  * web: fix help link
+  * web: use today as default when adding with a blank date
+  * web: re-enable account/period fields, they seem to be fixed,
+    along with file re-reading (\#16)
+  * web: get static files from the cabal data dir, or the current
+    dir when using make (\#13)
+  * web: preserve encoding during add, assuming it's utf-8 (\#15)
+  * fix some non-utf8-aware file handling (\#15)
+  * filter ledger again for each command, not just once at program
+    start
+  * refactoring, clearer data types
+
+Stats: 62 days, 2 contributors, 76 commits since last release. Now
+at 3464 lines of non-test code, 97 tests, 53% test coverage.
+
+## 2009/12/11 hledger 0.7
+
+  * price history support (first cut): P directives now work,
+    though differently from c++ ledger. Each posting amount takes its
+    fixed unit price from the price history (or
+    @) when available. This is simple and useful for things like
+    foreign currency expenses (but not investment tracking). Like
+    ledger, balance and register don't show amount prices any more, and
+    don't separate differently-priced amounts. Unlike ledger, print
+    shows all amount prices, and supports -B.
+  * --effective option, will use transactions' effective dates if
+    any
+  * convert: new rules file format, find/create rules file
+    automatically, more robust parsing, more useful --debug output
+  * print: always sort by date, fix long account name truncation,
+    align amounts, show end of line comments, show all amounts for
+    clarity (don't elide the final balancing amount)
+  * ui: use vty 4, fixes non-ascii and gnome terminal problems
+    (issues \#3, \#4)
+  * web: allow data entry, react to data file changes, better
+    layout, help links, remove histogram command and filter fields for
+    now, fix bad localhost redirect, filter form did not work in eg
+    firefox (issue \#7), reset link did not work in all browsers
+  * parsing: require whitespace between date and status code, allow
+    (and ignore) a time in price records, better error messages,
+    non-zero exit code on parse failure
+  * display non-ascii error messages properly (issue \#5)
+  * fix an arithmetic bug that occasionally rejected valid
+    transactions
+  * fix a regex bug in showtree
+  * don't break if HOME is undefined
+  * --debug now implies --verbose
+  * add functional tests like ledger's, use test-framework for
+    speedy running, release shelltestrunner as a separate package
+  * many hlint cleanups (Marko Kocić)
+  * many site and documentation updates
+
+Stats: 60 days, 1 contributor, 50 commits since last release. Now
+at 3377 lines of non-test code, 97 tests, 53% test coverage.
+
+## 2009/06/22 hledger 0.6.1
+
+  * avoid use of exitSuccess which was breaking ghc 6.8/base 3
+    compatibility (issue \#2)
+
+## 2009/06/13 hledger 0.6
+
+  * now cabal-installable on unix, mac, and windows, with Haskell
+    Platform
+  * provide experimental platform binaries
+  * parsing: fix a silly failure to open ledger file paths
+    containing \~
+  * parsing: show better errors for unbalanced transaction and
+    missing default year
+  * parsing: allow parentheses and brackets inside account names,
+    as ledger does
+  * parsing: fail on empty account name components, don't just
+    ignore
+  * add: description passed as arguments now affects first
+    transaction only
+  * add: better handling of virtual postings and default amounts
+  * print, register: show virtual accounts bracketed/parenthesised
+  * web: improved web ui supporting full patterns & period
+    expressions
+  * new "stats" command reports some ledger statistics
+  * many dev/doc/deployment infrastructure improvements
+  * move website into darcs repo, update home page
+  * move issue tracker to google code
+
+Release stats:
+
+  * Contributors: Simon Michael
+  * Days since last release: 21
+  * Commits: 94
+  * Lines of non-test code: 2865
+  * Tests: 82
+  * Test coverage: 53% expressions
+  * Known errors: 3 (inconsistent eliding, vty-related failures)
+  * Performance: similar
+    (http://hledger.org/profs/200906131120.bench)
+
+## 2009/05/23 hledger 0.5.1
+
+  * two fixes: really disable vty flag by default, and include
+    ConvertCommand in cabal file
+
+## 2009/05/23 hledger 0.5
+
+  * the vty flag is disabled by default again, to ease installation
+    on windows
+  * use ledger 3 terminology: a ledger contains transactions which
+    contain postings
+  * new "add" command prompts for transactions interactively and
+    adds them to the ledger
+  * new "convert" command transforms bank CSV exports to ledger
+    format, with rule-based cleanup
+  * new "histogram" command shows transaction counts per day or
+    other reporting interval
+  * most commands now work properly with UTF8-encoded text (Sergey
+    Astanin)
+  * invoking as "hours" is now less different: it just uses your
+    timelog, not your ledger
+  * ..quarterly/-Q option summarises by quarter
+  * ..uncleared/-U option looks only at uncleared transactions
+  * be more accurate about checking balanced amounts, don't rely on
+    display precision
+  * enforce balancing for bracketed virtual postings
+  * fix bug in eliding of posting amounts
+  * don't show trailing spaces on amountless postings
+  * parse null input as an empty ledger
+  * don't treat comments as part of transaction descriptions
+  * require some postings in ledger transactions
+  * require a non-empty description in ledger transactions
+  * don't fail when matching an empty pattern, as in "not:"
+  * make the web server handle the null path
+  * code, api and documentation updates
+  * add a contributor agreement/list
+
+Release stats:
+
+  * Contributors: Simon Michael, Sergey Astanin
+  * Days since last release: 51
+  * Commits: 101
+  * Lines of non-test code: 2795
+  * Tests: 76
+  * Known errors: 0
+
+## 2009/04/03 hledger 0.4
+
+  * new "web" command serves reports in a web browser (install with
+    -f happs to build this)
+  * make the vty-based curses ui a cabal build option, which will
+    be ignored on MS windows
+  * drop the ..options-anywhere flag, that is now the default
+  * patterns now use not: and desc: prefixes instead of \^ and \^\^
+  * patterns are now case-insensitive, like ledger
+  * !include directives are now relative to the including file (Tim
+    Docker)
+  * "Y2009" default year directives are now supported, allowing m/d
+    dates in ledger
+  * individual transactions now have a cleared status
+  * unbalanced entries now cause a proper warning
+  * balance report now passes all ledger compatibility tests
+  * balance report now shows subtotals by default, like ledger 3
+  * balance report shows the final zero total when -E is used
+  * balance report hides the final total when ..no-total is used
+  * ..depth affects print and register reports (aggregating with a
+    reporting interval, filtering otherwise)
+  * register report sorts transactions by date
+  * register report shows zero-amount transactions when -E is used
+  * provide more convenient timelog querying when invoked as
+    "hours"
+  * multi-day timelog sessions are split at midnight
+  * unterminated timelog sessions are now counted. Accurate time
+    reports at last!
+  * the test command gives better ..verbose output
+  * ..version gives more detailed version numbers including
+    patchlevel for dev builds
+  * new make targets include: ghci, haddocktest, doctest, unittest,
+    view-api-docs
+  * a doctest-style framework for functional/shell tests has been
+    added
+
+Release stats:
+
+  * Contributors: Simon Michael, Tim Docker; thanks to the HAppS,
+    happstack and testpack developers
+  * Days since release: 76
+  * Commits: 144
+  * Lines of non-test code: 2367
+  * Tests: 56
+  * Known errors: 0
+
+## 2009/01/17 hledger 0.3
+
+  * count timelog sessions on the day they end, like ledger, for
+    now
+  * when options are repeated, use the last instead of the first
+  * builds with ghc 6.10 as well as 6.8
+  * a simple ui for interactive report browsing: hledger ui
+  * accept smart dates everywhere (YYYYMMDD, Y/M/D, Y, M/D, D, jan,
+    today, last week etc.)
+  * ..period/-p flag accepting period expressions like "in 2008",
+    "weekly from last month"..
+  * -W/-M/-Y convenience flags to summarise register weekly,
+    monthly, yearly
+  * ..depth and -E flags also affect summarised register reports
+    (including depth=0)
+  * ..display/-d flag supporting date predicates (like "d<[DATE]",
+    "d\>=[DATE]")
+  * !include directive to include additional ledger files
+  * !account directive to set a default parent account
+  * Added support for reading historical prices from files
+  * timelog and ledger entries can be intermixed in one file
+  * modifier and periodic entries can appear anywhere (but are
+    still ignored)
+  * help and readme improvements
+  * runs much faster than 0.2
+
+Release stats:
+
+  * Contributors: Simon Michael, Nick Ingolia, Tim Docker; thanks
+    to Corey O'Connor & the vty team
+  * Lines of non-test code: 2123
+  * Tests: 58
+  * Known errors: 1
+
+## 2008/11/23 hledger 0.2
+
+  * fix balance report totals when filtering by account
+  * fix balance report selection of accounts when filtering by
+    account
+  * fix a bug with account name eliding in balance report
+  * if we happen to be showing a not-yet-auto-balanced entry, hide
+    the AUTO marker
+  * fix print command filtering by account
+  * omit transactions with zero amount from register report
+  * Fix bug in parsing of timelogs
+  * rename ..showsubs to ..subtotal, like ledger
+  * drop ..usage flag
+  * don't require quickcheck
+  * priced amounts (eg "10h @ $50") and ..basis/..cost/-B flag to
+    show them with cost basis
+  * easy ..depth option, equivalent to c++ ledger's -d 'l<=N'
+  * smarter y/m/d date parsing for -b and -e (any number of digits,
+    month and day default to 1, separator can be / - or .)
+  * -n flag for balance command
+  * ..empty/-E flag
+  * build a library, as well as the exe
+  * new home page url (http://joyful.com/hledger)
+  * publish html and pdf versions of README
+  * detect display preferences for each commodity like c++ ledger
+  * support amounts with multiple currencies/commodities
+  * support ..real/-R flag
+  * support -C/..cleared flag to filter by entry status (not
+    transaction status)
+  * support virtual and balanced virtual transactions
+  * parse comment lines beginning with a space, as from M-; in
+    emacs ledger-mode
+  * allow any non-whitespace in account names, perhaps avoiding
+    misleading missing amounts errors
+  * clearer error message when we can't balance an entry
+  * when we fail because of more than one missing amount in an
+    entry, show the full entry
+  * document the built-in test runner in ..help
+  * add a ..verbose/-v flag, use it to show more test-running
+    detail
+
+Release stats:
+
+  * Contributors: Simon Michael, Tim Docker
+  * Lines of non-test code: 1350
+  * Tests: 43
+  * Known errors: 0
+
+## 2008/10/15 hledger 0.1
+
+Release stats:
+
+  * Contributors: Simon Michael
diff --git a/NEWS.rst b/NEWS.rst
deleted file mode 100644
--- a/NEWS.rst
+++ /dev/null
@@ -1,380 +0,0 @@
----
-title: hledger news
----
-hledger news
-============
-
-2010/07/17 hledger 0.11.1
-.........................
-
-  * fix --version output
-
-2010/07/17 hledger 0.11
-........................
-
-  * split --help, adding --help-options and --help-all/-H, and make it the default command
-  * use "journal" instead of "ledger file"; default suffix is .journal, default file is ~/.journal
-  * auto-create missing journal files rather than giving an error
-  * new format-detecting file reader (mixed journal transactions and timelog entries are no longer supported)
-  * work around for first real-world rounding issue (test zero to 8 decimal places instead of 10)
-  * when reporting a balancing error, convert the error amount to cost
-  * parsing: support double-quoted commodity symbols, containing anything but a newline or double quote
-  * parsing: allow minus sign before commodity symbol as well as after (also fixes a convert bug)
-  * parsing: fix wrong parse error locations within postings
-  * parsing: don't let trailing whitespace in a timelog description mess up account names
-  * add: allow blank descriptions
-  * balance: --flat provides a simple non-hierarchical format
-  * balance: --drop removes leading account name components from a --flat report
-  * print, register, balance: fix layout issues with mixed-commodity amounts
-  * print: display non-simple commodity names with double-quotes
-  * stats: layout tweaks, add payee/description count
-  * stats: don't break on an empty file
-  * stats: -p/--period support; a reporting interval generates multiple reports
-  * test: drop verbose test runner and testpack dependency
-  * web: a new web ui based on yesod, requires ghc 6.12; old ghc 6.10-compatible version remains as -fweb610
-  * web: allow wiki-like journal editing
-  * web: warn and keep running if reloading the journal gives an error
-  * web: --port and --base-url options set the webserver's tcp port and base url
-  * web: slightly better browser opening on microsoft windows, should find a standard firefox install now
-  * web: in a web-enabled build on microsoft windows, run the web ui by default
-
-  Stats: 55 days and 136 commits since last release. Now at 5552 lines of code with 132 tests and 54% unit test coverage.
-
-2010/05/23 hledger 0.10
-........................
-
-  * fix too-loose testpack dependency, missing safe dependency
-  * fix ghc 6.12 compatibility with -fweb
-  * fix handling of non-ascii arguments with ghc 6.12
-  * fix "0.8" in --version output
-  * fix an occasional stack overflow error due to infinite recursion in Posting/Transaction equality tests
-  * the -fwebhappstack build flag is gone for now, to avoid a cabal problem
-  * parsing: if there is no description, don't require a space after the transaction date
-  * parsing: balance balanced-virtual postings separately, allow them to have an implicit amount
-  * parsing: timelog entries now generate balanced transactions, using virtual postings
-  * parsing: simpler high-level parse error message
-  * parsing: clearer bad date errors
-  * add: fix wrongful program exit on bad dates
-  * print: negative account patterns now exclude transactions containing any posting to a matched account
-  * vty: rename the ui command to vty for consistency
-  * vty: fix restricted account scope when backing up to top level
-  * web: fix non-ascii handling with ghc 6.12
-  * web: fix a bug possibly affecting reload-on-change
-  * consolidate module namespace under Hledger, api cleanups
-
-  Stats:
-  44 days, 81 commits since last release.
-  Now at 4904 lines of code including tests, 144 tests, 53% coverage.
-
-2010/04/10 hledger 0.9
-......................
-
-  * ghc 6.12 support
-  * split off hledger-lib package, containing core types & utils
-  * parsing: ignore D, C, N, tag, end tag directives; we should now accept any ledger 2.6 file
-  * parsing: allow numbers in commodities if double-quoted, like ledger
-  * parsing: allow transactions with empty descriptions
-  * parsing: show a better error for illegal month/day numbers in dates
-  * parsing: don't ignore trailing junk in a smart date, eg in web add form
-  * parsing: don't ignore unparsed text following an amount
-  * parsing: @ was being treated as a currency symbol
-  * add: fix precision handling in default amounts (#19)
-  * add: elide last amount in added transactions
-  * convert: keep original description by default, allow backreferences in replace pattern
-  * convert: basic csv file checking, warn instead of dying when it looks wrong
-  * convert: allow blank/comment lines at end of rules file
-  * print: always show zero amounts as 0, hiding any commodity/decimal places/price, like ledger
-  * register: fix bad layout with years < 1000
-  * register: fix a Prelude.head error with reporting interval, --empty, and --depth
-  * register: fix a regression, register should not show posting comments
-  * register: with --empty, intervals should continue to ends of the specified period
-  * stats: better output when last transaction is in the future
-  * stats: show commodity symbols, account tree depth, reorder slightly
-  * web: -fweb now builds with simpleserver; to get happstack, use -fwebhappstack instead
-  * web: pre-fill the add form with today's date
-  * web: help links, better search form wording
-  * web: show a proper error for a bad date in add form (#17)
-  * web: fix for unicode search form values
-  * web: fix stack overflow caused by regexpr, and handle requests faster (#14)
-  * web: look for more-generic browser executables
-  * web: more robust browser starting (#6)
-  * error message cleanups
-  * more tests, refactoring, docs
-
-  Stats:
-  58 days, 2 contributors, 102 commits since last release.
-  Now at 3983 lines of non-test code, 139 tests, 53% coverage.
-
-2010/02/11 hledger 0.8
-......................
-
-  * parsing: in date=date2, use first date's year as a default for the second
-  * add: ctrl-d doesn't work on windows, suggest ctrl-c instead
-  * add: --no-new-accounts option disallows new accounts (Roman Cheplyaka)
-  * add: re-use the previous transaction's date as default (Roman Cheplyaka)
-  * add: a command-line argument now filters by account during history matching (Roman Cheplyaka)
-  * chart: new command, generates balances pie chart (requires -fchart flag, gtk2hs) (Roman Cheplyaka, Simon Michael)
-  * register: make reporting intervals honour a display expression (#18)
-  * web: fix help link
-  * web: use today as default when adding with a blank date
-  * web: re-enable account/period fields, they seem to be fixed, along with file re-reading (#16)
-  * web: get static files from the cabal data dir, or the current dir when using make (#13)
-  * web: preserve encoding during add, assuming it's utf-8 (#15)
-  * fix some non-utf8-aware file handling (#15)
-  * filter ledger again for each command, not just once at program start
-  * refactoring, clearer data types
-
-  Stats:
-  62 days, 2 contributors, 76 commits since last release.
-  Now at 3464 lines of non-test code, 97 tests, 53% test coverage.
-
-2009/12/11 hledger 0.7
-........................
-
-  * price history support (first cut):
-    P directives now work, though differently from c++ ledger. Each
-    posting amount takes its fixed unit price from the price history (or
-    @) when available. This is simple and useful for things like foreign
-    currency expenses (but not investment tracking). Like ledger, balance
-    and register don't show amount prices any more, and don't separate
-    differently-priced amounts. Unlike ledger, print shows all amount
-    prices, and supports -B.
-  * --effective option, will use transactions' effective dates if any
-  * convert: new rules file format, find/create rules file automatically,
-    more robust parsing, more useful --debug output
-  * print: always sort by date, fix long account name truncation, align
-    amounts, show end of line comments, show all amounts for clarity
-    (don't elide the final balancing amount)
-  * ui: use vty 4, fixes non-ascii and gnome terminal problems (issues #3, #4)
-  * web: allow data entry, react to data file changes, better layout, help
-    links, remove histogram command and filter fields for now, fix bad
-    localhost redirect, filter form did not work in eg firefox (issue #7),
-    reset link did not work in all browsers
-  * parsing: require whitespace between date and status code, allow (and
-    ignore) a time in price records, better error messages, non-zero exit
-    code on parse failure
-  * display non-ascii error messages properly (issue #5)
-  * fix an arithmetic bug that occasionally rejected valid transactions
-  * fix a regex bug in showtree
-  * don't break if HOME is undefined
-  * --debug now implies --verbose
-  * add functional tests like ledger's, use test-framework for speedy
-    running, release shelltestrunner as a separate package
-  * many hlint cleanups (Marko Kocić)
-  * many site and documentation updates
-
-  Stats:
-  60 days, 1 contributor, 50 commits since last release.
-  Now at 3377 lines of non-test code, 97 tests, 53% test coverage.
-
-2009/06/22 hledger 0.6.1
-........................
-
-  * avoid use of exitSuccess which was breaking ghc 6.8/base 3 compatibility (issue #2)
-
-2009/06/13 hledger 0.6
-......................
-
-  * now cabal-installable on unix, mac, and windows, with Haskell Platform
-  * provide experimental platform binaries
-  * parsing: fix a silly failure to open ledger file paths containing ~
-  * parsing: show better errors for unbalanced transaction and missing default year
-  * parsing: allow parentheses and brackets inside account names, as ledger does
-  * parsing: fail on empty account name components, don't just ignore
-  * add: description passed as arguments now affects first transaction only
-  * add: better handling of virtual postings and default amounts
-  * print, register: show virtual accounts bracketed/parenthesised
-  * web: improved web ui supporting full patterns & period expressions
-  * new "stats" command reports some ledger statistics
-  * many dev/doc/deployment infrastructure improvements
-  * move website into darcs repo, update home page
-  * move issue tracker to google code
-
-Release stats:
-
-  * Contributors: Simon Michael
-  * Days since last release: 21
-  * Commits: 94
-  * Lines of non-test code: 2865
-  * Tests: 82
-  * Test coverage: 53% expressions
-  * Known errors: 3 (inconsistent eliding, vty-related failures)
-  * Performance: similar (http://hledger.org/profs/200906131120.bench)
-
-2009/05/23 hledger 0.5.1
-.................................
-
-  * two fixes: really disable vty flag by default, and include ConvertCommand in cabal file
-
-2009/05/23 hledger 0.5
-...............................
-
-  * the vty flag is disabled by default again, to ease installation on windows
-  * use ledger 3 terminology: a ledger contains transactions which contain postings
-  * new "add" command prompts for transactions interactively and adds them to the ledger
-  * new "convert" command transforms bank CSV exports to ledger format, with rule-based cleanup
-  * new "histogram" command shows transaction counts per day or other reporting interval
-  * most commands now work properly with UTF8-encoded text (Sergey Astanin)
-  * invoking as "hours" is now less different: it just uses your timelog, not your ledger
-  * ..quarterly/-Q option summarises by quarter
-  * ..uncleared/-U option looks only at uncleared transactions
-  * be more accurate about checking balanced amounts, don't rely on display precision
-  * enforce balancing for bracketed virtual postings
-  * fix bug in eliding of posting amounts
-  * don't show trailing spaces on amountless postings
-  * parse null input as an empty ledger
-  * don't treat comments as part of transaction descriptions
-  * require some postings in ledger transactions
-  * require a non-empty description in ledger transactions
-  * don't fail when matching an empty pattern, as in "not:"
-  * make the web server handle the null path
-  * code, api and documentation updates
-  * add a contributor agreement/list
-
-Release stats:
-
-  * Contributors: Simon Michael, Sergey Astanin
-  * Days since last release: 51
-  * Commits: 101
-  * Lines of non-test code: 2795
-  * Tests: 76
-  * Known errors: 0
-
-..
-  * Performance:
-                              || hledger-0.4 | hledger-0.5 | ledger
-     =========================++=============+=============+=======
-     -f sample.ledger balance ||        0.01 |        0.01 |   0.06
-     -f 1000.ledger balance   ||        1.33 |        1.46 |   0.53
-     -f 10000.ledger balance  ||       15.28 |       16.35 |   4.67
-
-
-2009/04/03 hledger 0.4
-...............................
-
-  * new "web" command serves reports in a web browser (install with -f happs to build this)
-  * make the vty-based curses ui a cabal build option, which will be ignored on MS windows
-  * drop the ..options-anywhere flag, that is now the default
-  * patterns now use not: and desc: prefixes instead of ^ and ^^
-  * patterns are now case-insensitive, like ledger
-  * !include directives are now relative to the including file (Tim Docker)
-  * "Y2009" default year directives are now supported, allowing m/d dates in ledger
-  * individual transactions now have a cleared status
-  * unbalanced entries now cause a proper warning
-  * balance report now passes all ledger compatibility tests
-  * balance report now shows subtotals by default, like ledger 3
-  * balance report shows the final zero total when -E is used
-  * balance report hides the final total when ..no-total is used
-  * ..depth affects print and register reports (aggregating with a reporting interval, filtering otherwise)
-  * register report sorts transactions by date
-  * register report shows zero-amount transactions when -E is used
-  * provide more convenient timelog querying when invoked as "hours"
-  * multi-day timelog sessions are split at midnight
-  * unterminated timelog sessions are now counted. Accurate time reports at last!
-  * the test command gives better ..verbose output
-  * ..version gives more detailed version numbers including patchlevel for dev builds
-  * new make targets include: ghci, haddocktest, doctest, unittest, view-api-docs
-  * a doctest-style framework for functional/shell tests has been added
-
-Release stats:
-
-  * Contributors: Simon Michael, Tim Docker; thanks to the HAppS, happstack and testpack developers
-  * Days since release: 76
-  * Commits: 144
-  * Lines of non-test code: 2367
-  * Tests: 56
-  * Known errors: 0
-
-..
-  * Performance:
-                                   || hledger-0.3 | hledger-0.4 | ledger-0.3
-     ==============================++=============+=============+===========
-     -f sample.ledger balance      ||        0.02 |        0.01 |       0.07
-     -f sample1000.ledger balance  ||        1.02 |        1.39 |       0.53
-     -f sample10000.ledger balance ||       12.72 |       14.97 |       4.63
-
-
-2009/01/17 hledger 0.3
-...............................
-
-  * count timelog sessions on the day they end, like ledger, for now
-  * when options are repeated, use the last instead of the first
-  * builds with ghc 6.10 as well as 6.8
-  * a simple ui for interactive report browsing: hledger ui
-  * accept smart dates everywhere (YYYYMMDD, Y/M/D, Y, M/D, D, jan, today, last week etc.)
-  * ..period/-p flag accepting period expressions like "in 2008", "weekly from last month"..
-  * -W/-M/-Y convenience flags to summarise register weekly, monthly, yearly
-  * ..depth and -E flags also affect summarised register reports (including depth=0)
-  * ..display/-d flag supporting date predicates (like "d<[DATE]", "d>=[DATE]")
-  * !include directive to include additional ledger files
-  * !account directive to set a default parent account
-  * Added support for reading historical prices from files
-  * timelog and ledger entries can be intermixed in one file
-  * modifier and periodic entries can appear anywhere (but are still ignored)
-  * help and readme improvements
-  * runs much faster than 0.2
-
-Release stats:
-
-  * Contributors: Simon Michael, Nick Ingolia, Tim Docker; thanks to Corey O'Connor & the vty team
-  * Lines of non-test code: 2123
-  * Tests: 58
-  * Known errors: 1
-
-..
-  * Performance:
-     $ bench hledger-0.2 hledger ledger
-                                       || hledger-0.2 | hledger | ledger
-     ==================================++=============+=========+=======
-     -f 2008.ledger -s balance         ||        2.59 |    0.26 |   0.11
-     -f 10000entries.ledger -s balance ||      566.68 |    2.72 |   0.96
-
-
-2008/11/23 hledger 0.2
-...............................
-
-  * fix balance report totals when filtering by account
-  * fix balance report selection of accounts when filtering by account
-  * fix a bug with account name eliding in balance report
-  * if we happen to be showing a not-yet-auto-balanced entry, hide the AUTO marker
-  * fix print command filtering by account
-  * omit transactions with zero amount from register report
-  * Fix bug in parsing of timelogs
-  * rename ..showsubs to ..subtotal, like ledger
-  * drop ..usage flag
-  * don't require quickcheck
-  * priced amounts (eg "10h @ $50") and ..basis/..cost/-B flag to show them with cost basis
-  * easy ..depth option, equivalent to c++ ledger's -d 'l<=N'
-  * smarter y/m/d date parsing for -b and -e
-    (any number of digits, month and day default to 1, separator can be / - or .)
-  * -n flag for balance command
-  * ..empty/-E flag
-  * build a library, as well as the exe
-  * new home page url (http://joyful.com/hledger)
-  * publish html and pdf versions of README
-  * detect display preferences for each commodity like c++ ledger
-  * support amounts with multiple currencies/commodities
-  * support ..real/-R flag
-  * support -C/..cleared flag to filter by entry status (not transaction status)
-  * support virtual and balanced virtual transactions
-  * parse comment lines beginning with a space, as from M-; in emacs ledger-mode
-  * allow any non-whitespace in account names, perhaps avoiding misleading missing amounts errors
-  * clearer error message when we can't balance an entry
-  * when we fail because of more than one missing amount in an entry, show the full entry
-  * document the built-in test runner in ..help
-  * add a ..verbose/-v flag, use it to show more test-running detail
-
-Release stats:
-
-  * Contributors: Simon Michael, Tim Docker
-  * Lines of non-test code: 1350
-  * Tests: 43
-  * Known errors: 0
-
-
-2008/10/15 hledger 0.1
-...............................
-
-Release stats:
-
-  * Contributors: Simon Michael
diff --git a/README.rst b/README.rst
--- a/README.rst
+++ b/README.rst
@@ -8,6 +8,7 @@
 commodities, using standard accounting principles. It is quite limited in
 features, but reliable.  For some, it is a bare-bones, less complex, less
 expensive alternative to Quicken or Microsoft Money.
+hledger is available for free under the GNU General Public License.
 
 hledger aims to help both computer experts and every-day users gain clarity in their finances and time management.
 I use it every day to: 
@@ -17,23 +18,17 @@
 - get accurate numbers for client billing and tax filing
 - track invoices
 
-Here is a demo_ of the web interface.
-
-Here are ready-to-run binaries_ for mac, windows and gnu/linux.
-They are not the `current release`_, sorry about that.
+The manual_ and screenshots_ will explain more. There's also a demo_ of hledger's web interface.
+To get started tracking your own numbers, download_ a copy.
 
-Here is the manual_.
 For support and more technical info, see `development`_ or `email me`_.
+I appreciate feedback on all aspects of hledger. Thanks!
 
-.. (If you're reading this in plain text, see also README2, MANUAL etc., or http://hledger.org)
 
-.. _development:          README2.html
-.. _manual:               MANUAL.html
-.. _demo:                 http://demo.hledger.org
-.. _binaries:             http://hledger.org/binaries/
-.. _hledger for mac:      http://hledger.org/binaries/hledger-0.6-mac-i386.gz
-.. _hledger for windows:  http://hledger.org/binaries/hledger-0.6-win-i386.zip
-.. _32 bit intel:         http://hledger.org/binaries/hledger-0.6.1+9-linux-i386.gz
-.. _64 bit intel:         http://hledger.org/binaries/hledger-0.6-linux-x86_64.gz
-.. _email me:             mailto:simon@joyful.com
-.. _current release:       http://hledger.org/MANUAL.html#installing
+.. _manual:        MANUAL.html
+.. _screenshots:   SCREENSHOTS.html
+.. _demo:          http://demo.hledger.org
+.. _web interface: MANUAL.html#web
+.. _download:      DOWNLOAD.html
+.. _development:   DEVELOPMENT.html
+.. _email me:      mailto:simon@joyful.com?subject=hledger:
diff --git a/README2.rst b/README2.rst
deleted file mode 100644
--- a/README2.rst
+++ /dev/null
@@ -1,101 +0,0 @@
----
-title: hledger development
----
-hledger development
-===================
-
-**Techie intro**
-
-hledger_ is a remix, in haskell_, of John Wiegley's excellent ledger_ accounting tool.
-It reads a plain text journal_ or timelog_ file describing your transactions
-and displays reports via command line, curses or web interfaces.
-
-The hledger project aims to produce:
-
-- a practical, accessible, dependable tool for end users
-- a useful library and toolbox for finance-minded haskell programmers
-- a successful, time-and-money-solvent project within a thriving ecosystem of financial software projects.
-
-hledger is free software by `Simon Michael`_ & `co.`_, released under GNU GPLv3.
-
-**Learn**
- news_, manual_, screenshots_, `some extra tips`_
-
-**Download**
- ``cabal install hledger``, 
- or try these ready-to-run binaries_,
- or see the `installing docs <MANUAL.html#installing>`_
-
-**Develop**
- ``darcs get http://joyful.com/repos/hledger``, 
- `browse the repo`_, 
- `hackage page`_, 
- `hledger-lib api docs`_, 
- `hledger api docs`_, 
- `hledger-lib sourcegraph report`_, 
- `hledger sourcegraph report`_, 
- benchmark_\/profile_\/heap_\/coverage_ reports,
- `developer notes`_
-
-.. raw:: html
-
- <a name="support" />
-
-**Support**
-
-- chat Simon (sm) on the `#ledger`_ irc channel which we share, or `email me`_
-- report problems at `bugs.hledger.org <http://bugs.hledger.org>`_
-- share and test journal snippets at paste . hledger.org
-- .. raw:: html
-
-     <form action="http://groups.google.com/group/hledger/boxsubscribe" >
-       join the hledger mail list at <a href="http://list.hledger.org">list.hledger.org</a>. Your email:
-       <input type=text name=email><input type=submit name="sub" value="Subscribe">
-     </form>
-
-**Related projects**
-
-- John Wiegley's ledger_ inspired hledger, and we try to stay compatible. You can often use both tools on the same journal file.
-- Uwe Hollerbach's umm_ is another haskell tool inspired by h/ledger.
-- Tim Docker's ledger-reports_ uses hledger as a library to generate `html reports`_. 
-- I have a few older bits and pieces `here <http://joyful.com/Ledger>`_.
-
-.. raw:: html
-
- <a href="http://joyful.com/darcsweb/darcsweb.cgi?r=hledger;a=shortlog"><img src=http://joyful.com/repos/hledger/commits.png border=0></a>
- <a href="https://www.google.com/analytics/reporting/?reset=1&id=15489822" accesskey="a"></a>
-
-
-.. _hledger:              README.html
-.. _journal:              http://joyful.com/repos/hledger/data/sample.journal
-.. _timelog:              http://joyful.com/repos/hledger/data/sample.timelog
-.. _command line:         SCREENSHOTS.html#hledger-screen-1
-.. _curses:               SCREENSHOTS.html#sshot
-.. _web interface:        http://demo.hledger.org
-.. _mail list:            http://list.hledger.org
-.. _issue tracker:        http://bugs.hledger.org
-.. _binaries:             http://hledger.org/binaries/
-.. _manual:               MANUAL.html
-.. _news:                 NEWS.html
-.. _screenshots:          SCREENSHOTS.html
-.. _hledger-lib api docs: http://joyful.com/repos/hledger/hledger-lib/dist/doc/html/hledger-lib/index.html
-.. _hledger api docs:     http://joyful.com/repos/hledger/dist/doc/html/hledger/index.html
-.. _hledger-lib sourcegraph report: http://joyful.com/repos/hledger/hledger-lib/SourceGraph/hledger-lib.html
-.. _hledger sourcegraph report: http://joyful.com/repos/hledger/SourceGraph/hledger.html
-.. _developer notes:      http://joyful.com/darcsweb/darcsweb.cgi?r=hledger;a=plainblob;f=/NOTES
-.. _benchmark:            http://hledger.org/profs/latest.bench
-.. _profile:              http://hledger.org/profs/latest.prof
-.. _heap:                 http://hledger.org/profs/latest.ps
-.. _coverage:             http://hledger.org/profs/coverage/hpc_index_fun.html
-.. _browse the repo:      http://joyful.com/darcsweb/darcsweb.cgi?r=hledger
-.. _email me:             mailto:simon@joyful.com
-.. _Simon Michael:        http://joyful.com
-.. _co.:                  http://hledger.org/CONTRIBUTORS.html
-.. _hackage page:         http://hackage.haskell.org/package/hledger
-.. _#ledger:              irc://irc.freenode.net/#ledger
-.. _haskell:              http://haskell.org
-.. _ledger:               http://wiki.github.com/jwiegley/ledger
-.. _umm:                  http://www.korgwal.com/umm/
-.. _ledger-reports:       http://dockerz.net/repos/ledger-reports
-.. _html reports:         http://dockerz.net/software/hledger_report_sample/report.html
-.. _some extra tips:      http://podcastle.org/2009/10/09/pc-miniature-38-accounting-for-dragons/
diff --git a/data/web/static/dhtmlxcombo.js b/data/web/static/dhtmlxcombo.js
new file mode 100644
--- /dev/null
+++ b/data/web/static/dhtmlxcombo.js
@@ -0,0 +1,1359 @@
+//v.2.6 build 100722
+
+/*
+Copyright DHTMLX LTD. http://www.dhtmlx.com
+You allowed to use this component or parts of it under GPL terms
+To use it on other terms or get Professional edition of the component please contact us at sales@dhtmlx.com
+*/
+
+/*_TOPICS_
+@0:Initialization
+@1:Selection control
+@2:Add/delete
+@3:Reserved
+@4:Methods of Option object
+*/
+
+/**
+*  Build combobox from existing select control.
+*
+*
+*  @param   parent      {string} id of existing select control
+*  @param   size      {int } size of control, optional
+*  @return            {object} combobox object
+*  @type   public
+*  @topic   0
+*
+*/
+
+function dhtmlXComboFromSelect(parent,size){
+   if (typeof(parent)=="string")
+      parent=document.getElementById(parent);
+   
+   
+   size=size||parent.getAttribute("width")||(window.getComputedStyle?window.getComputedStyle(parent,null)["width"]:(parent.currentStyle?parent.currentStyle["width"]:0));
+   if ((!size)||(size=="auto"))
+   		size=parent.offsetWidth||100;
+
+   var z=document.createElement("SPAN");
+   
+	
+  
+   parent.parentNode.insertBefore(z,parent);
+   parent.style.display='none';
+
+    var s_type = parent.getAttribute('opt_type');
+	
+   var w= new dhtmlXCombo(z,parent.name,size,s_type,parent.tabIndex);
+   
+   var x=new Array();
+   var sel=0;
+   for (var i=0; i<parent.options.length; i++){
+      if (parent.options[i].selected) sel=i;
+      var label=parent.options[i].innerHTML;
+      var val=parent.options[i].getAttribute("value");
+      if ((typeof(val)=="undefined")||(val===null)) val=label;
+      x[i]={value:val,text:label,img_src:parent.options[i].getAttribute("img_src")};
+   }
+    if(x.length)
+    w.addOption(x);
+   
+   
+	parent.parentNode.removeChild(parent);
+	w.selectOption(sel,null,true);
+	if (parent.onchange) 
+		w.attachEvent("onChange",parent.onchange);
+   		
+   	if(parent.style.direction=="rtl" && w.setRTL) 
+   		w.setRTL(true);
+   	
+   return w;
+}
+
+var dhtmlXCombo_optionTypes = [];
+/**
+*     @desc: build combobox
+*     @param: parent - (string) id of existing object which will be used as container
+*     @param: name - (string) name of combobox - will be used in FORM
+*     @param: width - (int) size of combobox
+*     @param: tabIndex - (int) tab index, optional
+*     @type: public
+*     @topic: 0
+*/
+function dhtmlXCombo(parent,name,width,optionType,tabIndex){
+      if (typeof(parent)=="string")
+         parent=document.getElementById(parent);
+
+      this.dhx_Event();
+      this.optionType = (optionType != window.undefined && dhtmlXCombo_optionTypes[optionType]) ? optionType : 'default';
+      this._optionObject = dhtmlXCombo_optionTypes[this.optionType];
+
+      this._disabled = false;
+	  
+	  if (!window.dhx_glbSelectAr){
+          window.dhx_glbSelectAr=new Array();
+          window.dhx_openedSelect=null;
+          window.dhx_SelectId=1;
+          dhtmlxEvent(document.body,"click",this.closeAll);
+          dhtmlxEvent(document.body,"keydown",function(e){ try { if ((e||event).keyCode==9)  window.dhx_glbSelectAr[0].closeAll(); } catch(e) {} return true; } );
+      }
+		
+     if (parent.tagName=="SELECT")
+         return dhtmlXComboFromSelect(parent);
+      else
+         this._createSelf(parent,name,width,tabIndex);
+      dhx_glbSelectAr.push(this);
+}
+
+/**
+*     @desc: change control size
+*     @param: new_size - (int) new size value
+*     @type: public
+*     @topic: 0
+*/
+   dhtmlXCombo.prototype.setSize = function(new_size){
+      this.DOMlist.style.width=new_size+"px";
+      if (this.DOMlistF) this.DOMlistF.style.width=new_size+"px";
+      this.DOMelem.style.width=new_size+"px";
+      this.DOMelem_input.style.width = Math.max(0,(new_size-19))+'px';
+   }      
+/**
+*     @desc: switch between combobox and auto-filter modes
+*     @param: mode - (boolean) enable filtering mode
+*     @param: url - (string) url for filtering from XML, optional
+*     @param: cache - (boolean) XML caching, optional
+*     @param: autosubload - (boolean) enable auto load additional suggestions on selecting last loaded option
+*     @type: public
+*     @topic: 0
+*/
+   dhtmlXCombo.prototype.enableFilteringMode = function(mode,url,cache,autosubload){
+      this._filter=convertStringToBoolean(mode);
+
+      if (url){
+         this._xml=url;
+         this._autoxml=convertStringToBoolean(autosubload);
+      }
+      if (convertStringToBoolean(cache)) this._xmlCache=[];
+      //this.DOMelem_button.style.display=(this._filter?"none":"");
+   }
+   
+   dhtmlXCombo.prototype.setFilteringParam=function(name,value){
+   		if (!this._prs) this._prs=[];
+   		this._prs.push([name,value]);
+   }
+/**
+*     @desc: disable combobox
+*     @param: mode - (boolean) disable combobox
+*     @type: public
+*     @topic: 1
+*/
+   dhtmlXCombo.prototype.disable = function(mode){
+      var z=convertStringToBoolean(mode);
+      if (this._disabled==z) return;
+      this.DOMelem_input.disabled=z;
+      this._disabled=z;
+   }
+/**
+*     @desc: switch to readonly mode
+*     @param: mode - (boolean) readonly mode
+*     @param: autosearch - (boolean) true by default 
+*     @type: public
+*     @topic: 1
+*/
+   dhtmlXCombo.prototype.readonly = function(mode,autosearch){
+        this.DOMelem_input.readOnly=mode ? true : false;
+		if(autosearch===false || mode===false){
+			this.DOMelem.onkeyup=function(ev){ }
+		} else {
+			var that = this;
+			this.DOMelem.onkeyup=function(ev){ 
+				ev=ev||window.event; 
+				if (ev.keyCode!=9) ev.cancelBubble=true;
+				if((ev.keyCode >= 48 && ev.keyCode <= 57)||(ev.keyCode >= 65 && ev.keyCode <= 90)){
+					for(var i=0; i<that.optionsArr.length; i++){
+						var text = that.optionsArr[i].text;
+						if(text.toString().toUpperCase().indexOf(String.fromCharCode(ev.keyCode)) == 0){
+								that.selectOption(i);
+								break;
+						}
+					}
+					ev.cancelBubble=true;
+				}
+			}
+		}
+	}	
+   
+/**
+*     @desc: get Option by value
+*     @param: value - (string) value of option in question
+*     @type: public
+*     @return: option object
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.getOption = function(value)
+   {
+      for(var i=0; i<this.optionsArr.length; i++)
+         if(this.optionsArr[i].value==value)
+            return this.optionsArr[i];
+      return null;
+   }
+/**
+*     @desc: get Option by label
+*     @param: label - (string) label of option in question
+*     @type: public
+*     @return: option object
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.getOptionByLabel = function(value)
+   {
+	  //value=value.replace(/&/g,"&amp;")
+      for(var i=0; i<this.optionsArr.length; i++)
+         if(this.optionsArr[i].text==value || this.optionsArr[i]._ctext==value)
+            return this.optionsArr[i];
+      return null;
+   }
+/**
+*     @desc: get Option by index
+*     @param: ind - (int) index of option in question
+*     @type: public
+*     @return: option object
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.getOptionByIndex = function(ind){
+      return this.optionsArr[ind];
+   }
+/**
+*     @desc: clear all options from combobox
+*     @type: public
+*     @param: value - (bool) clear current value as well
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.clearAll = function(all)
+   {
+	  if (all) this.setComboText("");   	  
+      this.optionsArr=new Array();
+      this.redrawOptions();
+      if (all) this._confirmSelection();
+   }
+/**
+*     @desc: delete option by value
+*     @param: value - (string) value of option in question
+*     @type: public
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.deleteOption = function(value)
+   {
+        var ind=this.getIndexByValue(value);
+      if(ind<0) return;                            
+      if (this.optionsArr[ind]==this._selOption) this._selOption=null;
+      this.optionsArr.splice(ind, 1);
+      this.redrawOptions();
+   }
+
+/**
+*     @desc: enable/disable immideatly rendering after changes in combobox
+*     @param: mode - (boolean) enable/disable
+*     @type: public
+*     @topic: 1
+*/
+   dhtmlXCombo.prototype.render=function(mode){
+      this._skiprender=(!convertStringToBoolean(mode));
+      this.redrawOptions();
+   }
+
+/**
+*     @desc: update option in combobox
+*     @param: oldvalue - (string) index of option in question
+*     @param: avalue - (variable)
+*     @type: public
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.updateOption = function(oldvalue, avalue, atext, acss)
+   {
+      var dOpt=this.getOption(oldvalue);
+      if (typeof(avalue)!="object") avalue={text:atext,value:avalue,css:acss};
+      dOpt.setValue(avalue);
+        this.redrawOptions();
+   }
+/**
+*     @desc: add new option
+*     @param: value - (variable) - different input for different kinds of options - please refer to examples
+*     @type: public
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.addOption = function(options)
+   {
+      if (!arguments[0].length || typeof(arguments[0])!="object")
+         args = [arguments];
+      else
+         args = options;
+
+      this.render(false);
+        for (var i=0; i<args.length; i++) {
+            var attr = args[i];
+         if (attr.length){
+               attr.value = attr[0]||"";
+               attr.text = attr[1]||"";
+               attr.css = attr[2]||"";
+         }
+            this._addOption(attr);
+        }
+      this.render(true);
+   }
+
+   dhtmlXCombo.prototype._addOption = function(attr)
+   {
+         dOpt = new this._optionObject();
+         this.optionsArr.push(dOpt);
+         dOpt.setValue.apply(dOpt,[attr]);
+         this.redrawOptions();
+   }
+
+
+/**
+*     @desc: return index of item by value
+*     @param: value - (string) value of option in question
+*     @type: public
+*     @return: option index
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.getIndexByValue = function(val){
+      for(var i=0; i<this.optionsArr.length; i++)
+         if(this.optionsArr[i].value == val) return i;
+      return -1;
+   }
+
+/**
+*     @desc: get value of selected item
+*     @type: public
+*     @return: option value
+*     @topic: 2 
+*/
+   dhtmlXCombo.prototype.getSelectedValue = function(){
+      return (this._selOption?this._selOption.value:null);
+   }
+/**
+*     @desc: get current text in combobox
+*     @type: public
+*     @return: combobox text
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.getComboText = function(){
+      return this.DOMelem_input.value;
+   }
+/**
+*     @desc: set text in covmbobox
+*     @param: text - (string) new text label
+*     @type: public
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.setComboText = function(text){
+      this.DOMelem_input.value=text;
+   }
+   
+/**
+*     @desc: set text in covmbobox
+*     @param: text - (string) new text label
+*     @type: public
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.setComboValue = function(text){
+      this.setComboText(text);
+	  for(var i=0; i<this.optionsArr.length; i++)
+         if (this.optionsArr[i].data()[0]==text)
+         return this.selectOption(i,null,true);      
+      this.DOMelem_hidden_input.value=text;
+   }
+
+/**
+*     @desc: get value which will be sent with form
+*     @type: public
+*     @return: combobox value
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.getActualValue = function(){
+      return this.DOMelem_hidden_input.value;
+   }
+/**
+*     @desc: get text of selected option
+*     @type: public
+*     @return: text of option
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.getSelectedText = function(){
+      return (this._selOption?this._selOption.text:"");
+   }
+/**
+*     @desc: get index of selected option
+*     @type: public
+*     @return: option index
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.getSelectedIndex = function(){
+      for(var i=0; i<this.optionsArr.length; i++)
+         if(this.optionsArr[i] == this._selOption) return i;
+      return -1;
+   }
+/**
+*     @desc: set name used while form submit
+*     @param: name - (string) new combobox name
+*     @type: public
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.setName = function(name){  
+        this.DOMelem_hidden_input.name = name;
+        this.DOMelem_hidden_input2 = name.replace(/(\]?)$/, "_new_value$1");
+
+        this.name = name;
+   }
+/**
+*     @desc: show combox ( reversion to hide command )
+*     @param: mode - (boolean) enable/disable
+*     @type: public
+*     @topic: 1
+*/
+   dhtmlXCombo.prototype.show = function(mode){
+      if (convertStringToBoolean(mode))
+         this.DOMelem.style.display = "";
+      else
+         this.DOMelem.style.display = "none";
+   }
+
+/**
+*     @desc: destroy object and any related HTML elements
+*     @type: public
+*     @topic: 0
+*/
+   dhtmlXCombo.prototype.destructor = function()
+   {
+      this.DOMParent.removeChild(this.DOMelem);
+      this.DOMlist.parentNode.removeChild(this.DOMlist);
+      var s=dhx_glbSelectAr;
+      this.DOMParent=this.DOMlist=this.DOMelem=0;
+      this.DOMlist.combo=this.DOMelem.combo=0;
+      for(var i=0; i<s.length; i++)
+      {
+         if(s[i] == this)
+         {
+            s[i] = null;
+            s.splice(i,1);
+            return;
+         }
+      }
+   }
+
+/**
+*     @desc: create self HTML
+*     @type: private
+*     @topic: 0
+*/
+      dhtmlXCombo.prototype._createSelf = function(selParent, name, width, tab)
+      {
+         if (width.toString().indexOf("%")!=-1){ 
+            var self = this;
+            var resWidht=parseInt(width)/100;
+            window.setInterval(function(){ 
+               if (!selParent.parentNode) return;
+               var ts=selParent.parentNode.offsetWidth*resWidht-2;
+               if (ts<0) return;
+               if (ts==self._lastTs) return;
+               self.setSize(self._lastTs=ts);},500);
+            var width=parseInt(selParent.offsetWidth); //mm
+         }
+
+         var width=parseInt(width||100); //mm
+         this.ListPosition = "Bottom"; //set optionlist positioning
+         this.DOMParent = selParent;
+         this._inID = null;
+         this.name = name;
+
+         this._selOption = null; //selected option object pointer
+         this.optionsArr = Array();
+
+            var opt = new this._optionObject();
+            opt.DrawHeader(this,name, width,tab);
+         //HTML select part 2 - options list DIV element
+         this.DOMlist = document.createElement("DIV");
+         this.DOMlist.className = 'dhx_combo_list '+(dhtmlx.skin?dhtmlx.skin+"_list":"");
+         this.DOMlist.style.width=width-(_isIE?0:0)+"px";
+		 if (_isOpera || _isKHTML ) 
+		 		this.DOMlist.style.overflow="auto";      
+         this.DOMlist.style.display = "none";
+         document.body.insertBefore(this.DOMlist,document.body.firstChild);         
+         if (_isIE)    {
+            this.DOMlistF = document.createElement("IFRAME");
+            this.DOMlistF.style.border="0px";
+            this.DOMlistF.className = 'dhx_combo_list';
+            this.DOMlistF.style.width=width-(_isIE?0:0)+"px";
+            this.DOMlistF.style.display = "none";
+            this.DOMlistF.src="javascript:false;"; 
+            document.body.insertBefore(this.DOMlistF,document.body.firstChild);
+            }
+
+
+
+         this.DOMlist.combo=this.DOMelem.combo=this;
+
+         this.DOMelem_input.onkeydown = this._onKey;
+          this.DOMelem_input.onkeypress = this._onKeyF;
+         this.DOMelem_input.onblur = this._onBlur;
+         this.DOMelem.onclick = this._toggleSelect;
+         this.DOMlist.onclick = this._selectOption;
+         this.DOMlist.onmousedown = function(){
+         	this._skipBlur=true;
+     	 }
+         
+         this.DOMlist.onkeydown = function(e){
+         	this.combo.DOMelem_input.focus();
+         	(e||event).cancelBubble=true;
+         	this.combo.DOMelem_input.onkeydown(e)
+     	 }
+		  this.DOMlist.onmouseover = this._listOver;
+     	 
+      }
+
+      dhtmlXCombo.prototype._listOver = function(e)
+      { 
+         e = e||event;
+         e.cancelBubble = true;
+         var node = (_isIE?event.srcElement:e.target);
+         var that = this.combo;
+         if ( node.parentNode == that.DOMlist ) {
+            if(that._selOption) that._selOption.deselect(); 
+			if(that._tempSel) that._tempSel.deselect();
+           
+		    var i=0;
+            for (i; i<that.DOMlist.childNodes.length; i++) {
+               if (that.DOMlist.childNodes[i]==node) break;
+            }
+            var z=that.optionsArr[i];
+           that._tempSel=z;
+           that._tempSel.select();
+        	  
+			if ((that._autoxml)&&((i+1)==that._lastLength)){
+				that._fetchOptions(i+1,that._lasttext||""); 
+            }           
+         }
+
+      }
+
+/**
+*     @desc: place option list in necessary place on screen
+*     @type: private
+*     @topic: 0
+*/
+      dhtmlXCombo.prototype._positList = function()
+      {
+	  	//if(this.ListAutoPosit) this.enableOptionAutoPositioning(true); //mm auto posit
+         var pos=this.getPosition(this.DOMelem);
+         if(this.ListPosition == 'Bottom'){
+            this.DOMlist.style.top = pos[1]+this.DOMelem.offsetHeight-1+"px";
+            this.DOMlist.style.left = pos[0]+"px";
+         }
+         else if(this.ListPosition == 'Top'){ //mm
+				this.DOMlist.style.top = pos[1] - this.DOMlist.offsetHeight+"px";
+            	
+				this.DOMlist.style.left = pos[0]+"px"; //mm
+          }
+		  else{
+			this.DOMlist.style.top = pos[1]+"px";
+            this.DOMlist.style.left = pos[0]+this.DOMelem.offsetWidth+"px";
+         }
+     
+      }
+	  
+	dhtmlXCombo.prototype.getPosition = function(oNode,pNode){
+		if (_isChrome){ 
+			if(!pNode)
+			var pNode = document.body
+			
+			var oCurrentNode=oNode;
+			var iLeft=0;
+			var iTop=0;
+			while ((oCurrentNode)&&(oCurrentNode!=pNode)){
+			iLeft+=oCurrentNode.offsetLeft-oCurrentNode.scrollLeft;
+			iTop+=oCurrentNode.offsetTop-oCurrentNode.scrollTop;
+			oCurrentNode=oCurrentNode.offsetParent;
+			}
+			if (pNode == document.body ){
+			if (_isIE && _isIE<8){
+			
+			if (document.documentElement.scrollTop)
+			iTop+=document.documentElement.scrollTop;
+			if (document.documentElement.scrollLeft)
+			iLeft+=document.documentElement.scrollLeft;
+			}
+			else
+			if (!_isFF){
+			iLeft+=document.body.offsetLeft;
+			iTop+=document.body.offsetTop;
+			}
+			}
+			return new Array(iLeft,iTop);
+		}
+		var pos = getOffset(oNode);
+        return [pos.left, pos.top];
+        
+	}
+/**
+*     @desc: correct current selection ( move it to first visible option )
+*     @type: private
+*     @topic: 2
+*/
+      dhtmlXCombo.prototype._correctSelection = function(){
+		 if (this.getComboText()!="")
+         for (var i=0; i<this.optionsArr.length; i++)
+            if (!this.optionsArr[i].isHidden()){
+               return this.selectOption(i,true,false);
+           }
+         this.unSelectOption();
+      }
+/**
+*     @desc: select next option in combobox
+*     @param: step - (int) step size
+*     @type: private
+*     @topic: 2
+*/
+      dhtmlXCombo.prototype.selectNext = function(step){
+         var z=this.getSelectedIndex()+step;
+         while (this.optionsArr[z]){
+            if (!this.optionsArr[z].isHidden())
+               return this.selectOption(z,false,false);
+            z+=step;
+         }
+      }
+/**
+*     @desc: on keypressed handler
+*     @type: private
+*     @topic: 0
+*/
+      dhtmlXCombo.prototype._onKeyF = function(e){
+         var that=this.parentNode.combo;
+         var ev=e||event;
+         ev.cancelBubble=true;
+         if (ev.keyCode=="13" || ev.keyCode=="9" ){
+         	that._confirmSelection();
+         	that.closeAll();
+     	} else
+        if (ev.keyCode=="27" ){
+         	that._resetSelection();
+         	that.closeAll();
+     	} else that._activeMode=true;
+         if (ev.keyCode=="13" || ev.keyCode=="27" ){ //enter
+            that.callEvent("onKeyPressed",[ev.keyCode])
+            return false;
+         }
+         return true;
+      }
+/**
+*     @desc: on keyup handler
+*     @type: private
+*     @topic: 0
+*/
+      dhtmlXCombo.prototype._onKey = function(e){
+         var that=this.parentNode.combo;
+         (e||event).cancelBubble=true;
+         var ev=(e||event).keyCode;
+         if (ev>15 && ev<19) return true; //shift,alt,ctrl
+        if (ev==27) return;
+        if ((that.DOMlist.style.display!="block")&&(ev!="13")&&(ev!="9")&&((!that._filter)||(that._filterAny)))
+            that.DOMelem.onclick(e||event);
+       
+		if ((ev!="13")&&(ev!="9")){
+         	window.setTimeout(function(){ that._onKeyB(ev); },1);
+         	if (ev=="40" || ev=="38")
+         		return false;
+     	}
+         else if (ev==9){
+         	that.closeAll();
+         	(e||event).cancelBubble=false;
+         }
+      }
+      dhtmlXCombo.prototype._onKeyB = function(ev)
+      {
+         if (ev=="40"){  //down
+            var z=this.selectNext(1);
+         } else if (ev=="38"){ //up
+            this.selectNext(-1);
+         } else{
+            this.callEvent("onKeyPressed",[ev])
+            if (this._filter) return this.filterSelf((ev==8)||(ev==46));
+            for(var i=0; i<this.optionsArr.length; i++)
+               if (this.optionsArr[i].data()[1]==this.DOMelem_input.value){
+//                  ev.cancelBubble=true;
+                  this.selectOption(i,false,false);
+                  return false;
+                  }
+            this.unSelectOption();
+         }
+         return true;
+      }
+
+
+/**
+*     @desc: on data change handler
+*     @type: private
+*     @topic: 0
+*/
+      dhtmlXCombo.prototype._onBlur = function()
+      {
+          var self = this.parentNode._self;
+          window.setTimeout(function(){
+          	if (self.DOMlist._skipBlur) return !(self.DOMlist._skipBlur=false);
+          	self._confirmSelection();        
+          	self.callEvent("onBlur",[]);
+          },100)
+          
+      }
+/**
+*     @desc: redraw combobox options
+*     @type: private
+*     @topic: 2
+*/
+      dhtmlXCombo.prototype.redrawOptions = function(){
+         if (this._skiprender) return;
+         for(var i=this.DOMlist.childNodes.length-1; i>=0; i--)
+            this.DOMlist.removeChild(this.DOMlist.childNodes[i]);
+         for(var i=0; i<this.optionsArr.length; i++)
+            this.DOMlist.appendChild(this.optionsArr[i].render());
+			
+		
+		
+						
+
+      }
+/**
+*     @desc: load list of options from XML
+*     @param: url - (string) xml url
+*     @type: public
+*     @topic: 0
+*/
+      dhtmlXCombo.prototype.loadXML = function(url,afterCall){
+         this._load=true;
+         this.callEvent("onXLS",[]);
+		 if (this._prs)
+           	for (var i=0; i<this._prs.length; i++)
+           		url+=[getUrlSymbol(url),escape(this._prs[i][0]),"=",escape(this._prs[i][1])].join("");         
+         if ((this._xmlCache)&&(this._xmlCache[url])){
+            this._fillFromXML(this,null,null,null,this._xmlCache[url]);
+            if (afterCall) afterCall();
+        }
+         else{
+            var xml=(new dtmlXMLLoaderObject(this._fillFromXML,this,true,true));
+            if (afterCall) xml.waitCall=afterCall;
+            xml._cPath=url;
+            xml.loadXML(url);
+         }
+      }
+
+/**
+*     @desc: load list of options from XML string
+*     @param: astring - (string) xml string
+*     @type: public
+*     @topic: 0
+*/
+      dhtmlXCombo.prototype.loadXMLString = function(astring){
+         var xml=(new dtmlXMLLoaderObject(this._fillFromXML,this,true,true));
+         xml.loadXMLString(astring);
+      }
+
+/**
+*     @desc: on XML load handler
+*     @type: private
+*     @topic: 0
+*/
+      dhtmlXCombo.prototype._fillFromXML = function(obj,b,c,d,xml){
+         if (obj._xmlCache) obj._xmlCache[xml._cPath]=xml;
+
+         //check that XML is correct 
+         var toptag=xml.getXMLTopNode("complete");
+         if (toptag.tagName!="complete") return;
+         var top=xml.doXPath("//complete");
+         var options=xml.doXPath("//option");
+         
+         var add = false;
+         
+         obj.render(false);
+         if ((!top[0])||(!top[0].getAttribute("add"))){
+            obj.clearAll();
+            obj._lastLength=options.length;
+	         if (obj._xml){
+	         if ((!options) || (!options.length)) 
+	         	obj.closeAll();
+	         else {
+	         		if (obj._activeMode){
+	         			obj._positList();
+	        			obj.DOMlist.style.display="block";
+	         			if (_isIE) obj._IEFix(true);
+         			}
+	     	 }}            
+         } else {
+            obj._lastLength+=options.length||Infinity;
+	        add = true;
+        }
+
+         for (var i=0; i<options.length; i++) {
+            var attr = new Object();
+            attr.text = options[i].firstChild?options[i].firstChild.nodeValue:"";
+            for (var j=0; j<options[i].attributes.length; j++) {
+               var a = options[i].attributes[j];
+               if (a)
+                  attr[a.nodeName] = a.nodeValue;
+            }
+            obj._addOption(attr);
+         }
+         obj.render(add!=true || (!!options.length));
+         	
+         if ((obj._load)&&(obj._load!==true))
+            obj.loadXML(obj._load);
+         else{
+            obj._load=false;
+             if ((!obj._lkmode)&&(obj._filter))
+               obj._correctSelection();
+            }
+
+         var selected=xml.doXPath("//option[@selected]");
+         if (selected.length)
+         	obj.selectOption(obj.getIndexByValue(selected[0].getAttribute("value")),false,true);
+
+         obj.callEvent("onXLE",[]);
+
+      }
+/**
+*     @desc: deselect option
+*     @type: public
+*     @topic: 1
+*/
+      dhtmlXCombo.prototype.unSelectOption = function(){
+         if (this._selOption) this._selOption.deselect();
+         if(this._tempSel) this._tempSel.deselect();
+         this._tempSel=this._selOption=null;
+      }
+	  
+/**
+*     @desc: confirms combo value (recommended to be called on form submit)
+*     @type: public
+*     @topic: 1
+*/
+	 dhtmlXCombo.prototype.confirmValue = function(){
+         this._confirmSelection();
+	 }
+	  
+	  
+      dhtmlXCombo.prototype._confirmSelection = function(data,status){
+      	if(arguments.length==0){
+      	    var z=this.getOptionByLabel(this.DOMelem_input.value);
+          	data = z?z.value:this.DOMelem_input.value;
+          	status = (z==null);
+          	if (data==this.getActualValue()) return;
+      	}
+          	
+      	this.DOMelem_hidden_input.value=data;
+        this.DOMelem_hidden_input2.value = (status?"true":"false");
+      	this.callEvent("onChange",[]);
+      	this._activeMode=false;
+  	  }
+      dhtmlXCombo.prototype._resetSelection = function(data,status){
+      		var z=this.getOption(this.DOMelem_hidden_input.value);
+      		this.setComboValue(z?z.data()[0]:this.DOMelem_hidden_input.value)
+      		this.setComboText(z?z.data()[1]:this.DOMelem_hidden_input.value)
+  	  }  	  
+  	  
+/**
+*     @desc: select option
+*     @param: ind - (int) index of option in question
+*     @param: filter - (boolean) enable autocomplit range, optional
+*     @param: conf - (boolean) true for real selection, false for pre-selection
+*     @type: public
+*     @topic: 1
+*/
+      dhtmlXCombo.prototype.selectOption = function(ind,filter,conf){
+      	 if (arguments.length<3) conf=true;
+         this.unSelectOption();
+         var z=this.optionsArr[ind];
+         if (!z)  return;
+         this._selOption=z;
+         this._selOption.select();
+
+         var corr=this._selOption.content.offsetTop+this._selOption.content.offsetHeight-this.DOMlist.scrollTop-this.DOMlist.offsetHeight;
+         if (corr>0) this.DOMlist.scrollTop+=corr;
+            corr=this.DOMlist.scrollTop-this._selOption.content.offsetTop;
+         if (corr>0) this.DOMlist.scrollTop-=corr;
+         var data=this._selOption.data();
+
+	 	 if (conf){
+	 	 	this.setComboText(data[1]);
+	 	 	this._confirmSelection(data[0],false);
+		 
+         	if ((this._autoxml)&&((ind+1)==this._lastLength))
+            	this._fetchOptions(ind+1,this._lasttext||"");
+		}
+
+         if (filter){
+            var text=this.getComboText();
+            if (text!=data[1]){
+               this.setComboText(data[1]);
+               dhtmlXRange(this.DOMelem_input,text.length+1,data[1].length);
+            }
+         }
+         else
+            this.setComboText(data[1]);
+         this._selOption.RedrawHeader(this);
+
+         /*Event*/
+         this.callEvent("onSelectionChange",[]);
+      }
+/**
+*     @desc: option on select handler
+*     @type: private
+*     @topic: 2
+*/
+      dhtmlXCombo.prototype._selectOption = function(e)
+      {
+         (e||event).cancelBubble = true;
+         var node=(_isIE?event.srcElement:e.target);
+         var that=this.combo;
+         while (!node._self) {
+            node = node.parentNode;
+            if (!node)
+               return;
+         }
+
+         var i=0;
+         for (i; i<that.DOMlist.childNodes.length; i++) {
+            if (that.DOMlist.childNodes[i]==node) break;
+         }
+         that.selectOption(i,false,true);
+         that.closeAll();
+         that.callEvent("onBlur",[])
+         that._activeMode=false;
+      }
+/**
+*     @desc: open list of options 
+*     @type: public
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype.openSelect = function(){ 
+   	
+	
+    
+	  if (this._disabled) return;
+      this.closeAll();
+      this._positList();
+      this.DOMlist.style.display="block";
+      this.callEvent("onOpen",[]);
+	  if(this._tempSel) this._tempSel.deselect();
+	  if(this._selOption) this._selOption.select();
+	  if(this._selOption){
+	 	var corr=this._selOption.content.offsetTop+this._selOption.content.offsetHeight-this.DOMlist.scrollTop-this.DOMlist.offsetHeight;
+         if (corr>0) this.DOMlist.scrollTop+=corr;
+            corr=this.DOMlist.scrollTop-this._selOption.content.offsetTop;
+         if (corr>0) this.DOMlist.scrollTop-=corr;
+	  }
+	  /* if (this.autoOptionSize){
+        	var x=this.DOMlist.offsetWidth; 
+			
+        	for ( var i=0; i<this.optionsArr.length; i++){
+				if(i==0) alert("this.DOMlist.childNodes[i].scrollWidth ="+ this.DOMlist.childNodes[i].scrollWidth + "> x= "+ x);
+        		if (this.DOMlist.childNodes[i].scrollWidth > x)
+        			x=this.DOMlist.childNodes[i].scrollWidth;
+			}
+        			
+			this.DOMlist.style.width=x+"px";
+		}*/
+		
+		      
+      if (_isIE) this._IEFix(true);
+      this.DOMelem_input.focus();
+	
+      if (this._filter) this.filterSelf();
+   }
+/**
+*     @desc: open(close) list
+*     @type: private
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype._toggleSelect = function(e)
+   {
+      var that=this.combo;
+      if ( that.DOMlist.style.display == "block" ) {
+      that.closeAll();
+      } else {
+         that.openSelect();
+      }
+      (e||event).cancelBubble = true;
+   }
+
+    dhtmlXCombo.prototype._fetchOptions=function(ind,text){
+         if (text=="") { this.closeAll();  return this.clearAll();   }
+         var url=this._xml+((this._xml.indexOf("?")!=-1)?"&":"?")+"pos="+ind+"&mask="+encodeURIComponent(text);
+         this._lasttext=text;
+         if (this._load) this._load=url;
+         else {
+			if (!this.callEvent("onDynXLS",[text,ind])) return;
+			this.loadXML(url);
+     	 }
+    }
+/**
+*     @desc: filter list of options
+*     @type: private
+*     @topic: 2
+*/
+    dhtmlXCombo.prototype.filterSelf = function(mode)
+   {
+      var text=this.getComboText();
+      if (this._xml){
+         this._lkmode=mode;
+         this._fetchOptions(0,text);
+      }
+      try{ var filter=new RegExp("^"+text,"i"); } catch (e){ var filter=new RegExp("^"+text.replace(/([\[\]\{\}\(\)\+\*\\])/g,"\\$1")); }
+      this.filterAny=false;
+      for(var i=0; i<this.optionsArr.length; i++){
+      	 var z=filter.test(this.optionsArr[i].text);
+      	 this.filterAny|=z;
+         this.optionsArr[i].hide(!z);
+      }
+      if (!this.filterAny) {
+		  this.closeAll();
+		  this._activeMode=true;
+	  }
+      else {
+      	  if (this.DOMlist.style.display!="block")
+      	   		this.openSelect();
+	      if (_isIE) this._IEFix(true);
+  		}
+         
+        if (!mode)
+         this._correctSelection();   
+        else this.unSelectOption();
+   }
+ 
+ 
+
+
+/**
+*     @desc: set hidden iframe for IE
+*     @type: private
+*     @topic: 2
+*/
+   dhtmlXCombo.prototype._IEFix = function(mode){
+      this.DOMlistF.style.display=(mode?"block":"none");
+        this.DOMlistF.style.top=this.DOMlist.style.top;
+        this.DOMlistF.style.left=this.DOMlist.style.left;
+   }
+/**
+*     @desc: close opened combobox list
+*     @type: public
+*     @topic: 1
+*/
+   dhtmlXCombo.prototype.closeAll = function()
+   {
+      if(window.dhx_glbSelectAr)
+         for (var i=0; i<dhx_glbSelectAr.length; i++){
+            if (dhx_glbSelectAr[i].DOMlist.style.display=="block") {
+               dhx_glbSelectAr[i].DOMlist.style.display = "none";
+               if (_isIE) dhx_glbSelectAr[i]._IEFix(false);
+            }
+            dhx_glbSelectAr[i]._activeMode=false;
+        }
+   }
+ 
+		
+/**
+*     @desc: create selection range in input control
+*     @param: InputId - (string) id of input ( object can be used as well )
+*     @param: Start - (int) start selection position
+*     @param: End - (int) end selection position
+*     @type: public
+*     @topic: 0
+*/
+function dhtmlXRange(InputId, Start, End)
+{
+   var Input = typeof(InputId)=='object' ? InputId : document.getElementById(InputId);
+   try{    Input.focus();   } catch(e){};
+   var Length = Input.value.length;
+   Start--;
+   if (Start < 0 || Start > End || Start > Length)
+      Start = 0;
+   if (End > Length)
+      End = Length;
+   if (Start==End) return;
+   if (Input.setSelectionRange) {
+      Input.setSelectionRange(Start, End);
+   } else if (Input.createTextRange) {
+      var range = Input.createTextRange();
+      range.moveStart('character', Start);
+      range.moveEnd('character', End-Length);
+	  try{
+         range.select();
+	  }
+	  catch(e){
+	  }
+   }
+}
+/**
+*     @desc: combobox option object constructor
+*     @type: public
+*     @topic: 0
+*/
+      dhtmlXCombo_defaultOption = function(){
+         this.init();
+      }
+/**
+*     @desc: option initialization function
+*     @type: private
+*     @topic: 4
+*/
+      dhtmlXCombo_defaultOption.prototype.init = function(){
+         this.value = null;
+         this.text = "";
+         this.selected = false;
+         this.css = "";
+      }
+/**
+*     @desc: mark option as selected
+*     @type: public
+*     @topic: 4
+*/
+      dhtmlXCombo_defaultOption.prototype.select = function(){
+         if (this.content){
+            this.content.className="dhx_selected_option"+(dhtmlx.skin?" combo_"+dhtmlx.skin+"_sel":"");
+		}
+      }
+/**
+*     @desc: hide option
+*     @param: mode - (boolean)
+*     @type: public
+*     @topic: 4
+*/
+      dhtmlXCombo_defaultOption.prototype.hide = function(mode){
+         this.render().style.display=mode?"none":"";
+      }
+/**
+*     @desc: return hide state of option
+*     @type: public
+*     @return: hide state of option
+*     @topic: 4
+*/
+      dhtmlXCombo_defaultOption.prototype.isHidden = function(){
+         return (this.render().style.display=="none");
+      }
+/**
+*     @desc: mark option as not selected
+*     @type: public
+*     @topic: 4
+*/
+      dhtmlXCombo_defaultOption.prototype.deselect = function(){
+         if (this.content) this.render();
+            this.content.className="";
+      }
+/**
+*     @desc: set value of option
+*     @param: value - (string) value
+*     @param: text - (string) text
+*     @param: css - (string) css style string
+*     @type: public
+*     @topic: 4
+*/
+dhtmlXCombo_defaultOption.prototype.setValue = function(attr){
+    this.value = attr.value||"";
+    this.text = attr.text||"";
+    this.css = attr.css||"";
+   this.content=null;
+}
+
+				
+/**
+*     @desc: render option
+*     @type: private
+*     @topic: 4
+*/
+      dhtmlXCombo_defaultOption.prototype.render = function(){
+         if (!this.content){
+            this.content=document.createElement("DIV");
+        this.content._self = this;
+
+            this.content.style.cssText='width:100%; overflow:hidden;'+this.css;
+            if (_isOpera || _isKHTML ) this.content.style.padding="2px 0px 2px 0px";
+            this.content.innerHTML=this.text;
+            this._ctext=_isIE?this.content.innerText:this.content.textContent;
+         }
+         return this.content;
+      }
+/**
+*     @desc: return option data
+*     @type: public
+*     @return: array of data related to option
+*     @topic: 4
+*/
+      dhtmlXCombo_defaultOption.prototype.data = function(){
+         if (this.content)
+            return [this.value,this._ctext ? this._ctext : this.text];
+      }
+
+dhtmlXCombo_defaultOption.prototype.DrawHeader = function(self, name, width, tab)
+{
+    var z=document.createElement("DIV");
+    z.style.width = width+"px";
+    z.className = 'dhx_combo_box '+(dhtmlx.skin||"");
+    z._self = self;
+	self.DOMelem = z;
+   	this._DrawHeaderInput(self, name, width,tab);
+	this._DrawHeaderButton(self, name, width);
+    self.DOMParent.appendChild(self.DOMelem);
+}
+
+dhtmlXCombo_defaultOption.prototype._DrawHeaderInput = function(self, name, width,tab)
+{
+
+	var z=document.createElement('input');
+	z.setAttribute("autocomplete","off"); 
+	z.type = 'text';
+	z.className = 'dhx_combo_input';
+
+   if (tab) z.tabIndex=tab;
+   z.style.width = width-19-(document.compatMode=="BackCompat"?0:3)+'px';
+   self.DOMelem.appendChild(z);
+   self.DOMelem_input = z;
+
+   z = document.createElement('input');
+   z.type = 'hidden';
+   z.name = name;
+   self.DOMelem.appendChild(z);
+   self.DOMelem_hidden_input = z;
+
+   z = document.createElement('input');
+   z.type = 'hidden';
+   z.name = (name||"").replace(/(\]?)$/, "_new_value$1");
+   z.value="true";
+   self.DOMelem.appendChild(z);
+   self.DOMelem_hidden_input2 = z;
+}
+
+dhtmlXCombo_defaultOption.prototype._DrawHeaderButton = function(self, name, width)
+{
+   var z=document.createElement('img');
+   z.className = 'dhx_combo_img';
+  if(dhtmlx.image_path) dhx_globalImgPath = dhtmlx.image_path;
+   z.src = (window.dhx_globalImgPath?dhx_globalImgPath:"")+'combo_select'+(dhtmlx.skin?"_"+dhtmlx.skin:"")+'.gif';
+   self.DOMelem.appendChild(z);
+   self.DOMelem_button=z;
+}
+
+dhtmlXCombo_defaultOption.prototype.RedrawHeader = function(self)
+{
+}
+
+
+dhtmlXCombo_optionTypes['default'] = dhtmlXCombo_defaultOption;
+
+dhtmlXCombo.prototype.dhx_Event=function()
+{
+   this.dhx_SeverCatcherPath="";
+
+   this.attachEvent = function(original, catcher, CallObj)
+   {
+      CallObj = CallObj||this;
+      original = 'ev_'+original;
+       if ( ( !this[original] ) || ( !this[original].addEvent ) ) {
+           var z = new this.eventCatcher(CallObj);
+           z.addEvent( this[original] );
+           this[original] = z;
+       }
+       return ( original + ':' + this[original].addEvent(catcher) );   //return ID (event name & event ID)
+   }
+   this.callEvent=function(name,arg0){
+         if (this["ev_"+name]) return this["ev_"+name].apply(this,arg0);
+       return true;
+   }
+   this.checkEvent=function(name){
+         if (this["ev_"+name]) return true;
+       return false;
+   }
+
+   this.eventCatcher = function(obj)
+   {
+       var dhx_catch = new Array();
+       var m_obj = obj;
+       var func_server = function(catcher,rpc)
+                         {
+                           catcher = catcher.split(":");
+                     var postVar="";
+                     var postVar2="";
+                           var target=catcher[1];
+                     if (catcher[1]=="rpc"){
+                           postVar='<?xml version="1.0"?><methodCall><methodName>'+catcher[2]+'</methodName><params>';
+                        postVar2="</params></methodCall>";
+                        target=rpc;
+                     }
+                           var z = function() {
+                                   }
+                           return z;
+                         }
+       var z = function()
+             {
+                   if (dhx_catch)
+                      var res=true;
+                   for (var i=0; i<dhx_catch.length; i++) {
+                      if (dhx_catch[i] != null) {
+                           var zr = dhx_catch[i].apply( m_obj, arguments );
+                           res = res && zr;
+                      }
+                   }
+                   return res;
+                }
+       z.addEvent = function(ev)
+                {
+                       if ( typeof(ev) != "function" )
+                           if (ev && ev.indexOf && ev.indexOf("server:") == 0)
+                               ev = new func_server(ev,m_obj.rpcServer);
+                           else
+                               ev = eval(ev);
+                       if (ev)
+                           return dhx_catch.push( ev ) - 1;
+                       return false;
+                }
+       z.removeEvent = function(id)
+                   {
+                     dhx_catch[id] = null;
+                   }
+       return z;
+   }
+
+   this.detachEvent = function(id)
+   {
+      if (id != false) {
+         var list = id.split(':');            //get EventName and ID
+         this[ list[0] ].removeEvent( list[1] );   //remove event
+      }
+   }
+};
+
+
+//combo
+(function(){
+	dhtmlx.extend_api("dhtmlXCombo",{
+		_init:function(obj){
+			if (obj.image_path) 
+				dhx_globalImgPath=obj.image_path;
+			return [obj.parent, obj.name, (obj.width||"100%"), obj.type, obj.index ];
+		},
+		filter:"filter_command",
+		auto_height:"enableOptionAutoHeight",
+		auto_position:"enableOptionAutoPositioning",
+		auto_width:"enableOptionAutoWidth",
+		xml:"loadXML",
+		readonly:"readonly",
+		items:"addOption"
+	},{
+		filter_command:function(data){
+			if (typeof data == "string")
+				this.enableFilteringMode(true,data);
+			else
+				this.enableFilteringMode(data);
+		}
+	});
+})();
+
+
+//(c)dhtmlx ltd. www.dhtmlx.com
diff --git a/data/web/static/dhtmlxcommon.js b/data/web/static/dhtmlxcommon.js
new file mode 100644
--- /dev/null
+++ b/data/web/static/dhtmlxcommon.js
@@ -0,0 +1,940 @@
+//v.2.6 build 100722
+
+/*
+Copyright DHTMLX LTD. http://www.dhtmlx.com
+You allowed to use this component or parts of it under GPL terms
+To use it on other terms or get Professional edition of the component please contact us at sales@dhtmlx.com
+*/
+dhtmlx=function(obj){
+	for (var a in obj) dhtmlx[a]=obj[a];
+	return dhtmlx; //simple singleton
+};
+dhtmlx.extend_api=function(name,map,ext){
+	var t = window[name];
+	if (!t) return; //component not defined
+	window[name]=function(obj){
+		if (obj && typeof obj == "object" && !obj.tagName){
+			var that = t.apply(this,(map._init?map._init(obj):arguments));
+			//global settings
+			for (var a in dhtmlx)
+				if (map[a]) this[map[a]](dhtmlx[a]);			
+			//local settings
+			for (var a in obj){
+				if (map[a]) this[map[a]](obj[a]);
+				else if (a.indexOf("on")==0){
+					this.attachEvent(a,obj[a]);
+				}
+			}
+		} else
+			var that = t.apply(this,arguments);
+		if (map._patch) map._patch(this);
+		return that||this;
+	};
+	window[name].prototype=t.prototype;
+	if (ext)
+		dhtmlXHeir(window[name].prototype,ext);
+};
+
+dhtmlxAjax={
+	get:function(url,callback){
+		var t=new dtmlXMLLoaderObject(true);
+		t.async=(arguments.length<3);
+		t.waitCall=callback;
+		t.loadXML(url)
+		return t;
+	},
+	post:function(url,post,callback){
+		var t=new dtmlXMLLoaderObject(true);
+		t.async=(arguments.length<4);
+		t.waitCall=callback;
+		t.loadXML(url,true,post)
+		return t;
+	},
+	getSync:function(url){
+		return this.get(url,null,true)
+	},
+	postSync:function(url,post){
+		return this.post(url,post,null,true);		
+	}
+}
+
+/**
+  *     @desc: xmlLoader object
+  *     @type: private
+  *     @param: funcObject - xml parser function
+  *     @param: object - jsControl object
+  *     @param: async - sync/async mode (async by default)
+  *     @param: rSeed - enable/disable random seed ( prevent IE caching)
+  *     @topic: 0
+  */
+function dtmlXMLLoaderObject(funcObject, dhtmlObject, async, rSeed){
+	this.xmlDoc="";
+
+	if (typeof (async) != "undefined")
+		this.async=async;
+	else
+		this.async=true;
+
+	this.onloadAction=funcObject||null;
+	this.mainObject=dhtmlObject||null;
+	this.waitCall=null;
+	this.rSeed=rSeed||false;
+	return this;
+};
+/**
+  *     @desc: xml loading handler
+  *     @type: private
+  *     @param: dtmlObject - xmlLoader object
+  *     @topic: 0
+  */
+dtmlXMLLoaderObject.prototype.waitLoadFunction=function(dhtmlObject){
+	var once = true;
+	this.check=function (){
+		if ((dhtmlObject)&&(dhtmlObject.onloadAction != null)){
+			if ((!dhtmlObject.xmlDoc.readyState)||(dhtmlObject.xmlDoc.readyState == 4)){
+				if (!once)
+					return;
+
+				once=false; //IE 5 fix
+				if (typeof dhtmlObject.onloadAction == "function")
+					dhtmlObject.onloadAction(dhtmlObject.mainObject, null, null, null, dhtmlObject);
+
+				if (dhtmlObject.waitCall){
+					dhtmlObject.waitCall.call(this,dhtmlObject);
+					dhtmlObject.waitCall=null;
+				}
+			}
+		}
+	};
+	return this.check;
+};
+
+/**
+  *     @desc: return XML top node
+  *     @param: tagName - top XML node tag name (not used in IE, required for Safari and Mozilla)
+  *     @type: private
+  *     @returns: top XML node
+  *     @topic: 0  
+  */
+dtmlXMLLoaderObject.prototype.getXMLTopNode=function(tagName, oldObj){
+	if (this.xmlDoc.responseXML){
+		var temp = this.xmlDoc.responseXML.getElementsByTagName(tagName);
+		if(temp.length==0 && tagName.indexOf(":")!=-1)
+			var temp = this.xmlDoc.responseXML.getElementsByTagName((tagName.split(":"))[1]);
+		var z = temp[0];
+	} else
+		var z = this.xmlDoc.documentElement;
+
+	if (z){
+		this._retry=false;
+		return z;
+	}
+
+	if ((_isIE)&&(!this._retry)){
+		//fall back to MS.XMLDOM
+		var xmlString = this.xmlDoc.responseText;
+		var oldObj = this.xmlDoc;
+		this._retry=true;
+		this.xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
+		this.xmlDoc.async=false;
+		this.xmlDoc["loadXM"+"L"](xmlString);
+
+		return this.getXMLTopNode(tagName, oldObj);
+	}
+	dhtmlxError.throwError("LoadXML", "Incorrect XML", [
+		(oldObj||this.xmlDoc),
+		this.mainObject
+	]);
+
+	return document.createElement("DIV");
+};
+
+/**
+  *     @desc: load XML from string
+  *     @type: private
+  *     @param: xmlString - xml string
+  *     @topic: 0  
+  */
+dtmlXMLLoaderObject.prototype.loadXMLString=function(xmlString){
+	{
+		try{
+			var parser = new DOMParser();
+			this.xmlDoc=parser.parseFromString(xmlString, "text/xml");
+		}
+		catch (e){
+			this.xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
+			this.xmlDoc.async=this.async;
+			this.xmlDoc["loadXM"+"L"](xmlString);
+		}
+	}
+
+	this.onloadAction(this.mainObject, null, null, null, this);
+
+	if (this.waitCall){
+		this.waitCall();
+		this.waitCall=null;
+	}
+}
+/**
+  *     @desc: load XML
+  *     @type: private
+  *     @param: filePath - xml file path
+  *     @param: postMode - send POST request
+  *     @param: postVars - list of vars for post request
+  *     @topic: 0
+  */
+dtmlXMLLoaderObject.prototype.loadXML=function(filePath, postMode, postVars, rpc){
+	if (this.rSeed)
+		filePath+=((filePath.indexOf("?") != -1) ? "&" : "?")+"a_dhx_rSeed="+(new Date()).valueOf();
+	this.filePath=filePath;
+
+	if ((!_isIE)&&(window.XMLHttpRequest))
+		this.xmlDoc=new XMLHttpRequest();
+	else {
+		if (document.implementation&&document.implementation.createDocument){
+			this.xmlDoc=document.implementation.createDocument("", "", null);
+			this.xmlDoc.onload=new this.waitLoadFunction(this);
+			this.xmlDoc.load(filePath);
+			return;
+		} else
+			this.xmlDoc=new ActiveXObject("Microsoft.XMLHTTP");
+	}
+
+	if (this.async)
+		this.xmlDoc.onreadystatechange=new this.waitLoadFunction(this);
+	this.xmlDoc.open(postMode ? "POST" : "GET", filePath, this.async);
+
+	if (rpc){
+		this.xmlDoc.setRequestHeader("User-Agent", "dhtmlxRPC v0.1 ("+navigator.userAgent+")");
+		this.xmlDoc.setRequestHeader("Content-type", "text/xml");
+	}
+
+	else if (postMode)
+		this.xmlDoc.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+		
+	this.xmlDoc.setRequestHeader("X-Requested-With","XMLHttpRequest");
+	this.xmlDoc.send(null||postVars);
+
+	if (!this.async)
+		(new this.waitLoadFunction(this))();
+};
+/**
+  *     @desc: destructor, cleans used memory
+  *     @type: private
+  *     @topic: 0
+  */
+dtmlXMLLoaderObject.prototype.destructor=function(){
+	this._filterXPath = null;
+	this._getAllNamedChilds = null;
+	this._retry = null;
+	this.async = null;
+	this.rSeed = null;
+	this.filePath = null;
+	this.onloadAction = null;
+	this.mainObject = null;
+	this.xmlDoc = null;
+	this.doXPath = null;
+	this.doXPathOpera = null;
+	this.doXSLTransToObject = null;
+	this.doXSLTransToString = null;
+	this.loadXML = null;
+	this.loadXMLString = null;
+	// this.waitLoadFunction = null;
+	this.doSerialization = null;
+	this.xmlNodeToJSON = null;
+	this.getXMLTopNode = null;
+	this.setXSLParamValue = null;
+	return null;
+}
+
+dtmlXMLLoaderObject.prototype.xmlNodeToJSON = function(node){
+        var t={};
+        for (var i=0; i<node.attributes.length; i++)
+            t[node.attributes[i].name]=node.attributes[i].value;
+        t["_tagvalue"]=node.firstChild?node.firstChild.nodeValue:"";
+        for (var i=0; i<node.childNodes.length; i++){
+            var name=node.childNodes[i].tagName;
+            if (name){
+                if (!t[name]) t[name]=[];
+                t[name].push(this.xmlNodeToJSON(node.childNodes[i]));
+            }            
+        }        
+        return t;
+    }
+
+/**  
+  *     @desc: Call wrapper
+  *     @type: private
+  *     @param: funcObject - action handler
+  *     @param: dhtmlObject - user data
+  *     @returns: function handler
+  *     @topic: 0  
+  */
+function callerFunction(funcObject, dhtmlObject){
+	this.handler=function(e){
+		if (!e)
+			e=window.event;
+		funcObject(e, dhtmlObject);
+		return true;
+	};
+	return this.handler;
+};
+
+/**  
+  *     @desc: Calculate absolute position of html object
+  *     @type: private
+  *     @param: htmlObject - html object
+  *     @topic: 0  
+  */
+function getAbsoluteLeft(htmlObject){
+	return getOffset(htmlObject).left;
+}
+/**
+  *     @desc: Calculate absolute position of html object
+  *     @type: private
+  *     @param: htmlObject - html object
+  *     @topic: 0  
+  */
+function getAbsoluteTop(htmlObject){
+	return getOffset(htmlObject).top;
+}
+
+function getOffsetSum(elem) {
+	var top=0, left=0;
+	while(elem) {
+		top = top + parseInt(elem.offsetTop);
+		left = left + parseInt(elem.offsetLeft);
+		elem = elem.offsetParent;
+	}
+	return {top: top, left: left};
+}
+function getOffsetRect(elem) {
+	var box = elem.getBoundingClientRect();
+	var body = document.body;
+	var docElem = document.documentElement;
+	var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
+	var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;
+	var clientTop = docElem.clientTop || body.clientTop || 0;
+	var clientLeft = docElem.clientLeft || body.clientLeft || 0;
+	var top  = box.top +  scrollTop - clientTop;
+	var left = box.left + scrollLeft - clientLeft;
+	return { top: Math.round(top), left: Math.round(left) };
+}
+function getOffset(elem) {
+	if (elem.getBoundingClientRect) {
+		return getOffsetRect(elem);
+	} else {
+		return getOffsetSum(elem);
+	}
+}
+
+/**  
+*     @desc: Convert string to it boolean representation
+*     @type: private
+*     @param: inputString - string for covertion
+*     @topic: 0
+*/
+function convertStringToBoolean(inputString){
+	if (typeof (inputString) == "string")
+		inputString=inputString.toLowerCase();
+
+	switch (inputString){
+		case "1":
+		case "true":
+		case "yes":
+		case "y":
+		case 1:
+		case true:
+			return true;
+			break;
+
+		default: return false;
+	}
+}
+
+/**  
+*     @desc: find out what symbol to use as url param delimiters in further params
+*     @type: private
+*     @param: str - current url string
+*     @topic: 0  
+*/
+function getUrlSymbol(str){
+	if (str.indexOf("?") != -1)
+		return "&"
+	else
+		return "?"
+}
+
+function dhtmlDragAndDropObject(){
+	if (window.dhtmlDragAndDrop)
+		return window.dhtmlDragAndDrop;
+
+	this.lastLanding=0;
+	this.dragNode=0;
+	this.dragStartNode=0;
+	this.dragStartObject=0;
+	this.tempDOMU=null;
+	this.tempDOMM=null;
+	this.waitDrag=0;
+	window.dhtmlDragAndDrop=this;
+
+	return this;
+};
+
+dhtmlDragAndDropObject.prototype.removeDraggableItem=function(htmlNode){
+	htmlNode.onmousedown=null;
+	htmlNode.dragStarter=null;
+	htmlNode.dragLanding=null;
+}
+dhtmlDragAndDropObject.prototype.addDraggableItem=function(htmlNode, dhtmlObject){
+	htmlNode.onmousedown=this.preCreateDragCopy;
+	htmlNode.dragStarter=dhtmlObject;
+	this.addDragLanding(htmlNode, dhtmlObject);
+}
+dhtmlDragAndDropObject.prototype.addDragLanding=function(htmlNode, dhtmlObject){
+	htmlNode.dragLanding=dhtmlObject;
+}
+dhtmlDragAndDropObject.prototype.preCreateDragCopy=function(e){
+	if ((e||window.event) && (e||event).button == 2)
+		return;
+
+	if (window.dhtmlDragAndDrop.waitDrag){
+		window.dhtmlDragAndDrop.waitDrag=0;
+		document.body.onmouseup=window.dhtmlDragAndDrop.tempDOMU;
+		document.body.onmousemove=window.dhtmlDragAndDrop.tempDOMM;
+		return false;
+	}
+
+	window.dhtmlDragAndDrop.waitDrag=1;
+	window.dhtmlDragAndDrop.tempDOMU=document.body.onmouseup;
+	window.dhtmlDragAndDrop.tempDOMM=document.body.onmousemove;
+	window.dhtmlDragAndDrop.dragStartNode=this;
+	window.dhtmlDragAndDrop.dragStartObject=this.dragStarter;
+	document.body.onmouseup=window.dhtmlDragAndDrop.preCreateDragCopy;
+	document.body.onmousemove=window.dhtmlDragAndDrop.callDrag;
+	window.dhtmlDragAndDrop.downtime = new Date().valueOf();
+	
+
+	if ((e)&&(e.preventDefault)){
+		e.preventDefault();
+		return false;
+	}
+	return false;
+};
+dhtmlDragAndDropObject.prototype.callDrag=function(e){
+	if (!e)
+		e=window.event;
+	dragger=window.dhtmlDragAndDrop;
+	if ((new Date()).valueOf()-dragger.downtime<100) return;
+
+	if ((e.button == 0)&&(_isIE))
+		return dragger.stopDrag();
+
+	if (!dragger.dragNode&&dragger.waitDrag){
+		dragger.dragNode=dragger.dragStartObject._createDragNode(dragger.dragStartNode, e);
+
+		if (!dragger.dragNode)
+			return dragger.stopDrag();
+
+		dragger.dragNode.onselectstart=function(){return false;}
+		dragger.gldragNode=dragger.dragNode;
+		document.body.appendChild(dragger.dragNode);
+		document.body.onmouseup=dragger.stopDrag;
+		dragger.waitDrag=0;
+		dragger.dragNode.pWindow=window;
+		dragger.initFrameRoute();
+	}
+
+	if (dragger.dragNode.parentNode != window.document.body){
+		var grd = dragger.gldragNode;
+
+		if (dragger.gldragNode.old)
+			grd=dragger.gldragNode.old;
+
+		//if (!document.all) dragger.calculateFramePosition();
+		grd.parentNode.removeChild(grd);
+		var oldBody = dragger.dragNode.pWindow;
+
+		if (grd.pWindow &&	grd.pWindow.dhtmlDragAndDrop.lastLanding)
+			grd.pWindow.dhtmlDragAndDrop.lastLanding.dragLanding._dragOut(grd.pWindow.dhtmlDragAndDrop.lastLanding);	
+			
+		//		var oldp=dragger.dragNode.parentObject;
+		if (_isIE){
+			var div = document.createElement("Div");
+			div.innerHTML=dragger.dragNode.outerHTML;
+			dragger.dragNode=div.childNodes[0];
+		} else
+			dragger.dragNode=dragger.dragNode.cloneNode(true);
+
+		dragger.dragNode.pWindow=window;
+		//		dragger.dragNode.parentObject=oldp;
+
+		dragger.gldragNode.old=dragger.dragNode;
+		document.body.appendChild(dragger.dragNode);
+		oldBody.dhtmlDragAndDrop.dragNode=dragger.dragNode;
+	}
+
+	dragger.dragNode.style.left=e.clientX+15+(dragger.fx
+		? dragger.fx*(-1)
+		: 0)
+		+(document.body.scrollLeft||document.documentElement.scrollLeft)+"px";
+	dragger.dragNode.style.top=e.clientY+3+(dragger.fy
+		? dragger.fy*(-1)
+		: 0)
+		+(document.body.scrollTop||document.documentElement.scrollTop)+"px";
+
+	if (!e.srcElement)
+		var z = e.target;
+	else
+		z=e.srcElement;
+	dragger.checkLanding(z, e);
+}
+
+dhtmlDragAndDropObject.prototype.calculateFramePosition=function(n){
+	//this.fx = 0, this.fy = 0;
+	if (window.name){
+		var el = parent.frames[window.name].frameElement.offsetParent;
+		var fx = 0;
+		var fy = 0;
+
+		while (el){
+			fx+=el.offsetLeft;
+			fy+=el.offsetTop;
+			el=el.offsetParent;
+		}
+
+		if ((parent.dhtmlDragAndDrop)){
+			var ls = parent.dhtmlDragAndDrop.calculateFramePosition(1);
+			fx+=ls.split('_')[0]*1;
+			fy+=ls.split('_')[1]*1;
+		}
+
+		if (n)
+			return fx+"_"+fy;
+		else
+			this.fx=fx;
+		this.fy=fy;
+	}
+	return "0_0";
+}
+dhtmlDragAndDropObject.prototype.checkLanding=function(htmlObject, e){
+	if ((htmlObject)&&(htmlObject.dragLanding)){
+		if (this.lastLanding)
+			this.lastLanding.dragLanding._dragOut(this.lastLanding);
+		this.lastLanding=htmlObject;
+		this.lastLanding=this.lastLanding.dragLanding._dragIn(this.lastLanding, this.dragStartNode, e.clientX,
+			e.clientY, e);
+		this.lastLanding_scr=(_isIE ? e.srcElement : e.target);
+	} else {
+		if ((htmlObject)&&(htmlObject.tagName != "BODY"))
+			this.checkLanding(htmlObject.parentNode, e);
+		else {
+			if (this.lastLanding)
+				this.lastLanding.dragLanding._dragOut(this.lastLanding, e.clientX, e.clientY, e);
+			this.lastLanding=0;
+
+			if (this._onNotFound)
+				this._onNotFound();
+		}
+	}
+}
+dhtmlDragAndDropObject.prototype.stopDrag=function(e, mode){
+	dragger=window.dhtmlDragAndDrop;
+
+	if (!mode){
+		dragger.stopFrameRoute();
+		var temp = dragger.lastLanding;
+		dragger.lastLanding=null;
+
+		if (temp)
+			temp.dragLanding._drag(dragger.dragStartNode, dragger.dragStartObject, temp, (_isIE
+				? event.srcElement
+				: e.target));
+	}
+	dragger.lastLanding=null;
+
+	if ((dragger.dragNode)&&(dragger.dragNode.parentNode == document.body))
+		dragger.dragNode.parentNode.removeChild(dragger.dragNode);
+	dragger.dragNode=0;
+	dragger.gldragNode=0;
+	dragger.fx=0;
+	dragger.fy=0;
+	dragger.dragStartNode=0;
+	dragger.dragStartObject=0;
+	document.body.onmouseup=dragger.tempDOMU;
+	document.body.onmousemove=dragger.tempDOMM;
+	dragger.tempDOMU=null;
+	dragger.tempDOMM=null;
+	dragger.waitDrag=0;
+}
+
+dhtmlDragAndDropObject.prototype.stopFrameRoute=function(win){
+	if (win)
+		window.dhtmlDragAndDrop.stopDrag(1, 1);
+
+	for (var i = 0; i < window.frames.length; i++){
+		try{
+		if ((window.frames[i] != win)&&(window.frames[i].dhtmlDragAndDrop))
+			window.frames[i].dhtmlDragAndDrop.stopFrameRoute(window);
+		} catch(e){}
+	}
+
+	try{
+	if ((parent.dhtmlDragAndDrop)&&(parent != window)&&(parent != win))
+		parent.dhtmlDragAndDrop.stopFrameRoute(window);
+	} catch(e){}
+}
+dhtmlDragAndDropObject.prototype.initFrameRoute=function(win, mode){
+	if (win){
+		window.dhtmlDragAndDrop.preCreateDragCopy();
+		window.dhtmlDragAndDrop.dragStartNode=win.dhtmlDragAndDrop.dragStartNode;
+		window.dhtmlDragAndDrop.dragStartObject=win.dhtmlDragAndDrop.dragStartObject;
+		window.dhtmlDragAndDrop.dragNode=win.dhtmlDragAndDrop.dragNode;
+		window.dhtmlDragAndDrop.gldragNode=win.dhtmlDragAndDrop.dragNode;
+		window.document.body.onmouseup=window.dhtmlDragAndDrop.stopDrag;
+		window.waitDrag=0;
+
+		if (((!_isIE)&&(mode))&&((!_isFF)||(_FFrv < 1.8)))
+			window.dhtmlDragAndDrop.calculateFramePosition();
+	}
+	try{
+	if ((parent.dhtmlDragAndDrop)&&(parent != window)&&(parent != win))
+		parent.dhtmlDragAndDrop.initFrameRoute(window);
+	}catch(e){}
+
+	for (var i = 0; i < window.frames.length; i++){
+		try{
+		if ((window.frames[i] != win)&&(window.frames[i].dhtmlDragAndDrop))
+			window.frames[i].dhtmlDragAndDrop.initFrameRoute(window, ((!win||mode) ? 1 : 0));
+		} catch(e){}
+	}
+}
+
+var _isFF = false;
+var _isIE = false;
+var _isOpera = false;
+var _isKHTML = false;
+var _isMacOS = false;
+var _isChrome = false;
+
+if (navigator.userAgent.indexOf('Macintosh') != -1)
+	_isMacOS=true;
+
+
+if (navigator.userAgent.toLowerCase().indexOf('chrome')>-1)
+	_isChrome=true;
+
+if ((navigator.userAgent.indexOf('Safari') != -1)||(navigator.userAgent.indexOf('Konqueror') != -1)){
+	var _KHTMLrv = parseFloat(navigator.userAgent.substr(navigator.userAgent.indexOf('Safari')+7, 5));
+
+	if (_KHTMLrv > 525){ //mimic FF behavior for Safari 3.1+
+		_isFF=true;
+		var _FFrv = 1.9;
+	} else
+		_isKHTML=true;
+} else if (navigator.userAgent.indexOf('Opera') != -1){
+	_isOpera=true;
+	_OperaRv=parseFloat(navigator.userAgent.substr(navigator.userAgent.indexOf('Opera')+6, 3));
+}
+
+
+else if (navigator.appName.indexOf("Microsoft") != -1){
+	_isIE=true;
+	if (navigator.appVersion.indexOf("MSIE 8.0")!= -1 && document.compatMode != "BackCompat") _isIE=8;
+} else {
+	_isFF=true;
+	var _FFrv = parseFloat(navigator.userAgent.split("rv:")[1])
+}
+
+
+//multibrowser Xpath processor
+dtmlXMLLoaderObject.prototype.doXPath=function(xpathExp, docObj, namespace, result_type){
+	if (_isKHTML || (!_isIE && !window.XPathResult))
+		return this.doXPathOpera(xpathExp, docObj);
+
+	if (_isIE){ //IE
+		if (!docObj)
+			if (!this.xmlDoc.nodeName)
+				docObj=this.xmlDoc.responseXML
+			else
+				docObj=this.xmlDoc;
+
+		if (!docObj)
+			dhtmlxError.throwError("LoadXML", "Incorrect XML", [
+				(docObj||this.xmlDoc),
+				this.mainObject
+			]);
+
+		if (namespace != null)
+			docObj.setProperty("SelectionNamespaces", "xmlns:xsl='"+namespace+"'"); //
+
+		if (result_type == 'single'){
+			return docObj.selectSingleNode(xpathExp);
+		}
+		else {
+			return docObj.selectNodes(xpathExp)||new Array(0);
+		}
+	} else { //Mozilla
+		var nodeObj = docObj;
+
+		if (!docObj){
+			if (!this.xmlDoc.nodeName){
+				docObj=this.xmlDoc.responseXML
+			}
+			else {
+				docObj=this.xmlDoc;
+			}
+		}
+
+		if (!docObj)
+			dhtmlxError.throwError("LoadXML", "Incorrect XML", [
+				(docObj||this.xmlDoc),
+				this.mainObject
+			]);
+
+		if (docObj.nodeName.indexOf("document") != -1){
+			nodeObj=docObj;
+		}
+		else {
+			nodeObj=docObj;
+			docObj=docObj.ownerDocument;
+		}
+		var retType = XPathResult.ANY_TYPE;
+
+		if (result_type == 'single')
+			retType=XPathResult.FIRST_ORDERED_NODE_TYPE
+		var rowsCol = new Array();
+		var col = docObj.evaluate(xpathExp, nodeObj, function(pref){
+			return namespace
+		}, retType, null);
+
+		if (retType == XPathResult.FIRST_ORDERED_NODE_TYPE){
+			return col.singleNodeValue;
+		}
+		var thisColMemb = col.iterateNext();
+
+		while (thisColMemb){
+			rowsCol[rowsCol.length]=thisColMemb;
+			thisColMemb=col.iterateNext();
+		}
+		return rowsCol;
+	}
+}
+
+function _dhtmlxError(type, name, params){
+	if (!this.catches)
+		this.catches=new Array();
+
+	return this;
+}
+
+_dhtmlxError.prototype.catchError=function(type, func_name){
+	this.catches[type]=func_name;
+}
+_dhtmlxError.prototype.throwError=function(type, name, params){
+	if (this.catches[type])
+		return this.catches[type](type, name, params);
+
+	if (this.catches["ALL"])
+		return this.catches["ALL"](type, name, params);
+
+	alert("Error type: "+arguments[0]+"\nDescription: "+arguments[1]);
+	return null;
+}
+
+window.dhtmlxError=new _dhtmlxError();
+
+
+//opera fake, while 9.0 not released
+//multibrowser Xpath processor
+dtmlXMLLoaderObject.prototype.doXPathOpera=function(xpathExp, docObj){
+	//this is fake for Opera
+	var z = xpathExp.replace(/[\/]+/gi, "/").split('/');
+	var obj = null;
+	var i = 1;
+
+	if (!z.length)
+		return [];
+
+	if (z[0] == ".")
+		obj=[docObj]; else if (z[0] == ""){
+		obj=(this.xmlDoc.responseXML||this.xmlDoc).getElementsByTagName(z[i].replace(/\[[^\]]*\]/g, ""));
+		i++;
+	} else
+		return [];
+
+	for (i; i < z.length; i++)obj=this._getAllNamedChilds(obj, z[i]);
+
+	if (z[i-1].indexOf("[") != -1)
+		obj=this._filterXPath(obj, z[i-1]);
+	return obj;
+}
+
+dtmlXMLLoaderObject.prototype._filterXPath=function(a, b){
+	var c = new Array();
+	var b = b.replace(/[^\[]*\[\@/g, "").replace(/[\[\]\@]*/g, "");
+
+	for (var i = 0; i < a.length; i++)
+		if (a[i].getAttribute(b))
+			c[c.length]=a[i];
+
+	return c;
+}
+dtmlXMLLoaderObject.prototype._getAllNamedChilds=function(a, b){
+	var c = new Array();
+
+	if (_isKHTML)
+		b=b.toUpperCase();
+
+	for (var i = 0; i < a.length; i++)for (var j = 0; j < a[i].childNodes.length; j++){
+		if (_isKHTML){
+			if (a[i].childNodes[j].tagName&&a[i].childNodes[j].tagName.toUpperCase() == b)
+				c[c.length]=a[i].childNodes[j];
+		}
+
+		else if (a[i].childNodes[j].tagName == b)
+			c[c.length]=a[i].childNodes[j];
+	}
+
+	return c;
+}
+
+function dhtmlXHeir(a, b){
+	for (var c in b)
+		if (typeof (b[c]) == "function")
+			a[c]=b[c];
+	return a;
+}
+
+function dhtmlxEvent(el, event, handler){
+	if (el.addEventListener)
+		el.addEventListener(event, handler, false);
+
+	else if (el.attachEvent)
+		el.attachEvent("on"+event, handler);
+}
+
+//============= XSL Extension ===================================
+
+dtmlXMLLoaderObject.prototype.xslDoc=null;
+dtmlXMLLoaderObject.prototype.setXSLParamValue=function(paramName, paramValue, xslDoc){
+	if (!xslDoc)
+		xslDoc=this.xslDoc
+
+	if (xslDoc.responseXML)
+		xslDoc=xslDoc.responseXML;
+	var item =
+		this.doXPath("/xsl:stylesheet/xsl:variable[@name='"+paramName+"']", xslDoc,
+			"http:/\/www.w3.org/1999/XSL/Transform", "single");
+
+	if (item != null)
+		item.firstChild.nodeValue=paramValue
+}
+dtmlXMLLoaderObject.prototype.doXSLTransToObject=function(xslDoc, xmlDoc){
+	if (!xslDoc)
+		xslDoc=this.xslDoc;
+
+	if (xslDoc.responseXML)
+		xslDoc=xslDoc.responseXML
+
+	if (!xmlDoc)
+		xmlDoc=this.xmlDoc;
+
+	if (xmlDoc.responseXML)
+		xmlDoc=xmlDoc.responseXML
+
+	//MOzilla
+	if (!_isIE){
+		if (!this.XSLProcessor){
+			this.XSLProcessor=new XSLTProcessor();
+			this.XSLProcessor.importStylesheet(xslDoc);
+		}
+		var result = this.XSLProcessor.transformToDocument(xmlDoc);
+	} else {
+		var result = new ActiveXObject("Msxml2.DOMDocument.3.0");
+		try{
+			xmlDoc.transformNodeToObject(xslDoc, result);
+		}catch(e){
+			result = xmlDoc.transformNode(xslDoc);
+		}
+	}
+	return result;
+}
+
+dtmlXMLLoaderObject.prototype.doXSLTransToString=function(xslDoc, xmlDoc){
+	var res = this.doXSLTransToObject(xslDoc, xmlDoc);
+	if(typeof(res)=="string")
+		return res;
+	return this.doSerialization(res);
+}
+
+dtmlXMLLoaderObject.prototype.doSerialization=function(xmlDoc){
+	if (!xmlDoc)
+			xmlDoc=this.xmlDoc;
+	if (xmlDoc.responseXML)
+			xmlDoc=xmlDoc.responseXML
+	if (!_isIE){
+		var xmlSerializer = new XMLSerializer();
+		return xmlSerializer.serializeToString(xmlDoc);
+	} else
+		return xmlDoc.xml;
+}
+
+/**
+*   @desc: 
+*   @type: private
+*/
+dhtmlxEventable=function(obj){
+		obj.attachEvent=function(name, catcher, callObj){
+			name='ev_'+name.toLowerCase();
+			if (!this[name])
+				this[name]=new this.eventCatcher(callObj||this);
+				
+			return(name+':'+this[name].addEvent(catcher)); //return ID (event name & event ID)
+		}
+		obj.callEvent=function(name, arg0){ 
+			name='ev_'+name.toLowerCase();
+			if (this[name])
+				return this[name].apply(this, arg0);
+			return true;
+		}
+		obj.checkEvent=function(name){
+			return (!!this['ev_'+name.toLowerCase()])
+		}
+		obj.eventCatcher=function(obj){
+			var dhx_catch = [];
+			var z = function(){
+				var res = true;
+				for (var i = 0; i < dhx_catch.length; i++){
+					if (dhx_catch[i] != null){
+						var zr = dhx_catch[i].apply(obj, arguments);
+						res=res&&zr;
+					}
+				}
+				return res;
+			}
+			z.addEvent=function(ev){
+				if (typeof (ev) != "function")
+					ev=eval(ev);
+				if (ev)
+					return dhx_catch.push(ev)-1;
+				return false;
+			}
+			z.removeEvent=function(id){
+				dhx_catch[id]=null;
+			}
+			return z;
+		}
+		obj.detachEvent=function(id){
+			if (id != false){
+				var list = id.split(':');           //get EventName and ID
+				this[list[0]].removeEvent(list[1]); //remove event
+			}
+		}
+		obj.detachAllEvents = function(){
+			for (var name in this){
+				if (name.indexOf("ev_")==0) 
+					delete this[name];
+			}
+		}
+}
diff --git a/data/web/static/hledger.js b/data/web/static/hledger.js
new file mode 100644
--- /dev/null
+++ b/data/web/static/hledger.js
@@ -0,0 +1,138 @@
+/* hledger web ui javascripts */
+/* depends on jquery, other support libs, and additional js inserted inline */
+
+$(document).ready(function() {
+
+    /* maybe show forms */
+    if ($.url.param('add')) addformToggle();
+    else if ($.url.param('edit')) editformToggle();
+
+});
+
+function filterformToggle() {
+ var a = document.getElementById('addform');
+ var e = document.getElementById('editform');
+ var f = document.getElementById('filterform');
+ var i = document.getElementById('importform');
+ var t = document.getElementById('transactions');
+ var alink = document.getElementById('addformlink');
+ var elink = document.getElementById('editformlink');
+ var flink = document.getElementById('filterformlink');
+ var ilink = document.getElementById('importformlink');
+ var jlink = document.getElementById('journallink');
+ var rlink = document.getElementById('registerlink');
+
+ if (f.style.display == 'none') {
+  flink.style['font-weight'] = 'bold';
+  f.style.display = 'block';
+ } else {
+  flink.style['font-weight'] = 'normal';
+  f.style.display = 'none';
+ }
+ return false;
+}
+
+function addformToggle() {
+ var a = document.getElementById('addform');
+ var e = document.getElementById('editform');
+ var f = document.getElementById('filterform');
+ var i = document.getElementById('importform');
+ var t = document.getElementById('transactions');
+ var alink = document.getElementById('addformlink');
+ var elink = document.getElementById('editformlink');
+ var flink = document.getElementById('filterformlink');
+ var ilink = document.getElementById('importformlink');
+ var jlink = document.getElementById('journallink');
+ var rlink = document.getElementById('registerlink');
+
+ if (a.style.display == 'none') {
+  alink.style['font-weight'] = 'bold';
+  elink.style['font-weight'] = 'normal';
+  ilink.style['font-weight'] = 'normal';
+  jlink.style['font-weight'] = 'normal';
+  rlink.style['font-weight'] = 'normal';
+  a.style.display = 'block';
+  e.style.display = 'none';
+  i.style.display = 'none';
+  t.style.display = 'none';
+ } else {
+  alink.style['font-weight'] = 'normal';
+  elink.style['font-weight'] = 'normal';
+  ilink.style['font-weight'] = 'normal';
+  a.style.display = 'none';
+  e.style.display = 'none';
+  i.style.display = 'none';
+  t.style.display = 'block';
+ }
+ return false;
+}
+
+function editformToggle() {
+ var a = document.getElementById('addform');
+ var e = document.getElementById('editform');
+ var f = document.getElementById('filterform');
+ var i = document.getElementById('importform');
+ var t = document.getElementById('transactions');
+ var alink = document.getElementById('addformlink');
+ var elink = document.getElementById('editformlink');
+ var flink = document.getElementById('filterformlink');
+ var ilink = document.getElementById('importformlink');
+ var jlink = document.getElementById('journallink');
+ var rlink = document.getElementById('registerlink');
+
+ if (e.style.display == 'none') {
+  alink.style['font-weight'] = 'normal';
+  elink.style['font-weight'] = 'bold';
+  ilink.style['font-weight'] = 'normal';
+  jlink.style['font-weight'] = 'normal';
+  rlink.style['font-weight'] = 'normal';
+  a.style.display = 'none';
+  e.style.display = 'block';
+  i.style.display = 'none';
+  t.style.display = 'none';
+ } else {
+  alink.style['font-weight'] = 'normal';
+  elink.style['font-weight'] = 'normal';
+  ilink.style['font-weight'] = 'normal';
+  a.style.display = 'none';
+  e.style.display = 'none';
+  i.style.display = 'none';
+  t.style.display = 'block';
+ }
+ return false;
+}
+
+function importformToggle() {
+ var a = document.getElementById('addform');
+ var e = document.getElementById('editform');
+ var f = document.getElementById('filterform');
+ var i = document.getElementById('importform');
+ var t = document.getElementById('transactions');
+ var alink = document.getElementById('addformlink');
+ var elink = document.getElementById('editformlink');
+ var flink = document.getElementById('filterformlink');
+ var ilink = document.getElementById('importformlink');
+ var jlink = document.getElementById('journallink');
+ var rlink = document.getElementById('registerlink');
+
+ if (i.style.display == 'none') {
+  alink.style['font-weight'] = 'normal';
+  elink.style['font-weight'] = 'normal';
+  ilink.style['font-weight'] = 'bold';
+  jlink.style['font-weight'] = 'normal';
+  rlink.style['font-weight'] = 'normal';
+  a.style.display = 'none';
+  e.style.display = 'none';
+  i.style.display = 'block';
+  t.style.display = 'none';
+ } else {
+  alink.style['font-weight'] = 'normal';
+  elink.style['font-weight'] = 'normal';
+  ilink.style['font-weight'] = 'normal';
+  a.style.display = 'none';
+  e.style.display = 'none';
+  i.style.display = 'none';
+  t.style.display = 'block';
+ }
+ return false;
+}
diff --git a/data/web/static/images/combo_select.gif b/data/web/static/images/combo_select.gif
new file mode 100644
Binary files /dev/null and b/data/web/static/images/combo_select.gif differ
diff --git a/data/web/static/jquery.js b/data/web/static/jquery.js
new file mode 100644
--- /dev/null
+++ b/data/web/static/jquery.js
@@ -0,0 +1,154 @@
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML="   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
+function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
diff --git a/data/web/static/jquery.url.js b/data/web/static/jquery.url.js
new file mode 100644
--- /dev/null
+++ b/data/web/static/jquery.url.js
@@ -0,0 +1,1 @@
+jQuery.url=function(){var segments={};var parsed={};var options={url:window.location,strictMode:false,key:["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],q:{name:"queryKey",parser:/(?:^|&)([^&=]*)=?([^&]*)/g},parser:{strict:/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,loose:/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/}};var parseUri=function(){str=decodeURI(options.url);var m=options.parser[options.strictMode?"strict":"loose"].exec(str);var uri={};var i=14;while(i--){uri[options.key[i]]=m[i]||""}uri[options.q.name]={};uri[options.key[12]].replace(options.q.parser,function($0,$1,$2){if($1){uri[options.q.name][$1]=$2}});return uri};var key=function(key){if(!parsed.length){setUp()}if(key=="base"){if(parsed.port!==null&&parsed.port!==""){return parsed.protocol+"://"+parsed.host+":"+parsed.port+"/"}else{return parsed.protocol+"://"+parsed.host+"/"}}return(parsed[key]==="")?null:parsed[key]};var param=function(item){if(!parsed.length){setUp()}return(parsed.queryKey[item]===null)?null:parsed.queryKey[item]};var setUp=function(){parsed=parseUri();getSegments()};var getSegments=function(){var p=parsed.path;segments=[];segments=parsed.path.length==1?{}:(p.charAt(p.length-1)=="/"?p.substring(1,p.length-1):path=p.substring(1)).split("/")};return{setMode:function(mode){strictMode=mode=="strict"?true:false;return this},setUrl:function(newUri){options.url=newUri===undefined?window.location:newUri;setUp();return this},segment:function(pos){if(!parsed.length){setUp()}if(pos===undefined){return segments.length}return(segments[pos]===""||segments[pos]===undefined)?null:segments[pos]},attr:key,param:param}}();
diff --git a/data/web/static/style.css b/data/web/static/style.css
new file mode 100644
--- /dev/null
+++ b/data/web/static/style.css
@@ -0,0 +1,226 @@
+/* hledger web ui styles */
+
+/* font families & sizes */
+/* overspecified for cross-browser robustness */
+body                        { font-family:helvetica,arial,"sans serif"; }
+pre                         { font-family:courier,"courier new",monospace; }
+input.textinput, .dhx_combo_input, .dhx_combo_list    { font-size:small; }
+#editform textarea          { font-family:courier,"courier new",monospace; font-size:small; }
+.nav2                       { font-size:small; }
+#filterform                 { font-size:small; }
+.topleftlink                { font-size:small; }
+.toprightlink               { font-size:small; }
+#journalinfo                { font-size:small; }
+.help                       { font-size:smaller; }
+.form                       { font-size:small; }
+.journalreport              { font-size:small; }
+.balancereport              { font-size:small; }
+.registerreport             { font-size:small; }
+#showmoreaccounts           { font-size:small; }
+/* #addformlink                { font-size:small; } */
+/* #editformlink               { font-size:small; } */
+
+body                        { margin:0; }
+#navbar                     { padding:2px; }
+.topleftlink                { float:left; margin-right:1em; padding:2px; }
+.toprightlink               { float:right; margin-left:1em; padding:2px; }
+#navbar h1                  { display:inline-block; vertical-align:top; margin:0; }
+#journalinfo                { vertical-align:middle; margin:0; }
+/* #navbar                  { padding:4px; background-color:#eee; border-bottom:2px solid #ddd; } */
+#messages                   { color:red; background-color:#ffeeee; margin:0.5em;}
+.help                       { font-style: italic; }
+.helprow td                 { padding-bottom:8px; }
+#content                    { margin:1em; }
+#navlinks                   { margin-bottom:1em; }
+.navlink                    { }
+.navlinkcurrent             { font-weight:bold; }
+.form                       { margin:1em; }
+
+#filterformdiv              { margin:0 0 1em 0; white-space:nowrap; }
+#filterform                 { margin:0; }
+#filterform span            { padding:4px; }
+#stopfilterlink             { font-weight:bold; }
+.filtering                  { background-color:#ddd; }
+
+.current                    { font-weight:bold; background-color:#ddd; }
+.description                { padding-left:1em; }
+.account                    { white-space:nowrap; padding-left:1em; }
+.amount                     { white-space:nowrap; padding-left:1em; }
+.balance                    { white-space:nowrap; padding-left:1em; }
+.account, .amount, .balance { width:2em; }  /* minimum width */
+.positive                   { }
+.negative                   { color:#800; }
+/* .odd                     { background-color:#e8e8e8; } */
+/* .even                    { background-color:#e8f8e8; } */
+/* .even                    { background-color:#f0fff0; } */
+
+table.journalreport         { border-spacing: 0; }
+.journalreport td           { border-top:thin solid #eee; }
+.journalreport pre          { margin-top:0; }
+
+.ledger .accounts           {padding-right:1em; margin-right:1em; border-right:thin solid #eee; }
+.ledger .register           { }
+
+div.accounts                { margin-bottom:5em; }
+
+.balancereport tr           { vertical-align:top; }
+table.balancereport         { border-spacing:0; }
+.ledger .balancereport td   { padding:0; }
+.totalrule td               { border-top:thin solid black; }
+
+table.registerreport        { border-spacing:0; }
+.registerreport tr          { vertical-align:top; }
+.registerreport td          { padding-bottom:0.2em; }
+.registerreport .date       { white-space:nowrap; }
+.firstposting td            { }
+.registerreport .odd        { background-color:#f0f0f0; }
+
+#addform input.textinput, #addform .dhx_combo_input, .dhx_combo_list   { background-color:#eee; padding:4px; }
+#addform table              { }
+#addform #addbuttonrow      { text-align:right; }
+/* #editform                   { width:95%; } */
+#editform textarea          { width:100%; background-color:#eee; padding:4px; }
+#filterform table           { border-spacing:0; padding-left:1em; }
+
+
+/*------------------------------------------------------------------------------------------*/
+
+.dhx_combo_input{
+/* color:#333333; */
+/* font-family: Arial; */
+/* font-size: 9pt; */
+/* border:0px; */
+/* padding:2px 2px 2px 2px; */
+/* position:absolute; */
+/* top:0px; */
+}
+
+/* table {border:thin solid red} */
+/* div {border:thin solid yellow} */
+
+.dhx_combo_box{
+    position:relative;
+    display:inline-block;
+    /* text-align:left; */
+    /* height:20px; */
+    /* _height:22px; */
+    /* overflow:hidden; */
+    /* background-color: white; */
+}
+
+.dhx_combo_list{
+    position:absolute;
+    z-index:230;
+    overflow-y:auto;
+    overflow-x:hidden;
+    white-space:nowrap;
+    border:1px solid black;
+    height:50%;
+    /* background-color: white; */
+}
+
+.dhx_combo_list div{
+    cursor:default;
+    padding:2px 2px 2px 2px;
+}
+
+.dhx_selected_option{
+    background-color:navy;
+    color:white;
+}
+
+.dhx_combo_img{
+    /* display:none; */
+    width:18px;
+    height:20px;
+    position:absolute;
+    top:12px;
+    right:-10px;
+}
+
+.dhx_combo_option_img{
+	position:relative;
+	top:1px;
+	margin:0px;
+	margin-left:2px;
+	left:0px;
+	width:18px; height:18px;
+}
+
+/* .combo_dhx_sel{ */
+/* .dhx_selected_option{ */
+/*    background-image: url("../static/images/bg_selection.gif") !important; */
+/*    background-position: bottom; */
+/*    background-repeat: repeat-x; */
+/*    color:black; */
+/* } */
+
+
+
+/* .dhx_combo_img_rtl{ */
+/* 	position:absolute; */
+/* 	top:0px; */
+/* 	left:1px; */
+/* 	width:17px; */
+/* 	height:20px; */
+/* } */
+/* .dhx_combo_option_img_rtl{ */
+/* 	float:right; */
+/* 	margin-right :0px; */
+/* 	width:18px; height:18px; */
+/* } */
+
+/* .dhx_combo_list_rtl{ */
+/* 	direction: rtl; */
+/* 	unicode-bidi : bidi-override; */
+/*    position:absolute; */
+/*    z-index:230; */
+/*    overflow-y:auto; */
+/*    overflow-x:hidden; */
+/*    border:1px solid black; */
+/*    height:100px; */
+/*    /\* font-family: Arial; *\/ */
+/*    font-size: 9pt; */
+/*    background-color: white; */
+/* } */
+/* .dhx_combo_list_rtl div{ */
+/* 	direction: rtl; */
+/* 	unicode-bidi : bidi-override; */
+/* 	padding:2px 2px 2px 2px; */
+/* } */
+/* .dhx_combo_list_rtl div div{ */
+/* 	float :right !important; */
+/* 	cursor:default; */
+/* } */
+/* .dhx_combo_list_rtl div img{ */
+/* 	float :right !important; */
+/* } */
+/* .dhx_combo_list_rtl div input{ */
+/* 	float :right !important; */
+/* } */
+
+/* .dhx_combo_box.dhx_skyblue{ */
+/* 	border:1px solid #a4bed4; */
+/* } */
+/* .dhx_combo_box.dhx_skyblue .dhx_combo_input { */
+/* 	font-family:Tahoma; */
+/* 	font-size: 11px; */
+/* 	padding:3px; */
+/* } */
+/* .dhx_combo_list.dhx_skyblue_list{ */
+/* 	background-color: #eaf2fb; */
+/* 	border:1px solid #a4bed4; */
+/* 	font-family:Tahoma; */
+/* 	font-size: 11px; */
+/* } */
+/* .dhx_combo_list.dhx_skyblue_list div{ */
+/* 	cursor:default; */
+/* 	padding:3px 4px; */
+/* } */
+/* .dhx_combo_list_rtl.dhx_skyblue_list{ */
+/*    background-color: #eaf2fb; */
+/* 	border:1px solid #a4bed4; */
+/* 	font-family:Tahoma; */
+/* 	font-size: 11px; */
+/* } */
+
diff --git a/data/web/style.css b/data/web/style.css
deleted file mode 100644
--- a/data/web/style.css
+++ /dev/null
@@ -1,27 +0,0 @@
-/* hledger web ui stylesheet */
-
-body { font-family: "helvetica","arial", "sans serif"; margin:0; }
-#navbar { background-color:#eeeeee; border-bottom:2px solid #dddddd; padding:4px 4px 6px 4px; }
-#navlinks { display:inline; }
-.navlink { }
-.navlinkcurrent { font-weight:bold; }
-#searchform { font-size:small; display:inline; margin-left:1em; }
-#resetlink { font-size:small; }
-.toprightlink { font-size:small; margin-left:1em; float:right; }
-#messages { color:red; background-color:#ffeeee; margin:0.5em;}
-.form { margin:1em; font-size:small; }
-#addform { background-color:#eeeeee; border:2px solid #dddddd; cell-padding:0; cell-spacing:0; }
-#addform { /* float:right; */ }
-#addform #descriptionrow { }
-#addform #postingrow { }
-#addform #addbuttonrow { text-align:right; }
-#editform { width:95%; }
-#editform textarea { background-color:#eeeeee; font-family:monospace; font-size:medium; width:100%; }
-#content { margin:1em; }
-.formheading td { padding-bottom:8px; }
-#formheading { font-size:medium; font-weight:bold; }
-.helprow td { padding-bottom:8px; }
-#help {font-style: italic; font-size:smaller; }
-
-/* for -fweb610 */
-#hledgerorglink, #helplink { float:right; margin-left:1em; }
diff --git a/hledger.cabal b/hledger.cabal
--- a/hledger.cabal
+++ b/hledger.cabal
@@ -1,5 +1,5 @@
 name:           hledger
-version: 0.11.1
+version: 0.12
 category:       Finance
 synopsis:       A command-line (or curses or web-based) double-entry accounting tool.
 description:
@@ -17,19 +17,25 @@
 maintainer:     Simon Michael <simon@joyful.com>
 homepage:       http://hledger.org
 bug-reports:    http://code.google.com/p/hledger/issues
-stability:      experimental
+stability:      beta
 tested-with:    GHC==6.10, GHC==6.12
-cabal-version:  >= 1.2
+cabal-version:  >= 1.6
 build-type:     Custom
 data-dir:       data
 data-files:
-                web/style.css
+                web/static/style.css
+                web/static/hledger.js
+                web/static/jquery.js
+                web/static/jquery.url.js
+                web/static/dhtmlxcommon.js
+                web/static/dhtmlxcombo.js
+                web/static/images/combo_select.gif
 extra-tmp-files:
 extra-source-files:
   README.rst
-  README2.rst
+  DEVELOPMENT.rst
   MANUAL.markdown
-  NEWS.rst
+  NEWS.markdown
   CONTRIBUTORS.rst
   data/sample.journal
   data/sample.timelog
@@ -47,14 +53,11 @@
   description: enable the web ui (using yesod/hamlet/simpleserver, requires ghc 6.12)
   default:     False
 
-flag web610
-  description: enable the web ui (using loli/hsp/simpleserver, works with ghc 6.10)
-  default:     False
-
 executable hledger
   main-is:        hledger.hs
   -- should set patchlevel here as in Makefile
   cpp-options:    -DPATCHLEVEL=0
+  ghc-options:    -threaded -W
   other-modules:
                   Hledger.Cli.Main
                   Hledger.Cli.Options
@@ -70,7 +73,7 @@
                   Hledger.Cli.Commands.Register
                   Hledger.Cli.Commands.Stats
   build-depends:
-                  hledger-lib >= 0.11.1
+                  hledger-lib == 0.12
                  ,HUnit
                  ,base >= 3 && < 5
                  ,containers
@@ -84,6 +87,7 @@
                  ,process
                  ,regexpr >= 0.5.1
                  ,safe >= 0.2
+                 ,split == 0.1.*
                  ,time
                  ,utf8-string >= 0.3
 
@@ -93,6 +97,7 @@
     build-depends:
                   Chart >= 0.11
                  ,colour
+                 ,gtk2hs-buildtools
 
   if flag(vty)
     cpp-options: -DVTY
@@ -105,29 +110,18 @@
     other-modules:Hledger.Cli.Commands.Web
     build-depends:
                   io-storage >= 0.3 && < 0.4
-                 ,yesod >= 0.4.0 && < 0.5
-                 ,convertible-text >= 0.3.0.1
-
-  if flag(web610)
-    cpp-options: -DWEB610
-    other-modules:Hledger.Cli.Commands.Web610
-    build-depends:
-                  hsp
-                 ,hsx
-                 ,xhtml >= 3000.2
-                 ,loli
-                 ,io-storage
-                 ,hack-contrib
-                 ,hack
-                 ,hack-handler-simpleserver
-                 ,HTTP >= 4000.0
-                 ,applicative-extras
+                 ,yesod >= 0.5.0.3 && < 0.6
+                 ,hamlet == 0.5.*
+                 ,convertible-text >= 0.3.0.1 && < 0.4
+                 ,data-object >= 0.3.1.2 && < 0.4
+                 ,failure >= 0.1 && < 0.2
 
 -- modules and dependencies below should be as above, except
 -- chart, vty, web etc. are not presently exposed as library functions
 library
   -- should set patchlevel here as in Makefile
   cpp-options:    -DPATCHLEVEL=0
+  ghc-options:    -W
   exposed-modules:
                   Hledger.Cli.Main
                   Hledger.Cli.Options
@@ -143,7 +137,7 @@
                   Hledger.Cli.Commands.Register
                   Hledger.Cli.Commands.Stats
   build-depends:
-                  hledger-lib >= 0.11.1
+                  hledger-lib == 0.12
                  ,HUnit
                  ,base >= 3 && < 5
                  ,containers
@@ -157,16 +151,13 @@
                  ,process
                  ,regexpr >= 0.5.1
                  ,safe >= 0.2
+                 ,split == 0.1.*
                  ,time
                  ,utf8-string >= 0.3
 
--- source-repository head
---   type:     darcs
---   location: http://joyful.com/repos/hledger
--- disabled for now due to:
--- The 'source-repository' section is new in Cabal-1.6. Unfortunately it messes
--- up the parser in earlier Cabal versions so you need to specify 'cabal-version:
--- >= 1.6'.
+source-repository head
+  type:     darcs
+  location: http://joyful.com/repos/hledger
 
 -- cf http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html
 
diff --git a/hledger.hs b/hledger.hs
--- a/hledger.hs
+++ b/hledger.hs
@@ -1,4 +1,6 @@
 #!/usr/bin/env runhaskell
 -- the hledger command-line executable; see Hledger/Cli/Main.hs
 
+module Main (main)
+where
 import Hledger.Cli.Main (main)
