Browse Source

Update edit frontend for new parsing, add static log filter and many small visual changes

Sebastian Kreisel 1 year ago
parent
commit
7902b3c10a

+ 36 - 8
README.txt

@@ -8,8 +8,6 @@ Structure for deployment
 
 elfcom
   /elfcom-git
-  /parseck_js
-  /MathJax
   /db
   /db/files
   /log
@@ -17,14 +15,17 @@ elfcom
   /elfcom_version.txt
   /bin
 
+Initially:
 * git pull elfcom-git
-* Create base build-image as elfeck/elfcom-build:1.0
+* docker build -f docker/Dockerfile_base_build -t elfeck/elfcom_build:1:0 .
+* sh docker/dockerbuild.sh
 
-* sh dockerbuild.sh
-* sh dockercommit.sh
-* git pull parseck_js
-* git pull mathjax
-* sh dockerrun.sh
+* docker build -f docker/Dockerfile_base_run -t elfeck/elfcom_run:1.0 .
+* sh docker/dockerrun.sh
+
+Updating:
+* sh docker/dockerbuild.sh
+* sh docker/dockerrun.sh
 
 
 Font-Stack
@@ -51,3 +52,30 @@ Info-Elements :: DejaVu Serif > Georgia > serif
 
 Text-Elements :: Liberation Serif > Times New Roman > serif
   * Post Content
+
+
+Kanji SRS
+------------------------------------------------------------------------------
+
+Basic Requirements:
+* Entry creation interface
+* SRS interface
+
+Basic Entry Fields:
+* Word - Kanji
+* Word - Kana Pitch Accent
+* Kanji Character List
+* Example Sentences
+* Image
+
+* Translation
+* Word audio
+* Sentence audio
+* Kanji Stroke Image
+
+* Related Words (from Kanji)
+
+Card Types:
+* Comprehension: Word (in sentence) -> Translation
+* Production-Pronunciation: Image / Blank -> Word + Pronunciation
+* Production-Kanji: Image + Kana Spelling -> Kanji

+ 7 - 7
TODO.org

@@ -1,18 +1,16 @@
 #+STARTUP: showall
 
 * Urgent
-** TODO Backend+Log: Flag to filter out files and static resources
 ** TODO Backend+Admin: Modify log al-container to be width independent
-** TODO Backend: Image Viewer: Origin and contains
-** TODO Backend: Image viewer: Direct image links
 ** TODO Backend: Add meta-tags to header
 ** TODO Backend: HTTP Header info for privacy
-** TODO Parsing: See error post. textit{\href} does not parse correctly
-** TODO Parsing: /books minipage paragraph not working correctly
+** TODO CSS: Fix the excess height for image-in-par elements
 
 * Short Term
 ** TODO Backend: Add no-script tag to math posts
 ** TODO Backend: rework + more robust config parsing
+** TODO Backend: Fix mobile issues
+** TODO Backend: Imageviewer: Support multiple contains
 
 * Small/Vague and Low-priority
 ** TODO Backend: Try to make site web-reader friendly
@@ -23,10 +21,10 @@
 
 * Features
 ** TODO Parsing: Introduce id's to headlines and textbf and maybe more?
-** TODO Backend: Rubble list only
+** TODO Backend: Rubble: Post title list only
 ** TODO Front+Backend: Change file pathing to be acc/typ/[abitrary/url/bla/blb]
 ** TODO Backend: Content type and font for japanese posts
-** TODO Backend: Image viewer: Overview page
+** TODO Backend: Content type for email transcripts
 ** TODO Frontend File: Include directy resource link
 ** TODO Frontend File: Split between new uploaded img and fetch image
 
@@ -37,6 +35,8 @@
 ** TODO Latex math via nodejs
 
 * Housekeeping
+** TODO Frontend+Log: Improve on static file filter in log
+** TODO Backend: Edit Endpoint: Fetch Access Magic Number 10
 ** TODO Backend: Url encode properly
 ** TODO Backend: Unified get argument construction
 ** TODO Backend: Login Endpoint error redirect ?!?

+ 1 - 1
elfcom-backend/src/Common.hs

@@ -294,7 +294,7 @@ spockConfig (SiteConfig dev sessDurs _ _ _ _) conn = do
     { spc_initialState = AppState selperms sell accessChan accessLock dev
     , spc_database = conn
     , spc_sessionCfg = sessConfig
-    , spc_maxRequestSize = Just (5 * 1024 * 1024)
+    , spc_maxRequestSize = Just (200 * 1024 * 1024)
     , spc_errorHandler = errorHandler
     , spc_csrfProtection = False
     , spc_csrfHeaderName = "X-Csrf-Token"

+ 4 - 2
elfcom-backend/src/Endpoint/AccessLog.hs

@@ -30,7 +30,8 @@ handleAccessLogEndpoints lock logD = do
 
 accessLogFetchHandler :: TVar Bool -> T.Text -> AccessLogFetchReq ->
                          Action ctx AccessLogFetchRsp
-accessLogFetchHandler lock logD (AccessLogFetchReq exclMe exclRb mtime mip) =do
+accessLogFetchHandler lock logD (AccessLogFetchReq exclMe exclRb exclSt
+                                 mtime mip) = do
   tim <- case mtime of
     Nothing -> liftIO $ do
       now <- getCurrentTime
@@ -46,12 +47,13 @@ accessLogFetchHandler lock logD (AccessLogFetchReq exclMe exclRb mtime mip) =do
                     )
   logtext <- liftIO $ readAccessLogFiles lock logD tim
   return $ AccessLogFetchOkay $
-    buildIpSummaries (AccessLogQuery muid exclRb tim mip) logtext
+    buildIpSummaries (AccessLogQuery muid exclRb exclSt tim mip) logtext
 
 
 data AccessLogFetchReq = AccessLogFetchReq {
   fetMe :: !Bool
   , fetRobots :: !Bool
+  , fetStatic :: !Bool
   , fetTime :: !(Maybe (UTCTime, UTCTime))
   , fetIps :: !([T.Text]) }
                        deriving (Show, Eq, Generic, ToJSON, FromJSON)

+ 7 - 10
elfcom-backend/src/Endpoint/Edit.hs

@@ -38,17 +38,14 @@ handleEditEndpoints = do
 -- ---------------------------------------------------------------------------
 
 editFetchHandler :: EditFetchReq -> Action ctx EditFetchRsp
-editFetchHandler (EditFetchReq po mtime mtitle mcat) = do
-  let q = PostQuery 10 (tr po) (fromMaybe [] mcat) mtitle mtime Nothing
+editFetchHandler (EditFetchReq mts mtime mtitle mcat) = do
+  let q = PostQuery 10 (fromMaybe [] mts) (fromMaybe [] mcat)
+          mtitle mtime Nothing
   posts <- runSQL $ queryPostList q
   let res = map (\(pid, p) -> (fromIntegral $ fromSqlKey pid,
-                               displayName (postTitle p) (postModDate p)))
+                               displayName (postTitle p) (postCrtDate p)))
             posts
   return $ EditFetchOkay res
-  where tr 0 = OnlySite
-        tr 1 = OnlyPosts
-        tr 2 = OnlyDrivel
-        tr _ = AllPosts
 
 editLoadHandler :: EditLoadReq -> Action ctx EditLoadRsp
 editLoadHandler (EditLoadReq pid) = do
@@ -153,8 +150,8 @@ isUrlAllowed u = T.isPrefixOf "/" u &&
 
 displayName :: Maybe T.Text -> UTCTime -> T.Text
 displayName (Just tit) _ = tit
-displayName Nothing tm = T.concat ["[Post from ",  T.pack tstring, " ]"]
-  where tstring = formatTime defaultTimeLocale "%R %d. %b %y" tm
+displayName Nothing tm = T.concat ["[Rubble ",  T.pack tstring, " ]"]
+  where tstring = formatTime defaultTimeLocale "%d. %b %y :: %R" tm
 
 
 -- Data Types
@@ -193,7 +190,7 @@ data EditLoadRsp = EditLoadOkay { lodTitle :: !T.Text
                  deriving (Show, Eq, Generic, ToJSON, FromJSON)
 
 
-data EditFetchReq = EditFetchReq { fetPostOnly :: !Int
+data EditFetchReq = EditFetchReq { fetTypes :: !(Maybe [Int])
                                  , fetTime :: !(Maybe (UTCTime, UTCTime))
                                  , fetTitle :: !(Maybe T.Text)
                                  , fetCategories :: !(Maybe [T.Text]) }

+ 5 - 5
elfcom-backend/src/Main.hs

@@ -174,13 +174,13 @@ getDrivel muser = do
   let selCats = foo $ T.splitOn "," (fromMaybe "" mselcats)
   let currPage = fromMaybe 0 mpage
   ptype <- case (mpo, mdro) of
-    (Just True, _) -> return OnlyPosts
-    (_, Just True) -> return OnlyDrivel
-    _ -> return AllDrivel
+    (Just True, _) -> return [1]
+    (_, Just True) -> return [2]
+    _ -> return [1,2]
   now <- liftIO getCurrentTime
   let cquery = PostQuery
                { access = acc
-               , postType = ptype
+               , postTypes = ptype
                , cats = selCats
                , title = Nothing
                , drange = Nothing
@@ -201,7 +201,7 @@ getDrivel muser = do
                , rndElf = rndE}
   let pquery = PostQuery
                { access = acc
-               , postType = ptype
+               , postTypes = ptype
                , cats = selCats
                , title = Nothing
                , drange = Nothing

+ 8 - 6
elfcom-backend/src/Model/AccessLog.hs

@@ -50,8 +50,9 @@ data AccessLogEntry = AccessLogEntry { timestamp :: UTCTime
                                      , loc :: IpLocation }
                     deriving Show
 
-data AccessLogQuery = AccessLogQuery { inclMe :: Maybe Int
-                                     , inclRb :: Bool
+data AccessLogQuery = AccessLogQuery { exclMe :: Maybe Int
+                                     , exclRb :: Bool
+                                     , exclSt :: Bool
                                      , timeRange :: (UTCTime, UTCTime)
                                      , exclIps :: [T.Text]
                                      }
@@ -194,7 +195,7 @@ buildIpSummaries :: AccessLogQuery -> T.Text -> [AccessLogSummary]
 buildIpSummaries query logtext =
   let xs = map buildIpSummary (groupEq (\x y -> (ip x) == (ip y))
                                (filEntries query (parseToEntries logtext)) [])
-  in filter (\x -> meFil (inclMe query) (sumUserIds x)) xs
+  in filter (\x -> meFil (exclMe query) (sumUserIds x)) xs
   where meFil Nothing _ = True
         meFil (Just uid) uids = uid `notElem` uids
 
@@ -215,12 +216,13 @@ buildIpSummary es = IpSummary (ip $ head es)
                   (map(\e ->(timestamp e,path e,fromMaybe "" $ referer e)) es)
 
 filEntries :: AccessLogQuery -> [AccessLogEntry] -> [AccessLogEntry]
-filEntries (AccessLogQuery mme rb dates excl) entries =
-  filter (\e -> datFil e && rbFil e && ipFil e) entries
+filEntries (AccessLogQuery mme rb st dates excl) entries =
+  filter (\e -> datFil e && rbFil e && stFil e && ipFil e) entries
   where robotIps = nub $ map ip (filter (\e -> (path e) == "/robots.txt")
                                  entries)
-        rbFil e = ((not rb) || (ip e) `notElem` robotIps)
+        rbFil e = (not rb) || (ip e) `notElem` robotIps
         ipFil e = (ip e) `notElem` excl
+        stFil e = (not st) || (not $ "files/" `T.isInfixOf` (path e))
         datFil e = (timestamp e) >= fst dates && (timestamp e) <= snd dates
 
 parseToEntries :: T.Text -> [AccessLogEntry]

+ 8 - 13
elfcom-backend/src/Model/DB.hs

@@ -12,10 +12,8 @@ import Model.CryptoUtils
 
 -- --------------------------------------------------------------------------
 
-data PostQueryType = OnlySite | OnlyPosts | OnlyDrivel | AllDrivel | AllPosts
-                   deriving Show
 data PostQuery = PostQuery { access :: Int
-                           , postType :: PostQueryType
+                           , postTypes :: [Int]
                            , cats :: [T.Text]
                            , title :: Maybe T.Text
                            , drange :: Maybe (UTCTime, UTCTime)
@@ -118,15 +116,15 @@ queryPostUrl url = do
   return $ fmap (\x -> (entityKey x, entityVal x)) mpost
 
 queryPostCount :: PostQuery -> SqlPersistM Int
-queryPostCount (PostQuery acc typ categs _ drng _) = do
-  restrT <- restrictType typ
+queryPostCount (PostQuery acc ts categs _ drng _) = do
+  restrT <- restrictType ts
   restrC <- restrictCats categs
   restrD <- restrictDate drng
   count (restrT ++ restrC ++ restrD ++ [PostAccess <=. acc])
 
 queryPostList :: PostQuery -> SqlPersistM [(PostId, Post)]
-queryPostList (PostQuery acc typ categs _ drng prng) = do
-  restrT <- restrictType typ
+queryPostList (PostQuery acc ts categs _ drng prng) = do
+  restrT <- restrictType ts
   restrC <- restrictCats categs
   restrD <- restrictDate drng
   restrP <- restrictRange prng
@@ -162,12 +160,9 @@ adjustTime :: Post -> UTCTime -> UTCTime -> Post
 adjustTime (Post tit cat url crl cdr _ _ typ acc hm) crt' mdd' =
   Post tit cat url crl cdr crt' mdd' typ acc hm
 
-restrictType :: PostQueryType -> SqlPersistM [Filter Post]
-restrictType OnlySite = return [PostPostType ==. 0]
-restrictType OnlyPosts = return [PostPostType ==. 1]
-restrictType OnlyDrivel = return [PostPostType ==. 2]
-restrictType AllDrivel = return [PostPostType >=. 1]
-restrictType AllPosts = return [PostPostType >=. 0]
+restrictType :: [Int] -> SqlPersistM [Filter Post]
+restrictType ts = return [PostPostType <-. ts]
+
 
 restrictCats :: [T.Text] -> SqlPersistM [Filter Post]
 restrictCats [] = return []

+ 48 - 34
elfcom-backend/src/View/Edit.hs

@@ -34,6 +34,7 @@ editMain = div ! class_ "cnt-edit-main font-admin-ui" $ do
     textarea ! id "edit-text" $ ""
     button ! id "edit-visit-button" $ "Vi!"
     button ! id "edit-info-button" $ "If!"
+    button ! id "edit-reparse-button" $ "Re!"
     input ! id "edit-response" ! class_ "edit-input" ! readonly "readonly"
     input ! id "edit-action" ! class_ "edit-input"
     button ! id "edit-submit-button" $ "Go!"
@@ -62,58 +63,71 @@ editHeader = div ! class_ "cnt-edit-header" $ ""
 
 editFooter ::  Html
 editFooter = div ! class_ "cnt-edit-footer" $ do
-  div ! class_ "edit-footer-link-cnt" $
-    a ! href "/admin" ! class_ "edit-footer-link" $ "back to admin"
-  div ! class_ "edit-footer-link-cnt" $
-    a ! href "/rubble" ! class_ "edit-footer-link" $ "back to rubble"
+  div ! class_ "edit-footer-left" $ do
+    div ! class_ "edit-footer-link-cnt" $
+      a ! href "/admin" ! class_ "edit-footer-link" $ "back to admin"
+    div ! class_ "edit-footer-link-cnt" $
+      a ! href "/rubble" ! class_ "edit-footer-link" $ "back to rubble"
+  div ! class_ "edit-footer-right" $ do
+    div ! id "edit-info-parse" $ ""
 
 
 -- Shortcuts
 -- --------------------------------------------------------------------------
 
 s1 :: [(String, String)]
-s1 = [ ("c | p | a", "")
-     , ("c = 1", "norm")
+s1 = [ ("c = 1", "norm")
      , ("c = 2", "math")
      , ("", "")
      , ("p = 0", "site")
      , ("p = 1", "post")
-     , ("p = 2", "drivel")
+     , ("p = 2", "rubble")
+     , ("p = 3", "dlog")
+     , ("p = 4", "email")
+     , ("p = 5", "chat")
      , ("", "")
-     , ("site-w", "780px")
+     , ("width", "760")
+     , ("mini-2", "375 :: 10")
+     , ("mini-3", "248 :: 8")
      ]
 
 s2 :: [(String, String)]
-s2 = [ ("^", "alt-q")
-    , ("_", "alt-w")
-    , ("{ }", "alt-<")
-    , ("frac", "alt-y")
-    , ("bb", "alt-x")
-    , ("cal", "alt-c")
-    , ("big", "alt-z")
-    , ("Big", "alt-u")
-    , ("bigg", "alt-i") ]
+s2 = [ ("frac", "a")
+     , ("sum", "s")
+     , ("prod", "d")
+     , ("int", "f")
+     , ("", "")
+     , ("bb", "g")
+     , ("cal", "h")
+     , ("", "")
+     , ("^", "q")
+     , ("_", "w")
+     , ("{ }", "e")
+     ]
 
 
 s3 :: [(String, String)]
-s3 = [ ("frac", "alt-a")
-     , ("sum", "alt-s")
-     , ("prod", "alt-d")
-     , ("int", "alt-f")
+s3 = [ ("big", "i")
+     , ("Big", "o")
+     , ("bigg", "p")
      , ("", "")
-     , ("L R Arrow", "alt-,")
-     , ("L LR Arrow", "alt-.")
-     , ("L SLR Arrow", "alt--")
-     , ("l L Arrow", "alt-l")
+     , ("L R Arrow", "r")
+     , ("L L Arrow", "t")
+     , ("L LR Arrow", "z")
+     , ("S R Arrow", "u")
      ]
 
 s4 :: [(String, String)]
-s4 = [ ("env", "alt-e")
-     , ("inline math", "alt-r")
-     , ("block math", "alt-t")
-     , ("section", "alt-v")
-     , ("textbf", "alt-b")
-     , ("textit", "alt-n")
-     , ("href", "alt-m")
-     , ("minipage 2", "alt-o")
-     , ("minipage 3", "alt-p")]
+s4 = [ ("inline math", "y")
+     , ("block math", "x")
+     , ("graphics", "c")
+     , ("section", "v")
+     , ("textbf", "b")
+     , ("textit", "n")
+     , ("href", "m")
+     , ("", "")
+     , ("env", "1")
+     , ("itemize", "2")
+     , ("enumerate", "3")
+     , ("minipage", "4")
+     , ("center", "5") ]

+ 4 - 5
elfcom-backend/src/View/FileDirectory.hs

@@ -101,17 +101,16 @@ filePage (DirQuery urlp(dbpath:[]) mref mpref mcont (Just (prv,cur,nex))) = do
       a ! class_ "fdlink" ! href (textValue $ fromMaybe "/" mref) $
         toHtml ("back to " <> (fromMaybe "elfeck" (fmap foo mref)))
   div ! class_ "cnt-fdcenter" $
-    a ! href (textValue $ urlp <> "/" <> fileName) $
-    img ! class_ "fdimage" !
-    src (textValue $ urlp <> "/" <> fileName) !
+    img ! class_ "fdimage" ! src (textValue $ urlp <> "/" <> fileName) !
     title (textValue fileName)
   div ! class_ "cnt-fdbottom" $ div ! class_ "cnt-bottomnav" $ do
     div ! class_ "cnt-fdnaventry fdbotleft" $
       a ! class_ "fdlink" !
       href (textValue $ urlp <> mkDirParam mref mpref mcont (Just prv)
             Nothing) $ "« prev"
-    div ! class_ "cnt-fdnaventry fdbotmiddle" $
-      toHtml (last $ T.splitOn "/" dbpath)
+    div ! class_ "cnt-fdnaventry fdbotmiddle" $ do
+      a ! class_ "fdlink" ! href (textValue $ urlp <> "/" <> fileName) $
+        toHtml (last $ T.splitOn "/" dbpath)
     div ! class_ "cnt-fdnaventry fdbotright" $
       a ! class_ "fdlink" !
       href (textValue $ urlp <> mkDirParam mref mpref mcont (Just nex)

+ 3 - 3
elfcom-backend/src/View/View.hs

@@ -24,7 +24,7 @@ siteHead' path htm = docTypeHtml $ head $ do
   meta ! charset "utf-8"
   meta ! name "viewport" ! content
     "width=device-width, initial-scale=1.0, maximum-scale=1.0"
-  link ! rel "icon" ! href (textValue $ T.concat [path, "/img/favicon.ico"])
+  link ! rel "icon" ! href (textValue $ T.concat[path,"/img/favicon.ico?v=8"])
   htm
 
 siteHeadFD :: T.Text -> Html -> Html
@@ -33,7 +33,7 @@ siteHeadFD path htm = docTypeHtml $ head $ do
   meta ! charset "utf-8"
   meta ! name "viewport" ! content
     "width=800, maximum-scale=1.0"
-  link ! rel "icon" ! href (textValue $ T.concat [path, "/img/favicon.ico"])
+  link ! rel "icon" ! href (textValue $T.concat[path, "/img/favicon.ico?v=8"])
   htm
 
 siteBody :: Html -> Html
@@ -69,7 +69,7 @@ siteFooter :: Maybe (UserId, User) -> T.Text -> Maybe UTCTime -> Html
 siteFooter muser rurl mmod = div ! class_ "footer font-ui" $ do
   div ! class_ "footer-info" $ "["
   wrapContainer $ do
-    div "a traditional website" ! class_ "footer-info"
+    div "an old-school website" ! class_ "footer-info"
   div ! class_ "footer-sep" $ "|"
   wrapContainer $ a "impressum & license" !
     class_ "footer-link" ! href "/impressum"

+ 13 - 15
elfcom-backend/static/css/latex.css

@@ -34,10 +34,7 @@ h3.txt-headline {
 }
 
 div.txt-par {
-    --overflow: auto;
     line-height: 160%;
-    --padding: 2px 0px 2px 0px;
-    --margin: 13px 0px 13px 0px;
 }
 
 div.txt-math {
@@ -62,6 +59,13 @@ a.txt-img-url {
 }
 
 div.txt-space {
+    display: inline-block;
+    margin: 0px;
+    padding: 0px;
+}
+
+div.txt-v-space {
+    display: block;
     margin: 0px;
     padding: 0px;
 }
@@ -74,19 +78,9 @@ div.txt-line {
     --margin: 3px 0px 0px 0px;
 }
 
-div.txt-mini-cont {
-    display: block;
-}
-
-div.txt-mini-page {
-    --overflow: auto;
-    vertical-align: top;
-    display: inline-block;
-}
-
-div.txt-mini-hspace {
-    --overflow: auto;
+div.txt-minipage {
     display: inline-block;
+    vertical-align: top;
 }
 
 ul.txt-ul {
@@ -150,3 +144,7 @@ div.txt-space-h3 {
 div.txt-space-h4 {
     height: 12px;
 }
+
+div.txt-img-cont {
+    display: inline-block;
+}

+ 21 - 2
elfcom-backend/static/css/site_edit.css

@@ -130,7 +130,7 @@ input#edit-paccess {
 }
 
 input#edit-response {
-    width: 420px;
+    width: 375px;
     margin-right: 5px;
     margin-left: 5px;
     text-align: center;
@@ -156,6 +156,12 @@ button#edit-visit-button {
 button#edit-info-button {
     width: 40px;
     height: 22px;
+    margin-right: 5px;
+}
+
+button#edit-reparse-button {
+    width: 40px;
+    height: 22px;
 }
 
 input#edit-id {
@@ -174,8 +180,21 @@ input#edit-search {
     font-size: 9px;
 }
 
-a.edit-footer-link {
+div.edit-footer-left {
+    display: inline-block;
+    width: 401px;
     font-size: 13px;
+    margin-right: 20px;
+}
+
+div.edit-footer-right {
+    display: inline-block;
+    text-align: right;
+    width: 401px;
+    font-size: 13px;
+}
+
+a.edit-footer-link {
 }
 
 div.edit-footer-link-cnt {

+ 2 - 2
elfcom-backend/static/css/site_filedirectory.css

@@ -190,11 +190,11 @@ div.ov-cont {
     width: 250px;
     height: 250px;
     display: inline-block;
-    border: 1px solid #555555;
+    --border: 1px solid #555555;
 }
 
 div.ov-cont:hover {
-    border: 1px solid #888888;
+    --border: 1px solid #555555;
 }
 
 div.ov-right {

BIN
elfcom-backend/static/img/favicon.ico


BIN
elfcom-backend/static/img/favicon.png


File diff suppressed because it is too large
+ 21 - 33
elfcom-backend/static/img/header_1.svg


+ 6 - 2
elfcom-frontend/admin/log.js

@@ -22,7 +22,7 @@ function init(sobjs) {
       Common.minuteString(d);
   }
   var dateString = "(" + buildDS(start) + " - " + buildDS(end) + ")";
-  sobjs.seaEl.value = "me;rb;date=" + dateString + ";";
+  sobjs.seaEl.value = "me;rb;st;date=" + dateString + ";";
   doFetch(sobjs);
 }
 
@@ -30,6 +30,7 @@ function doFetch(sobjs) {
   var tokens = sobjs.seaEl.value.split(";");
   var meToken = Common.filterTokens(tokens, "me");
   var rbToken = Common.filterTokens(tokens, "rb");
+  var stToken = Common.filterTokens(tokens, "st");
   var ipToken = Common.filterTokens(tokens, "ips");
   var dateToken = Common.filterTokens(tokens, "date");
   var dates = null;
@@ -48,11 +49,14 @@ function doFetch(sobjs) {
   }
   var me = false;
   var rb = false;
+  var st = false;
   if(meToken !== null) { me = true; }
   if(rbToken !== null) { rb = true; }
+  if(stToken !== null) { st = true; }
   var jsonObj = {
     "fetMe": me,
     "fetRobots": rb,
+    "fetStatic": st,
     "fetTime": dates,
     "fetIps": ips,
   };
@@ -136,7 +140,7 @@ function constructLogLine(logEntry) {
         pathLink.innerHTML = toks.slice(-2).join("/");
       }
     } else if(p.slice(-4).includes(".")) {
-      pathLink.innerHTML = "[static file ending in: " + p.slice(-4) + "]";
+      pathLink.innerHTML = "file :: " + p.split("/").slice(-2).join("/");
       pathDiv.className = "al-path al-path-grey";
     } else {
       pathLink.innerHTML = p;

+ 1 - 1
elfcom-frontend/common.js

@@ -111,7 +111,7 @@ function yearString(date) {
 }
 
 function selectByIndex(sobjs, ind) {
-    sobjs.selEl.selectedIndex = Math.min(ind, sobjs.selEl.length);
+  sobjs.selEl.selectedIndex = Math.min(ind, sobjs.selEl.length - 1);
 }
 
 function selectById(sobjs, id) {

+ 20 - 13
elfcom-frontend/edit/fetch.js

@@ -19,16 +19,15 @@ function init(sobjs) {
       Common.monthString(end) + "/" +
       Common.yearString(end) + ")";
 
-  sobjs.seaEl.value = "po;date=" + dateString + ";";
+  sobjs.seaEl.value = "p=0,1;date=" + dateString + ";";
   return doFetch(sobjs);
 }
 
 function doFetch(sobjs) {
   var tokens = sobjs.seaEl.value.split(";");
-  var soToken = Common.filterTokens(tokens, "so");
-  var poToken = Common.filterTokens(tokens, "po");
-  var doToken = Common.filterTokens(tokens, "do");
+  var typeToken = Common.filterTokens(tokens, "p");
   var dateToken = Common.filterTokens(tokens, "date");
+  var types = null;
   var dates = null;
   var cats = null;
   if(dateToken !== null) {
@@ -38,21 +37,20 @@ function doFetch(sobjs) {
   var titleToken = Common.filterTokens(tokens, "title");
   var catToken = Common.filterTokens(tokens, "cats");
   if(catToken !== null) {
-    cats = catToken.split(",").map(a => a.trim()).filter((c) => {
-      return c.length > 0;
-    });
+    cats = catToken.split(",").map(a => a.trim()).filter(c => c.length > 0);
     cats = cats.length === 0 ? null : cats;
   }
-  var ptype = 10;
-  if(soToken !== null) { ptype = 0; }
-  else if(poToken !== null) { ptype = 1; }
-  else if(doToken !== null) { ptype = 2; }
+  if(typeToken !== null) {
+    types = typeToken.split(",").map(a => a.trim()).filter(c => c.length > 0);
+    types = types.map(s => prsPostType(s)).filter(t => t >= 0);
+  }
   var jsonObj = {
-    "fetPostOnly": ptype,
+    "fetTypes": types,
     "fetTime": dates,
     "fetTitle": titleToken,
-    "fetCategories":cats,
+    "fetCategories": cats,
   };
+  console.log(jsonObj);
   return new Promise(function(resolve, reject) {
     Common.sendJson("/api/admin/edit/fetch", jsonObj)
       .then(res => res.json(),
@@ -83,3 +81,12 @@ function updateSelect(sobjs, items) {
   nopt.id = "-1"; nopt.innerHTML = "New Post";
   sobjs.selEl.add(nopt, 0);
 }
+
+function prsPostType(s) {
+  if(s === "0") { return 0; }
+  if(s === "1") { return 1; }
+  if(s === "2") { return 2; }
+  if(s === "3") { return 3; }
+  if(s === "4") { return 4; }
+  if(s === "5") { return 5; }
+}

+ 3 - 0
elfcom-frontend/edit/load.js

@@ -19,6 +19,9 @@ function init(sobjs) {
 
 function doLoad(sobjs) {
   var sid = sobjs.selEl.selectedIndex;
+  if(sid < 0) {
+    return Promise.reject("Nothing selected on doLoad");
+  }
   var sel = sobjs.selEl[sid];
   if(sel.id < 0) {
     sobjs.titEl.value = ""; sobjs.catEl.value = ""; sobjs.urlEl.value = "";

+ 72 - 33
elfcom-frontend/edit/preview.js

@@ -1,51 +1,90 @@
 "use strict";
 
-import Common from "../common.js";
-import Parseck from "../parseck.js";
+import PreblockParser from "../parseck_js/preblockparser.js";
+import LatexParser from "../parseck_js/latexparser.js";
+import HtmlWriter from "../parseck_js/htmlwriter.js";
+import Reparser from "../parseck_js/reparser.js";
 
 var Preview = {
   doPreview: doPreview,
+  doMathjax: doMathjax,
+  setSelection: setSelection,
 };
 export default Preview;
 
+var prevS = null;
+var prevE = null;
+var prevString = "";
+var blocks = null;
 
-function doPreview(sobjs) {
-  var text = sobjs.txtEl.value;
-  var tlBlocks = Parseck.parseToTLBlocks(text, false);
+var mathIndices = null;
 
-  var blockCount = tlBlocks.length;
-  var divCount = sobjs.prvEl.children.length;
+function doPreview(sobjs, fullRewrite) {
+  var string = sobjs.txtEl.value;
+  var sdiff = string.length - prevString.length;
+  var reprsInds;
+  // --- Parsing
+  var prefStart = performance.now();
+  if(blocks === null || fullRewrite) {
+    blocks = LatexParser.readLatex(PreblockParser.parsePreblocks(string));
+    fullRewrite = true; // always do full rewrite after init
+  } else {
+    reprsInds = Reparser.reparseBlocks(blocks, string, sdiff, prevS, prevE);
+  }
+  var scons = LatexParser.checkStringConsistency(blocks, string);
+  var icons = LatexParser.checkIndexConsistency(blocks);
+  // --- HTML Writing
+  var prefParse = performance.now();
+  if(fullRewrite) {
+    HtmlWriter.rewriteAll(sobjs.prvEl, blocks);
+    mathIndices = [];
+    doMathjax(sobjs);
+  } else {
+    HtmlWriter.rewritePartial(sobjs.prvEl, blocks, reprsInds.delInds,
+                              reprsInds.newInds);
+    if(reprsInds.newInds.length > 0) {
+      if(mathIndices === null) {
+        mathIndices = [reprsInds.newInds[0], reprsInds.newInds[1]];
+      } else {
+        mathIndices[0] = Math.min(mathIndices[0], reprsInds.newInds[0]);
+        mathIndices[1] = Math.max(mathIndices[1], reprsInds.newInds[1]);
+      }
+    }
+  }
+  var prefHtml = performance.now();
+  // --- Info
+  sobjs.prsEl.innerHTML = "parse " +
+    ((prefParse - prefStart) + "").substring(0,5) + "ms :: render " +
+    ((prefHtml - prefParse) + "").substring(0,5) + "ms :: " + scons + " | " +
+    icons;
 
-  console.log("block=" + blockCount + "  ||  div=" + divCount);
+  prevString = string;
+  prevS = sobjs.txtEl.selectionStart;
+  prevE = sobjs.txtEl.selectionEnd;
 
-  if(blockCount !== divCount || text === "" || tlBlocks.length === 0) {
-    var blocks = Parseck.parseToBlocks(text);
-    sobjs.prvEl.innerHTML = Parseck.writeHtmlString(blocks);
-    MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
+  return Promise.resolve();
+}
 
+function doMathjax(sobjs) {
+  if(mathIndices === null) {
     return Promise.resolve();
+  } else if(mathIndices.length === 0) {
+    MathJax.Hub.Queue(["Typeset", MathJax.Hub, sobjs.prvEl]);
+  } else {
+    var htmlBlocks = Array.from(sobjs.prvEl.children).filter(h => !isSpace(h));
+    for(var i = mathIndices[0]; i <= mathIndices[1]; i++) {
+      MathJax.Hub.Queue(["Typeset", MathJax.Hub, htmlBlocks[i]]);
+    }
   }
+  mathIndices = null;
+  return Promise.resolve();
+}
 
-  tlBlocks = Parseck.parseToTLBlocks(text, true);
-
-  //var l1 = text.length;
-  //var l2 = tlBlocks.map(a => a.toTexString()).join("").length;
-  //console.log(l1, l2);
+function setSelection(sobjs) {
+  prevS = sobjs.txtEl.selectionStart;
+  prevE = sobjs.txtEl.selectionEnd;
+}
 
-  var blockInd = 0;
-  var selInd = sobjs.txtEl.selectionStart;
-  var accumTL = tlBlocks[0].toTexString();
-  while(accumTL.length < selInd) {
-    blockInd++;
-    accumTL += tlBlocks[blockInd].toTexString();
-  }
-  console.log("Operating with blockId=" + blockInd);
-  var block = Parseck.parseToBlocks(tlBlocks[blockInd].toTexString());
-  var domEl = sobjs.prvEl.children[blockInd];
-  domEl.insertAdjacentHTML("beforebegin", Parseck.writeHtmlString(block));
-  domEl.remove();
-  //domEl.innerHTML = Parseck.writeHtmlString(block);
-
-  MathJax.Hub.Queue(["Typeset", MathJax.Hub, sobjs.prvEl.children[blockInd]]);
-  return Promise.resolve();
+function isSpace(el) {
+  return el.className.startsWith("txt-space-");
 }

+ 58 - 40
elfcom-frontend/edit/site_edit.js

@@ -6,6 +6,7 @@ import Load from "./load.js";
 import Submit from "./submit.js";
 import Preview from "./preview.js";
 
+var msSinceKeypress = 0;
 
 document.addEventListener("DOMContentLoaded", function() {
 
@@ -27,20 +28,22 @@ document.addEventListener("DOMContentLoaded", function() {
     "sbtEl": document.getElementById("edit-submit-button"),
     "vbtEl": document.getElementById("edit-visit-button"),
     "ibtEl": document.getElementById("edit-info-button"),
-    "infEl": document.getElementById("edit-info")
+    "infEl": document.getElementById("edit-info"),
+    "prsEl": document.getElementById("edit-info-parse"),
+    "pbtEl": document.getElementById("edit-reparse-button"),
   };
 
   registerEventHandlers(sobjs);
   Common.init(sobjs);
   Fetch.init(sobjs)
     .then(() => Load.init(sobjs))
-    .then(() => Preview.doPreview(sobjs));
+    .then(() => Preview.doPreview(sobjs, true));
 });
 
 function registerEventHandlers(sobjs) {
   sobjs.sbtEl.onclick = function() {
     Submit.doSubmit(sobjs, false)
-      .then(() => Preview.doPreview(sobjs));
+      .then(() => Preview.doPreview(sobjs, true));
   };
   sobjs.vbtEl.onclick = function() {
     var pid = parseInt(sobjs.pidEl.value);
@@ -56,35 +59,53 @@ function registerEventHandlers(sobjs) {
       sobjs.infEl.className = "cnt-edit-info";
     }
   };
+  sobjs.pbtEl.onclick = function() {
+    Preview.doPreview(sobjs, true);
+  }
   sobjs.selEl.onchange = function() {
     Load.doLoad(sobjs)
-      .then(() => Preview.doPreview(sobjs));
+      .then(() => Preview.doPreview(sobjs, true));
   };
   sobjs.seaEl.onkeyup = function(e) {
     if(e.keyCode === 13) {
       Fetch.doFetch(sobjs)
         .then(() => Common.selectByIndex(sobjs, 1))
         .then(() => Load.doLoad(sobjs))
-        .then(() => Preview.doPreview(sobjs));
+        .then(() => Preview.doPreview(sobjs, true));
     }
   };
   sobjs.txtEl.onkeydown = function(e) {
-    for(var key in shortcuts) {
-      if(e.altKey) {
-        if(key === e.key) {
+    if(e.altKey && e.shiftKey) {
+      for(var key in shortcuts) {
+        if(key === e.key.toLowerCase()) {
           insertTex(sobjs, shortcuts[key][0], shortcuts[key][1]);
+          msSinceKeypress = 0;
+        }
+        if(e.keyCode === 13) {
+          Preview.doPreview(sobjs, true);
         }
       }
     }
   };
-  sobjs.txtEl.onkeyup = function() { Preview.doPreview(sobjs); };
-
+  sobjs.txtEl.onkeyup = function() {
+    Preview.doPreview(sobjs, false);
+    msSinceKeypress = 0;
+  };
+  sobjs.txtEl.onmouseup = function() {
+    Preview.setSelection(sobjs);
+  };
   window.setInterval(function() {
     if(parseInt(sobjs.pidEl.value) !== -1) {
-      Submit.doSubmit(sobjs, true)
-        .then(() => Preview.doPreview(sobjs));
+      Submit.doSubmit(sobjs, true);
+       // .then(() => Preview.doPreview(sobjs));
     }
   }, 10 * 1000);
+  window.setInterval(function() {
+    msSinceKeypress += 100;
+    if(msSinceKeypress >= 500) {
+      Preview.doMathjax(sobjs);
+    }
+  }, 100);
 }
 
 function insertTex(sobjs, tex, posDiff) {
@@ -97,43 +118,40 @@ function insertTex(sobjs, tex, posDiff) {
 }
 
 var shortcuts = {
-  "q": ["^{}", -1],
-  "w": ["_{}", -1],
-
+  // math
   "a": ["\\frac{}{}", -3],
   "s": ["\\sum_{}^{}", -4],
   "d": ["\\prod_{}^{}", -4],
   "f": ["\\int_{}^{}", -4],
 
-  "y": ["\\mathfrac{}", -1],
-  "x": ["\\mathbb{}", -1],
-  "c": ["\\mathcal{}", -1],
-
-  "z": ["\\big", 0],
-  "u": ["\\Big", 0],
-  "i": ["\\bigg", 0],
-
-  "<": ["\\{\\}", -2],
+  "g": ["\\mathbb{}", -1],
+  "h": ["\\mathcal{}", -1],
 
-  ",": ["~\\Longrightarrow~", 0],
-  ".": ["~\\Longleftrightarrow~", 0],
-  "-": ["\\mathrel{\\rlap{\\hskip .5em/}}\\Longrightarrow", 0],
-  "l": ["~\\longrightarrow~", 0],
-
-  "e": ["\\begin{}\n\n\\end{}", -9],
-  "r": ["\\(  \\)", -3],
-  "t": ["\\[\n\n\\]", -3],
+  "q": ["^{}", -1],
+  "w": ["_{}", -1],
+  "e": ["\\{\\}", -2],
+  "i": ["\\big\\big", -4],
+  "o": ["\\Big\\Big", -4],
+  "p": ["\\bigg\\bigg", -5],
+
+  "r": ["~\\Longrightarrow~", 0],
+  "t": ["~\\Longlefttarrow~", 0],
+  "z": ["~\\Longleftrightarrow~", 0],
+  "u": ["~\\mathrel{\\rlap{\\hskip .5em/}}\\Longrightarrow~", 0],
+
+  // general
+  "y": ["\\(  \\)", -3],
+  "x": ["\\[\n\n\\]", -3],
+  "c": ["\\includegraphics{/files/0/}", -1 ],
   "v": ["\\section*{}", -1],
   "b": ["\\textbf{}", -1],
   "n": ["\\textit{}", -1],
   "m": ["\\href{}{}", -3],
 
-  "o": ["\\begin{minipage}[t]{375px}\n\n\\end{minipage}\n" +
-        "\\hspace{10px}\n" +
-        "\\begin{minipage}[t]{375px}\n\n\\end{minipage}", 0],
-  "p": ["\\begin{minipage}[t]{248px}\n\n\\end{minipage}\n" +
-        "\\hspace{8px}\n" +
-        "\\begin{minipage}[t]{248px}\n\n\\end{minipage}\n" +
-        "\\hspace{8px}\n" +
-        "\\begin{minipage}[t]{248px}\n\n\\end{minipage}", 0],
+  // begin-env
+  "1": ["\\begin{}\n\n\\end{}", -9],
+  "2": ["\\begin{itemize}\n\\item \n\\end{itemize}\n", -15],
+  "3": ["\\begin{enumerate}\n\\item \n\\end{enumerate}\n", -17],
+  "4": ["\\begin{minipage}[t]{375px}\n\n\\end{minipage}\n", -16],
+  "5": ["\\begin{center}\n\n\\end{center}\n", -14],
 };