~mro/geohash

f1edfa8f7dd7cd60e1f88cb7b93c2b7a1517fdea — Marcus Rohrmoser 2 years ago 8bcea42
CSP friendly without unsafe-inline but separate files.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/style-src
8 files changed, 124 insertions(+), 115 deletions(-)

M Makefile
M bin/cgi.ml
A res/doap2html.css
A res/doap2html.js
M res/doap2html.xslt
A res/gpx2html.css
A res/gpx2html.js
M res/gpx2html.xslt
M Makefile => Makefile +2 -2
@@ 6,8 6,8 @@ dst := _build/geohash-$(os)-$(cpu)-$(ver).cgi

final: build $(dst)

lib/res.ml:	res res/doap.rdf res/doap2html.xslt
	find res -name .DS_Store -delete
lib/res.ml:	res res/doap.rdf res/doap2html.xslt res/doap2html.css res/doap2html.js res/gpx2html.xslt res/gpx2html.css res/gpx2html.js
	find $< -name .DS_Store -delete
	# opam install ocp-ocamlres
	ocp-ocamlres -format ocaml $< -o $@


M bin/cgi.ml => bin/cgi.ml +4 -0
@@ 64,7 64,11 @@ let handle oc req =
      | "/about" -> dump_clob oc "text/xml" Res.doap_rdf
      | "/LICENSE" -> dump_clob oc "text/plain" Res._LICENSE
      | "/doap2html.xslt" -> dump_clob oc "text/xml" Res.doap2html_xslt
      | "/doap2html.css" -> dump_clob oc "text/css; charset=utf-8" Res.doap2html_css
      | "/doap2html.js" -> dump_clob oc "text/javascript; charset=utf-8" Res.doap2html_js
      | "/gpx2html.xslt" -> dump_clob oc "text/xml" Res.gpx2html_xslt
      | "/gpx2html.css" -> dump_clob oc "text/css; charset=utf-8" Res.gpx2html_css
      | "/gpx2html.js" -> dump_clob oc "text/javascript; charset=utf-8" Res.gpx2html_js
      | "" -> uri ^ "/" |> redirect oc
      | "/" -> (
          match req.query_string with

A res/doap2html.css => res/doap2html.css +38 -0
@@ 0,0 1,38 @@
html {
  font-family: sans-serif;
  background:  hsl(30, 50%, 93%);
  color:       hsl(30, 50%, 44%);
}
body {
  margin: auto;
  max-width: 43rem;
}
a, a:visited {
  color: hsl(115, 50%, 35%);
  text-decoration: none;
}
h1 > a, h2 > a {
  font-size:   60%;
  vertical-align: baseline;
  position:   relative;
  top:        -0.7em;
}
#sep {
  text-align: center;
}
#poweredby {
  font-size:  80%;
  color:      #888;
  border-top: 2px solid darkgrey;
  padding-top: 1ex;
}
p[lang="en"]::after { content: "🇬🇧"; }
pre[lang="en"]::before { content: "🇬🇧"; }
p[lang="fr"]::after { content: "🇫🇷"; }

@media (prefers-color-scheme: dark) {
  html {
    background: hsl(30, 20%, 23%);
  }
}


A res/doap2html.js => res/doap2html.js +2 -0
@@ 0,0 1,2 @@
document.getElementById('my-url').innerText = location.href;


M res/doap2html.xslt => res/doap2html.xslt +2 -45
@@ 51,47 51,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
  <style type="text/css">
/*<![CDATA[*/
  html {
    font-family: sans-serif;
    background:  hsl(30, 50%, 93%);
    color:       hsl(30, 50%, 44%);
  }
  body {
    margin: auto;
    max-width: 43rem;
  }
  a, a:visited {
    color: hsl(115, 50%, 35%);
    text-decoration: none;
  }
  h1 > a, h2 > a {
    font-size:   60%;
    vertical-align: baseline;
    position:   relative;
    top:        -0.7em;
  }
  #sep {
    text-align: center;
  }
  #poweredby {
    font-size:  80%;
    color:      #888;
    border-top: 2px solid darkgrey;
    padding-top: 1ex;
  }
  p[lang="en"]::after { content: "🇬🇧"; }
  pre[lang="en"]::before { content: "🇬🇧"; }
  p[lang="fr"]::after { content: "🇫🇷"; }

  @media (prefers-color-scheme: dark) {
    html {
      background: hsl(30, 20%, 23%);
    }
  }
  /*]]>*/
  </style>
  <link href="./doap2html.css" rel="stylesheet" type="text/css"/>

  <title><xsl:value-of select="doap:name"/> – DOAP</title>
</head>


@@ 193,10 153,7 @@

  <p id="poweredby">RDF (<a href="https://en.wikipedia.org/wiki/DOAP">DOAP</a>): <tt>$ <a href=
  "http://librdf.org/raptor/rapper.html">rapper</a> --guess --output turtle '<span id=
  "my-url">https://example.com/url-to-here</span>'</tt></p><script type="text/javascript">
//<![CDATA[
  document.getElementById('my-url').innerText = location.href;
  //]]>
  "my-url">https://example.com/url-to-here</span>'</tt></p><script src="./doap2html.js" type="text/javascript">
  </script>
</body>
</html>

A res/gpx2html.css => res/gpx2html.css +40 -0
@@ 0,0 1,40 @@
body {
  background: #ddd;
  margin: 0;
}
iframe {
  background: #ddd;
  border:0;
  height:100%;
  position:fixed; /* https://stackoverflow.com/a/2425694/349514 */
  width:100%;
}
form {
  left: 58px;
  line-height: 4ex;
  position: absolute;
  top: 4px;
  width: calc(100% - 59px);
}
input {
  background: white;
}
small {
  /* https://alligator.io/css/prevent-line-break/ */
  background-color: hsla(0,0%,100%,0.75);
  border: 2px solid #ccc;
  overflow: hidden;
  padding: 1.1ex;
  text-overflow: ellipsis;
  white-space: nowrap;
}
input,button {
  font-family: monospace;
  font-size: 10pt;
  padding: 0.8ex 1.5ex;
}
input,button,small {
  border-radius: 1ex;
  margin: 1.2ex;
}


A res/gpx2html.js => res/gpx2html.js +32 -0
@@ 0,0 1,32 @@
/* MIT License https://github.com/joliss/js-string-escape/blob/master/index.js */
function jsStringEscape(string) {
  return ('' + string).replace(/["'\\\n\r\u2028\u2029]/g, function (character) {
    // Escape all characters not included in SingleStringCharacters and
    // DoubleStringCharacters on
    // http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
    switch (character) {
      case '"':
      case "'":
      case '\\':
        return '\\' + character
      // Four possible LineTerminator characters need to be escaped:
      case '\n':
        return '\\n'
      case '\r':
        return '\\r'
      case '\u2028':
        return '\\u2028'
      case '\u2029':
        return '\\u2029'
    }
  })
}

document.onreadystatechange = function () {
  var a = document.getElementById("bookmarklet");
  a.href = a.href.replace("}('../'));", "}('"+jsStringEscape(window.location)+"/../'));");

  // load iframe content once it's size is set - mitigate FOUC https://stackoverflow.com/a/57675785/349514
  var f = document.getElementById('ifrm_map');
  f.src = f.dataset.src;
}

M res/gpx2html.xslt => res/gpx2html.xslt +4 -68
@@ 100,7 100,8 @@ function(bas)
      <xsl:variable name="wpt" select="g:wpt[1]"/>

      <body>
        <iframe src="https://www.openstreetmap.org/export/embed.html?bbox={$bbox/@minlon},{$bbox/@minlat},{$bbox/@maxlon},{$bbox/@maxlat}&amp;marker={$wpt/@lat},{$wpt/@lon}"/>
        <!-- defer until external (CSP!) css is ready -->
        <iframe id="ifrm_map" data-src="https://www.openstreetmap.org/export/embed.html?bbox={$bbox/@minlon},{$bbox/@minlat},{$bbox/@maxlon},{$bbox/@maxlat}&amp;marker={$wpt/@lat},{$wpt/@lon}"/>

        <form action="." id="search_form" name="search_form">
          <input name="q" placeholder="geo:lat,lon" size="24" value="geo:{$wpt/@lat},{$wpt/@lon}" autofocus="autofocus" />


@@ 125,33 126,7 @@ function(bas)
          <xsl:text> </xsl:text>
          <small>
            <a id="bookmarklet" href="{$Geo2Rdf}"><xsl:value-of select="$globe"/> Geohash Bookmarklet</a>
            <script>
/* MIT License https://github.com/joliss/js-string-escape/blob/master/index.js */
function jsStringEscape(string) {
  return ('' + string).replace(/["'\\\n\r\u2028\u2029]/g, function (character) {
    // Escape all characters not included in SingleStringCharacters and
    // DoubleStringCharacters on
    // http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
    switch (character) {
      case '"':
      case "'":
      case '\\':
        return '\\' + character
      // Four possible LineTerminator characters need to be escaped:
      case '\n':
        return '\\n'
      case '\r':
        return '\\r'
      case '\u2028':
        return '\\u2028'
      case '\u2029':
        return '\\u2029'
    }
  })
}
              var a = document.getElementById("bookmarklet");
              a.href = a.href.replace("}('../'));", "}('"+jsStringEscape(window.location)+"/../'));");
            </script>
            <script defer="defer" src="./gpx2html.js"/>
          </small>
        </form>
      </body>


@@ 168,46 143,7 @@ function jsStringEscape(string) {
      <meta name="viewport" content="width=device-width,initial-scale=1.0"/>

      <title>#<xsl:value-of select="$globe"/> Geohash</title>
      <style type="text/css">
/*<![CDATA[*/
  body {
    margin: 0;
  }
  iframe {
    border:0;
    height:100%;
    position:fixed; /* https://stackoverflow.com/a/2425694/349514 */
    width:100%;
  }
  form {
    left: 58px;
    line-height: 4ex;
    position: absolute;
    top: 4px;
    width: calc(100% - 59px);
  }
  input {
    background: white;
  }
  small {
    /* https://alligator.io/css/prevent-line-break/ */
    background-color: hsla(0,0%,100%,0.75);
    border: 2px solid #ccc;
    overflow: hidden;
    padding: 1.1ex;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  input,button {
    font-family: monospace;
    font-size: 10pt;
    padding: 0.8ex 1.5ex;
  }
  input,button,small {
    border-radius: 1ex;
    margin: 1.2ex;
  }
  /*]]>*/      </style>
      <link href="./gpx2html.css" rel="stylesheet" type="text/css"/>
    </head>
  </xsl:template>
</xsl:stylesheet>