diff --git a/Hledger/Cli/Commands/Web.hs b/Hledger/Cli/Commands/Web.hs
index f49726f26..ff42f3fb1 100644
--- a/Hledger/Cli/Commands/Web.hs
+++ b/Hledger/Cli/Commands/Web.hs
@@ -12,6 +12,7 @@ import System.FilePath ((>), takeFileName)
 import System.IO.Storage (withStore, putValue, getValue)
 import Text.ParserCombinators.Parsec (parse)
 import Yesod
+import Yesod.Helpers.Static
 
 import Hledger.Cli.Commands.Add (journalAddTransaction)
 import Hledger.Cli.Commands.Balance
@@ -28,36 +29,43 @@ import Paths_hledger_make (getDataFileName)
 #else
 import Paths_hledger (getDataFileName)
 #endif
+-- import Hledger.Cli.Commands.Web.Templates
 
 
-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
-     ,appWebdir  :: FilePath
+     ,appDir     :: FilePath
      ,appOpts    :: [Opt]
      ,appArgs    :: [String]
      ,appJournal :: Journal
+     ,appStatic  :: Static
      }
 
 mkYesod "HledgerWebApp" [$parseRoutes|
-/             IndexPage            GET
-/style.css    StyleCss             GET
-/journalonly  JournalOnlyPage      GET POST
-/registeronly RegisterOnlyPage     GET
-/accounts     AccountsPage         GET
-/journal      AccountsJournalPage  GET POST
-/register     AccountsRegisterPage GET POST
+/static          StaticR           Static appStatic
+/                IndexR            GET
+/journalonly     JournalOnlyR      GET POST
+/registeronly    RegisterOnlyR     GET
+/accounts        AccountsOnlyR     GET
+/journal         JournalR          GET POST
+/register        RegisterR         GET POST
 |]
 
+style_css       = StaticRoute ["style.css"]
+hledger_js      = StaticRoute ["hledger.js"]
+jquery_js       = StaticRoute ["jquery.js"]
+dhtmlxcommon_js = StaticRoute ["dhtmlxcommon.js"]
+dhtmlxcombo_js  = StaticRoute ["dhtmlxcombo.js"]
+
 instance Yesod HledgerWebApp where approot = appRoot
 
-defaultpage = AccountsJournalPage
+defaultroute = JournalR
 
 -- | A bundle of useful data passed to templates.
 data TemplateData = TD {
@@ -71,7 +79,7 @@ data TemplateData = TD {
     }
 
 mktd = TD {
-      here = IndexPage
+      here = IndexR
      ,title = "hledger"
      ,msg = Nothing
      ,a = ""
@@ -83,8 +91,9 @@ mktd = TD {
 -- | 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
 
@@ -98,10 +107,11 @@ browser baseurl = do
 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{
                appRoot=baseurl
-              ,appWebdir=fp
+              ,appDir=dir
+              ,appStatic=fileLookupDir (dir > "static") $ typeByExt -- ++[("hamlet","text/plain")]
               ,appOpts=opts
               ,appArgs=args
               ,appJournal=j
@@ -156,22 +166,14 @@ getHandlerParameters = do
 ----------------------------------------------------------------------
 -- handlers & templates
 
-getStyleCss :: Handler HledgerWebApp ()
-getStyleCss = do
-    app <- getYesod
-    let dir = appWebdir app
-    sendFile "text/css" $ dir > "style.css"
-
-----------------------------------------------------------------------
-
-getIndexPage :: Handler HledgerWebApp ()
-getIndexPage = redirect RedirectTemporary defaultpage
+getIndexR :: Handler HledgerWebApp ()
+getIndexR = redirect RedirectTemporary defaultroute
 
 ----------------------------------------------------------------------
 
 -- | A combined accounts and journal view.
-getAccountsJournalPage :: Handler HledgerWebApp RepHtml
-getAccountsJournalPage = do
+getJournalR :: Handler HledgerWebApp RepHtml
+getJournalR = do
   (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
   today <- liftIO getCurrentDay
   -- app <- getYesod
@@ -183,11 +185,10 @@ getAccountsJournalPage = do
       td = mktd{here=here, title="hledger", msg=msg, a=a, p=p, j=j, today=today}
       editform' = editform td $ jtext j
   hamletToRepHtml $ pageLayout td [$hamlet|
-^scripts^
 %div.ledger
  %div.accounts!style=float:left;  ^br^
  ^navlinks.td^
- ^addform^
+ ^addform.td^
  ^editform'^
  ^importform^
  %div#transactions.journal
@@ -195,14 +196,14 @@ getAccountsJournalPage = do
   ^jr^
 |]
 
-postAccountsJournalPage :: Handler HledgerWebApp RepPlain
-postAccountsJournalPage = postJournalOnlyPage
+postJournalR :: Handler HledgerWebApp RepPlain
+postJournalR = postJournalOnlyR
 
 ----------------------------------------------------------------------
 
 -- | A combined accounts and register view.
-getAccountsRegisterPage :: Handler HledgerWebApp RepHtml
-getAccountsRegisterPage = do
+getRegisterR :: Handler HledgerWebApp RepHtml
+getRegisterR = do
   (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
   today <- liftIO getCurrentDay
   -- app <- getYesod
@@ -215,11 +216,10 @@ getAccountsRegisterPage = do
       td = mktd{here=here, title="hledger", msg=msg, a=a, p=p, j=j, today=today}
       editform' = editform td $ jtext j
   hamletToRepHtml $ pageLayout td [$hamlet|
-^scripts^
 %div.ledger
  %div.accounts!style=float:left;  ^br^
  ^navlinks.td^
- ^addform^
+ ^addform.td^
  ^editform'^
  ^importform^
  %div#transactions.register
@@ -227,14 +227,14 @@ getAccountsRegisterPage = do
   ^rr^
 |]
 
-postAccountsRegisterPage :: Handler HledgerWebApp RepPlain
-postAccountsRegisterPage = postJournalOnlyPage
+postRegisterR :: Handler HledgerWebApp RepPlain
+postRegisterR = postJournalOnlyR
 
 ----------------------------------------------------------------------
 
 -- | A simple accounts and balances view like hledger balance.
-getAccountsPage :: Handler HledgerWebApp RepHtml
-getAccountsPage = do
+getAccountsOnlyR :: Handler HledgerWebApp RepHtml
+getAccountsOnlyR = do
   (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
   today <- liftIO getCurrentDay
   let td = mktd{here=here, title="hledger", msg=msg, a=a, p=p, j=j, today=today}
@@ -308,8 +308,8 @@ isAccountRegex s = take 1 s == "^" && (take 5 $ reverse s) == ")$|:("
 ----------------------------------------------------------------------
 
 -- | A basic journal view, like hledger print, with editing.
-getJournalOnlyPage :: Handler HledgerWebApp RepHtml
-getJournalOnlyPage = do
+getJournalOnlyR :: Handler HledgerWebApp RepHtml
+getJournalOnlyR = do
   (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
   today <- liftIO getCurrentDay
   let td = mktd{here=here, title="hledger", msg=msg, a=a, p=p, j=j, today=today}
@@ -317,12 +317,11 @@ getJournalOnlyPage = do
       txns = journalReportAsHtml opts td $ journalReport opts fspec j
   hamletToRepHtml $ pageLayout td [$hamlet|
 %div.journal
- ^scripts^
  %div.nav2
   %a#addformlink!href!onclick="return addformToggle()" add one transaction
   \ | $
   %a#editformlink!href!onclick="return editformToggle()" edit the whole journal
- ^addform^
+ ^addform.td^
  ^editform'^
  #transactions ^txns^
 |]
@@ -346,9 +345,25 @@ journalReportAsHtml _ td items = [$hamlet|
        evenodd = if even n then "even" else "odd"
        txn = trimnl $ showTransaction t where trimnl = reverse . dropWhile (=='\n') . reverse
 
-addform :: Hamlet HledgerWebAppRoute
-addform = [$hamlet|
- %form#addform!method=POST!style=display:none;
+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
@@ -361,7 +376,10 @@ addform = [$hamlet|
        %td!style=padding-left:1em;
         Description:
        %td
-        %input.textinput!size=35!name=description!value=$desc$
+        %select!id=description!name=description
+         %option
+         $forall descriptions d
+          %option!value=$d$ $d$
       %tr.helprow
        %td
        %td
@@ -369,8 +387,7 @@ addform = [$hamlet|
        %td
        %td
         .help $deschelp$
-   ^transactionfields1^
-   ^transactionfields2^
+   ^postingsfields.td^
    %tr#addbuttonrow
     %td!colspan=4
      %input!type=hidden!name=action!value=add
@@ -378,20 +395,29 @@ addform = [$hamlet|
 |]
  where
   -- datehelplink = helplink "dates" "..."
-  datehelp = "eg: 7/20, 2010/1/1, "
+  datehelp = "eg: 2010/7/20"
   deschelp = "eg: supermarket (optional)"
   date = "today"
-  desc = ""
-  transactionfields1 = transactionfields 1
-  transactionfields2 = transactionfields 2
+  descriptions = sort $ nub $ map tdescription $ jtxns $ j td
 
-transactionfields :: Int -> Hamlet HledgerWebAppRoute
-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
-   $label$:
+  %td!align=right $acctlabel$:
   %td
-   %input.textinput!size=35!name=$acctvar$!value=$acct$
+   %select!id=$acctvar$!name=$acctvar$
+    %option
+    $forall acctnames a
+     %option!value=$a$ $a$
   ^amtfield^
  %tr.helprow
   %td
@@ -402,24 +428,26 @@ transactionfields n = [$hamlet|
    .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|
+  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.textinput!size=15!name=$amtvar$!value=$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
+                     ,""
+                     )
 
 editform :: TemplateData -> String -> Hamlet HledgerWebAppRoute
 editform _ content = [$hamlet|
@@ -455,142 +483,8 @@ importform = [$hamlet|
      %a!href!onclick="return importformToggle()" cancel
 |]
 
-scripts = [$hamlet|
-
-|]
-
-postJournalOnlyPage :: Handler HledgerWebApp RepPlain
-postJournalOnlyPage = do
+postJournalOnlyR :: Handler HledgerWebApp RepPlain
+postJournalOnlyR = do
   action <- runFormPost' $ maybeStringInput "action"
   case action of Just "edit"   -> postEditForm
                  Just "import" -> postImportForm
@@ -606,9 +500,9 @@ postAddForm = do
     $ (,,,,,)
     <$> 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
@@ -643,14 +537,14 @@ postAddForm = do
    Left errs -> do
     -- save current form values in session
     setMessage $ string $ intercalate "; " errs
-    redirect RedirectTemporary AccountsRegisterPage
+    redirect RedirectTemporary RegisterR
 
    Right t -> do
     let t' = txnTieKnot t -- XXX move into balanceTransaction
     j <- liftIO $ fromJust `fmap` getValue "hledger" "journal"
     liftIO $ journalAddTransaction j opts t'
     setMessage $ string $ printf "Added transaction:\n%s" (show t')
-    redirect RedirectTemporary AccountsRegisterPage
+    redirect RedirectTemporary RegisterR
 
 -- | Handle a journal edit form post.
 postEditForm :: Handler HledgerWebApp RepPlain
@@ -663,7 +557,7 @@ postEditForm = do
    Left errs -> do
     -- XXX should save current form values in session
     setMessage $ string errs
-    redirect RedirectTemporary AccountsJournalPage
+    redirect RedirectTemporary JournalR
 
    Right t' -> do
     -- try to avoid unnecessary backups or saving invalid data
@@ -677,24 +571,24 @@ postEditForm = do
     if not changed
      then do
        setMessage $ string $ "No change"
-       redirect RedirectTemporary AccountsJournalPage
+       redirect RedirectTemporary JournalR
      else do
       jE <- liftIO $ journalFromPathAndString Nothing f tnew
       either
        (\e -> do
           setMessage $ string e
-          redirect RedirectTemporary AccountsJournalPage)
+          redirect RedirectTemporary JournalR)
        (const $ do
           liftIO $ writeFileWithBackup f tnew
           setMessage $ string $ printf "Saved journal %s\n" (show f)
-          redirect RedirectTemporary AccountsJournalPage)
+          redirect RedirectTemporary JournalR)
        jE
 
 -- | Handle an import page post.
 postImportForm :: Handler HledgerWebApp RepPlain
 postImportForm = do
   setMessage $ string $ "can't handle file upload yet"
-  redirect RedirectTemporary AccountsJournalPage
+  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
@@ -702,17 +596,17 @@ postImportForm = do
   -- case fileE of
   --  Left errs -> do
   --   setMessage $ string errs
-  --   redirect RedirectTemporary AccountsJournalPage
+  --   redirect RedirectTemporary JournalR
 
   --  Right s -> do
   --    setMessage $ string $ s
-  --    redirect RedirectTemporary AccountsJournalPage
+  --    redirect RedirectTemporary JournalR
 
 ----------------------------------------------------------------------
 
 -- | A simple postings view like hledger register.
-getRegisterOnlyPage :: Handler HledgerWebApp RepHtml
-getRegisterOnlyPage = do
+getRegisterOnlyR :: Handler HledgerWebApp RepHtml
+getRegisterOnlyR = do
   (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
   today <- liftIO getCurrentDay
   let td = mktd{here=here, title="hledger", msg=msg, a=a, p=p, j=j, today=today}
@@ -753,8 +647,8 @@ mixedAmountAsHtml b = preEscapedString $ addclass $ intercalate "
" $ lines $
 ----------------------------------------------------------------------
 
 -- | A standalone journal edit form page.
-getEditPage :: Handler HledgerWebApp RepHtml
-getEditPage = do
+getEditR :: Handler HledgerWebApp 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 ?
@@ -774,7 +668,11 @@ pageLayout td@TD{title=title, msg=msg} content = [$hamlet|
  %head
   %title $title$
   %meta!http-equiv=Content-Type!content=$metacontent$
-  %link!rel=stylesheet!type=text/css!href=@StyleCss@!media=all
+  %script!type=text/javascript!src=@StaticR.jquery_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$
@@ -787,7 +685,7 @@ pageLayout td@TD{title=title, msg=msg} content = [$hamlet|
 navbar :: TemplateData -> Hamlet HledgerWebAppRoute
 navbar TD{p=p,j=j,today=today} = [$hamlet|
  #navbar
-  %a.topleftlink!href=$hledgerurl$
+  %a.topleftlink!href=$hledgerorgurl$
    hledger
    
    $version$
@@ -817,8 +715,8 @@ navlinks td = [$hamlet|
 |]
 --  \ | $
  where
-   accountsjournallink  = navlink td "journal" AccountsJournalPage
-   accountsregisterlink = navlink td "register" AccountsRegisterPage
+   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$|]
diff --git a/data/web/static/hledger.js b/data/web/static/hledger.js
new file mode 100644
index 000000000..6e821734c
--- /dev/null
+++ b/data/web/static/hledger.js
@@ -0,0 +1,135 @@
+/* hledger web ui javascripts */
+/* depends on jquery, other support libs, and additional js inserted inline */
+
+$(document).ready(function() {
+
+
+});
+
+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/style.css b/data/web/static/style.css
similarity index 55%
rename from data/web/style.css
rename to data/web/static/style.css
index 70f735aff..4272c5c45 100644
--- a/data/web/style.css
+++ b/data/web/static/style.css
@@ -4,7 +4,7 @@
 /* overspecified for cross-browser robustness */
 body                        { font-family:helvetica,arial,"sans serif"; }
 pre                         { font-family:courier,"courier new",monospace; }
-#addform input.textinput    { font-family:courier,"courier new",monospace; font-size:small; }
+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; }
@@ -75,9 +75,152 @@ table.registerreport        { border-spacing:0; }
 .firstposting td            { }
 .registerreport .odd        { background-color:#f0f0f0; }
 
-#addform input.textinput    { background-color:#eee; padding:4px; }
+#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/hledger.cabal b/hledger.cabal
index a7ad189fa..61cbd20c2 100644
--- a/hledger.cabal
+++ b/hledger.cabal
@@ -23,7 +23,12 @@ cabal-version:  >= 1.2
 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/dhtmlxcommon.js
+                web/static/dhtmlxcombo.js
+                web/static/images/combo_select.gif
 extra-tmp-files:
 extra-source-files:
   README.rst