From 8c8395778ce8319c6210eda1574a4ac25d7649d4 Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Tue, 10 Aug 2010 00:13:47 +0000 Subject: [PATCH] web: cleanup, enable jquery, use auto-completing combo fields on add form --- Hledger/Cli/Commands/Web.hs | 360 ++++++++++++-------------------- data/web/static/hledger.js | 135 ++++++++++++ data/web/{ => static}/style.css | 147 ++++++++++++- hledger.cabal | 7 +- 4 files changed, 415 insertions(+), 234 deletions(-) create mode 100644 data/web/static/hledger.js rename data/web/{ => static}/style.css (55%) 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