web-rep 0.14.0.0 → 0.14.0.1
raw patch · 6 files changed
+43/−184 lines, 6 filesdep −doctest-paralleldep −string-interpolate
Dependencies removed: doctest-parallel, string-interpolate
Files
- src/Web/Rep/Examples.hs +19/−25
- src/Web/Rep/Page.hs +2/−19
- src/Web/Rep/SharedReps.hs +9/−8
- src/Web/Rep/Socket.hs +11/−112
- test/doctests.hs +0/−8
- web-rep.cabal +2/−12
src/Web/Rep/Examples.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE ApplicativeDo #-} {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-} {-# OPTIONS_GHC -Wno-name-shadowing #-} -- | Some simple usage examples to get started with the library.@@ -20,7 +19,6 @@ where import Data.ByteString (ByteString)-import Data.String.Interpolate import FlatParse.Basic (takeRest) import GHC.Generics import MarkupParse@@ -72,34 +70,30 @@ css1 :: Css css1 = Css- [i|-{- font-size : 10px;- font-family : "Arial","Helvetica", sans-serif;-}--\#btnGo-{- margin-top : 20px;- margin-bottom : 20px;-}--\#btnGo.on-{- color : \#008000;-}-|]+ "{\n\+ \ font-size : 10px;\n\+ \ font-family : \"Arial\",\"Helvetica\", sans-serif;\n\+ \}\n\+ \\n\+ \#btnGo\n\+ \{\n\+ \ margin-top : 20px;\n\+ \ margin-bottom : 20px;\n\+ \}\n\+ \\n\+ \#btnGo.on\n\+ \{\n\+ \ color : #008000;\n\+ \}" -- js click :: Js click = Js- [i|-$('\#btnGo').click( function() {- $('\#btnGo').toggleClass('on');- alert('bada bing!');-});-|]+ "$('#btnGo').click( function() {\n\+ \ $('#btnGo').toggleClass('on');\n\+ \ alert('bada bing!');\n\+ \});" button1 :: Markup button1 =
src/Web/Rep/Page.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-} {-# OPTIONS_GHC -fno-warn-incomplete-patterns #-} -- | Representations of a web page, covering Html, CSS & JS artifacts.@@ -23,7 +22,6 @@ import Data.ByteString (ByteString) import Data.ByteString.Char8 qualified as C-import Data.String.Interpolate import GHC.Generics import MarkupParse import Optics.Core@@ -137,26 +135,11 @@ cssColorScheme :: Css cssColorScheme = Css- [i|-{- color-scheme: light dark;-}-{- body {- background-color: rgb(92%, 92%, 92%);- color: rgb(5%, 5%, 5%);- }-}-@media (prefers-color-scheme:dark) {- body {- background-color: rgb(5%, 5%, 5%);- color: rgb(92%, 92%, 92%);- }-}|]+ "{\n color-scheme: light dark;\n}\n{\n body {\n background-color: rgb(92%, 92%, 92%);\n color: rgb(5%, 5%, 5%);\n }\n}\n@media (prefers-color-scheme:dark) {\n body {\n background-color: rgb(5%, 5%, 5%);\n color: rgb(92%, 92%, 92%);\n }\n}" -- | Javascript as string newtype Js = Js {jsByteString :: ByteString} deriving (Eq, Show, Generic, Semigroup, Monoid) -- | Add the windows.onload assignment onLoad :: Js -> Js-onLoad (Js t) = Js [i| window.onload=function(){#{t}};|]+onLoad (Js t) = Js (" window.onload=function(){" <> t <> "};")
@@ -1,7 +1,6 @@ {-# LANGUAGE ApplicativeDo #-} {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-} {-# OPTIONS_GHC -Wno-name-shadowing #-} {-# OPTIONS_GHC -Wno-x-partial #-} @@ -48,7 +47,6 @@ import Data.HashMap.Strict qualified as HashMap import Data.List qualified as List import Data.Maybe-import Data.String.Interpolate import FlatParse.Basic hiding (take) import MarkupParse import Optics.Core hiding (element)@@ -366,12 +364,15 @@ elementc "script" []- [i|-$('\##{checkName}').on('change', (function(){- var vis = this.checked ? "block" : "none";- document.getElementById("#{toggleId}").style.display = vis;-}));-|]+ ( "$('#"+ <> checkName+ <> "').on('change', (function(){"+ <> " var vis = this.checked ? \"block\" : \"none\";"+ <> " document.getElementById(\""+ <> toggleId+ <> "\").style.display = vis;"+ <> "}));"+ ) -- | A (fixed-size) list represented in html as an accordion card -- A major restriction of the library is that a 'SharedRep' does not have a Monad instance. In practice, this means that the external representation of lists cannot have a dynamic size.
src/Web/Rep/Socket.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -Wno-name-shadowing #-} @@ -54,7 +53,6 @@ import Data.Functor.Contravariant import Data.HashMap.Strict as HashMap import Data.Profunctor-import Data.String.Interpolate import Data.Text (Text) import Data.Text.Encoding import FlatParse.Basic@@ -278,11 +276,11 @@ -- | {"event":{"element":"textid","value":"abcdees"}} parserJ :: Parser e (ByteString, ByteString) parserJ = do- _ <- $(string [i|{"event":{"element":"|])+ _ <- $(string "{\"event\":{\"element\":\"") e <- byteStringOf $ some (satisfy (/= '"'))- _ <- $(string [i|","value":"|])+ _ <- $(string "\",\"value\":\"") v <- byteStringOf $ some (satisfy (/= '"'))- _ <- $(string [i|"}}|])+ _ <- $(string "\"}}") pure (e, v) -- * code hooks@@ -308,31 +306,21 @@ -- | write to the console console :: ByteString -> ByteString-console t = [i| console.log(#{t}) |]+console t = " console.log(" <> t <> ") " -- | send arbitrary byestrings. val :: ByteString -> ByteString-val t = [i| jsb.ws.send(#{t}) |]+val t = " jsb.ws.send(" <> t <> ") " -- | replace a container and run any embedded scripts replace :: ByteString -> ByteString -> ByteString replace d t =- [i|- var $container = document.getElementById('#{d}');- $container.innerHTML = '#{clean t}';- runScripts($container);- refreshJsb();- |]+ "\n var $container = document.getElementById('" <> d <> "');\n $container.innerHTML = '" <> clean t <> "';\n runScripts($container);\n refreshJsb();\n " -- | append to a container and run any embedded scripts append :: ByteString -> ByteString -> ByteString append d t =- [i|- var $container = document.getElementById('#{d}');- $container.innerHTML += '#{clean t}';- runScripts($container);- refreshJsb();- |]+ "\n var $container = document.getElementById('" <> d <> "');\n $container.innerHTML += '" <> clean t <> "';\n runScripts($container);\n refreshJsb();\n " -- | Double backslash newline and single quotes. clean :: ByteString -> ByteString@@ -348,15 +336,7 @@ webSocket :: Js webSocket = Js- [i|-window.jsb = {ws: new WebSocket('ws://' + location.host + '/')};-jsb.event = function(ev) {- jsb.ws.send(JSON.stringify({event: ev}));-};-jsb.ws.onmessage = function(evt){ - eval('(function(){' + evt.data + '})()');-};-|]+ "\nwindow.jsb = {ws: new WebSocket('ws://' + location.host + '/')};\njsb.event = function(ev) {\n jsb.ws.send(JSON.stringify({event: ev}));\n};\njsb.ws.onmessage = function(evt){ \n eval('(function(){' + evt.data + '})()'); \n};\n" -- * scripts @@ -364,68 +344,13 @@ refreshJsbJs :: Js refreshJsbJs = Js- [i|-function refreshJsb () {- $('.jsbClassEventInput').off('input');- $('.jsbClassEventInput').on('input', (function(){- jsb.event({ 'element': this.id, 'value': this.value});- }));- $('.jsbClassEventChange').off('change');- $('.jsbClassEventChange').on('change', (function(){- jsb.event({ 'element': this.id, 'value': this.value});- }));- $('.jsbClassEventFocusout').off('focusout');- $('.jsbClassEventFocusout').on('focusout', (function(){- jsb.event({ 'element': this.id, 'value': this.value});- }));- $('.jsbClassEventButton').off('click');- $('.jsbClassEventButton').on('click', (function(){- jsb.event({ 'element': this.id, 'value': this.value});- }));- $('.jsbClassEventToggle').off('click');- $('.jsbClassEventToggle').on('click', (function(){- jsb.event({ 'element': this.id, 'value': ('true' !== this.getAttribute('aria-pressed')).toString()});- }));- $('.jsbClassEventCheckbox').off('click');- $('.jsbClassEventCheckbox').on('click', (function(){- jsb.event({ 'element': this.id, 'value': this.checked.toString()});- }));- $('.jsbClassEventChooseFile').off('input');- $('.jsbClassEventChooseFile').on('input', (function(){- jsb.event({ 'element': this.id, 'value': this.files[0].name});- }));- $('.jsbClassEventShowSum').off('change');- $('.jsbClassEventShowSum').on('change', (function(){- var v = this.value;- $(this).parent('.sumtype-group').siblings('.subtype').each(function(i) {- if (this.dataset.sumtype === v) {- this.style.display = 'block';- } else {- this.style.display = 'none';- }- })- }));- $('.jsbClassEventChangeMultiple').off('change');- $('.jsbClassEventChangeMultiple').on('change', (function(){- jsb.event({ 'element': this.id, 'value': [...this.options].filter(option => option.selected).map(option => option.value).join(',')});- }));-};-|]+ "\nfunction refreshJsb () {\n $('.jsbClassEventInput').off('input');\n $('.jsbClassEventInput').on('input', (function(){\n jsb.event({ 'element': this.id, 'value': this.value});\n }));\n $('.jsbClassEventChange').off('change');\n $('.jsbClassEventChange').on('change', (function(){\n jsb.event({ 'element': this.id, 'value': this.value});\n }));\n $('.jsbClassEventFocusout').off('focusout');\n $('.jsbClassEventFocusout').on('focusout', (function(){\n jsb.event({ 'element': this.id, 'value': this.value});\n }));\n $('.jsbClassEventButton').off('click');\n $('.jsbClassEventButton').on('click', (function(){\n jsb.event({ 'element': this.id, 'value': this.value});\n }));\n $('.jsbClassEventToggle').off('click');\n $('.jsbClassEventToggle').on('click', (function(){\n jsb.event({ 'element': this.id, 'value': ('true' !== this.getAttribute('aria-pressed')).toString()});\n }));\n $('.jsbClassEventCheckbox').off('click');\n $('.jsbClassEventCheckbox').on('click', (function(){\n jsb.event({ 'element': this.id, 'value': this.checked.toString()});\n }));\n $('.jsbClassEventChooseFile').off('input');\n $('.jsbClassEventChooseFile').on('input', (function(){\n jsb.event({ 'element': this.id, 'value': this.files[0].name});\n }));\n $('.jsbClassEventShowSum').off('change');\n $('.jsbClassEventShowSum').on('change', (function(){\n var v = this.value;\n $(this).parent('.sumtype-group').siblings('.subtype').each(function(i) {\n if (this.dataset.sumtype === v) {\n this.style.display = 'block';\n } else {\n this.style.display = 'none';\n }\n })\n }));\n $('.jsbClassEventChangeMultiple').off('change');\n $('.jsbClassEventChangeMultiple').on('change', (function(){\n jsb.event({ 'element': this.id, 'value': [...this.options].filter(option => option.selected).map(option => option.value).join(',')});\n }));\n};\n" -- | prevent the Enter key from triggering an event preventEnter :: Js preventEnter = Js- [i|-window.addEventListener('keydown',function(e) {- if(e.keyIdentifier=='U+000A' || e.keyIdentifier=='Enter' || e.keyCode==13) {- if(e.target.nodeName=='INPUT' && e.target.type !== 'textarea') {- e.preventDefault();- return false;- }- }-}, true);-|]+ "\nwindow.addEventListener('keydown',function(e) {\n if(e.keyIdentifier=='U+000A' || e.keyIdentifier=='Enter' || e.keyCode==13) {\n if(e.target.nodeName=='INPUT' && e.target.type !== 'textarea') {\n e.preventDefault();\n return false;\n }\n }\n}, true);\n" -- | script injection js. --@@ -433,33 +358,7 @@ runScriptJs :: Js runScriptJs = Js- [i|-function insertScript ($script) {- var s = document.createElement('script')- s.type = 'text/javascript'- if ($script.src) {- s.onload = callback- s.onerror = callback- s.src = $script.src- } else {- s.textContent = $script.innerText- }-- // re-insert the script tag so it executes.- document.head.appendChild(s)-- // clean-up- $script.parentNode.removeChild($script)-}--function runScripts ($container) {- // get scripts tags from a node- var $scripts = $container.querySelectorAll('script')- $scripts.forEach(function ($script) {- insertScript($script)- })-}-|]+ "\nfunction insertScript ($script) {\n var s = document.createElement('script')\n s.type = 'text/javascript'\n if ($script.src) {\n s.onload = callback\n s.onerror = callback\n s.src = $script.src\n } else {\n s.textContent = $script.innerText\n }\n\n // re-insert the script tag so it executes.\n document.head.appendChild(s)\n\n // clean-up\n $script.parentNode.removeChild($script)\n}\n\nfunction runScripts ($container) {\n // get scripts tags from a node\n var $scripts = $container.querySelectorAll('script')\n $scripts.forEach(function ($script) {\n insertScript($script)\n })\n}\n" -- | Run a Parser, throwing away leftovers. Returns Left on 'Fail' or 'Err'. runParserEither :: Parser ByteString a -> ByteString -> Either ByteString a
− test/doctests.hs
@@ -1,8 +0,0 @@-module Main where--import System.Environment (getArgs)-import Test.DocTest (mainFromCabal)-import Prelude (IO, (=<<))--main :: IO ()-main = mainFromCabal "web-rep" =<< getArgs
web-rep.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: web-rep-version: 0.14.0.0+version: 0.14.0.1 license: BSD-3-Clause license-file: LICENSE copyright: Tony Day (c) 2015@@ -19,6 +19,7 @@ ghc ==9.8.4 ghc ==9.10.2 ghc ==9.12.2+ ghc ==9.14.1 extra-doc-files: ChangeLog.md@@ -84,7 +85,6 @@ optics-extra >=0.4 && <0.5, profunctors >=5.6.2 && <5.7, scotty >=0.11.5 && <0.23,- string-interpolate >=0.3 && <0.4, text >=1.2 && <2.2, transformers >=0.5.6 && <0.6.2, unordered-containers >=0.2 && <0.3,@@ -120,13 +120,3 @@ optparse-applicative >=0.17 && <0.20, web-rep, -test-suite doctests- import: ghc2024-stanza- main-is: doctests.hs- hs-source-dirs: test- build-depends:- base >=4.14 && <5,- doctest-parallel >=0.3 && <0.5,-- ghc-options: -threaded- type: exitcode-stdio-1.0