summaryrefslogtreecommitdiff
path: root/talermerchantdemos/blog/static
diff options
context:
space:
mode:
authorMS <ms@taler.net>2020-07-22 14:53:45 +0200
committerMS <ms@taler.net>2020-07-22 14:53:45 +0200
commit2d97ecc2c1ac605ca49e8a866b309daaeb7a831c (patch)
tree173f7917c5d0af822d2d51ed491c3cf2d8eaf23f /talermerchantdemos/blog/static
downloadtaler-merchant-demos-2d97ecc2c1ac605ca49e8a866b309daaeb7a831c.tar.gz
taler-merchant-demos-2d97ecc2c1ac605ca49e8a866b309daaeb7a831c.tar.bz2
taler-merchant-demos-2d97ecc2c1ac605ca49e8a866b309daaeb7a831c.zip
Installing the Blog
Diffstat (limited to 'talermerchantdemos/blog/static')
-rw-r--r--talermerchantdemos/blog/static/web-common/.gitignore6
-rw-r--r--talermerchantdemos/blog/static/web-common/AUTHORS2
-rw-r--r--talermerchantdemos/blog/static/web-common/Makefile.am51
-rw-r--r--talermerchantdemos/blog/static/web-common/Makefile_Guix5
-rw-r--r--talermerchantdemos/blog/static/web-common/chrome-any.d.ts2
-rw-r--r--talermerchantdemos/blog/static/web-common/demo.css69
-rw-r--r--talermerchantdemos/blog/static/web-common/dropdown-navbar.html51
-rw-r--r--talermerchantdemos/blog/static/web-common/dropdown-navbar_script.js60
-rw-r--r--talermerchantdemos/blog/static/web-common/dropdown-navbar_style.css58
-rw-r--r--talermerchantdemos/blog/static/web-common/favicon-taler.icobin0 -> 1150 bytes
-rw-r--r--talermerchantdemos/blog/static/web-common/lang.css29
-rw-r--r--talermerchantdemos/blog/static/web-common/lang.html29
-rw-r--r--talermerchantdemos/blog/static/web-common/lang.js69
-rw-r--r--talermerchantdemos/blog/static/web-common/licensing.rst2
-rw-r--r--talermerchantdemos/blog/static/web-common/logo-2015-medium.pngbin0 -> 75094 bytes
-rw-r--r--talermerchantdemos/blog/static/web-common/pure.css1508
-rw-r--r--talermerchantdemos/blog/static/web-common/style.css198
-rw-r--r--talermerchantdemos/blog/static/web-common/taler-fallback.css15
-rw-r--r--talermerchantdemos/blog/static/web-common/taler-wallet-lib.js446
-rw-r--r--talermerchantdemos/blog/static/web-common/taler-wallet-lib.ts488
-rw-r--r--talermerchantdemos/blog/static/web-common/tsconfig.json17
21 files changed, 3105 insertions, 0 deletions
diff --git a/talermerchantdemos/blog/static/web-common/.gitignore b/talermerchantdemos/blog/static/web-common/.gitignore
new file mode 100644
index 0000000..e64e5c1
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/.gitignore
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+# these should be commited pre-build to the repo, too:
+#taler-wallet-lib.js
+
+.idea
diff --git a/talermerchantdemos/blog/static/web-common/AUTHORS b/talermerchantdemos/blog/static/web-common/AUTHORS
new file mode 100644
index 0000000..7f16bf5
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/AUTHORS
@@ -0,0 +1,2 @@
+Florian Dold
+Marcello Stanisci
diff --git a/talermerchantdemos/blog/static/web-common/Makefile.am b/talermerchantdemos/blog/static/web-common/Makefile.am
new file mode 100644
index 0000000..71c1d68
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/Makefile.am
@@ -0,0 +1,51 @@
+# This Makefile.am is in the public domain
+EXTRA_DIST = \
+ style.css \
+ taler-wallet-lib.ts \
+ chrome-any.d.ts \
+ logo-2015-medium.png \
+ licensing.rst \
+ favicon-taler.ico \
+ dropdown-navbar.html \
+ lang.html \
+ lang.css \
+ lang.js \
+ dropdown-navbar_style.css \
+ dropdown-navbar_script.js \
+ taler-fallback.css \
+ tsconfig.json \
+ AUTHORS
+
+# Some file may be missing here...
+pkgdata_DATA = \
+ dropdown-navbar.html \
+ favicon-taler.ico \
+ logo-2015-medium.png \
+ style.css \
+ taler-wallet-lib.js
+
+all: taler-wallet-lib.min.js taler-wallet-lib.js.tar.gz taler-wallet-lib.js
+
+%.min.js: %.js
+ python3 -m jsmin $< > $@
+
+%.js.tar.gz: %.js
+ tar czf $@ $<
+
+taler-wallet-lib.js: taler-wallet-lib.ts chrome-any.d.ts
+if HAVE_TSC
+ tsc
+else
+ @echo
+ @if [ ! -f "$@" ] ; then \
+ echo "*** Error: typescript compiler 'tsc' is missing," \
+ "can't build" $@ ; \
+ echo ; \
+ exit 1 ; \
+ else \
+ echo "*** Warning: typescript compiler 'tsc' is missing," \
+ "using pre-build" $@ ; \
+ touch "$@"; \
+ fi
+ @echo
+endif
diff --git a/talermerchantdemos/blog/static/web-common/Makefile_Guix b/talermerchantdemos/blog/static/web-common/Makefile_Guix
new file mode 100644
index 0000000..43dfe04
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/Makefile_Guix
@@ -0,0 +1,5 @@
+# This Makefile is used to update the compiled JavaScripts
+# put under versioning for making Guix packaging easier
+
+all:
+ tsc taler-wallet-lib.ts
diff --git a/talermerchantdemos/blog/static/web-common/chrome-any.d.ts b/talermerchantdemos/blog/static/web-common/chrome-any.d.ts
new file mode 100644
index 0000000..b621337
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/chrome-any.d.ts
@@ -0,0 +1,2 @@
+// We don't really need the full declaration here, so just include this.
+declare let chrome: any;
diff --git a/talermerchantdemos/blog/static/web-common/demo.css b/talermerchantdemos/blog/static/web-common/demo.css
new file mode 100644
index 0000000..b2688ad
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/demo.css
@@ -0,0 +1,69 @@
+/* style common to all demo pages */
+
+.demobar h1 {
+ text-align: center;
+}
+.demobar > p {
+ padding: 0.5em;
+}
+.demobar a,
+.demobar a:visited {
+ color: inherit;
+}
+.adorn-brackets::before {
+ content: "❬";
+ color: #aa3939;
+}
+.adorn-brackets::after {
+ content: "❭";
+ color: #aa3939;
+}
+.tt {
+ font-family: 'Lucida Console', Monaco, monospace;
+}
+
+.informational-ok {
+ background: lightgreen;
+ border-radius: 1em;
+ padding: 0.5em;
+}
+
+.informational-fail {
+ background: lightpink;
+ border-radius: 1em;
+ padding: 0.5em;
+}
+
+.content {
+ overflow-x: auto;
+}
+.demobar {
+ overflow-x: auto;
+ background-color: #033;
+ color: white;
+}
+
+body {
+ overflow-y: scroll;
+}
+
+@media (min-width: 500px) {
+ .content {
+ margin-left: 25%;
+ padding-left: 2em;
+ margin-right: 1em;
+ overflow-x: auto;
+ }
+ .demobar {
+ height: 100%;
+ margin: 0;
+ top: 0;
+ left: 0;
+ background-color: #033;
+ color: white;
+ position: fixed;
+ width: 25%;
+ padding-right: 1em;
+ overflow: auto;
+ }
+}
diff --git a/talermerchantdemos/blog/static/web-common/dropdown-navbar.html b/talermerchantdemos/blog/static/web-common/dropdown-navbar.html
new file mode 100644
index 0000000..7d28787
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/dropdown-navbar.html
@@ -0,0 +1,51 @@
+<!--
+ This file is part of GNU TALER.
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+-->
+
+<!--
+ URLs here use the special redir_app_rel path, which is rewritten by the
+ webserver to be relative to the current application mount path, no matter
+ where it occurs in the path.
+-->
+
+<div>
+ <link rel="stylesheet" type="text/css" href="redir_app_rel/static/web-common/dropdown-navbar_style.css">
+ <script src="redir_app_rel/static/web-common/dropdown-navbar_script.js" type="application/javascript"></script>
+ <div class="dropdown-navbar">
+ <ul>
+ <li>
+ <a href="https://taler.net/" class="m_index btn btn-outline-success" id="home" target="_blank">Home</a>
+ </li>
+ <li>
+ <a href="redir_app_rel/landing" class="m_demo" id="demo">Instructions</a>
+ </li>
+ <li>
+ <a href="javascript:void(0)" class="dropbtn" id="bank">Bank</a>
+ <div class="dropdown-content bank">
+ <a href="redir_app_rel/bank">Main</a>
+ <a href="redir_app_rel/bank/public-accounts">Public accounts</a>
+ </div>
+ </li>
+ <li>
+ <a href="redir_app_rel/shop" class="m_demo" id="shop">Shop</a>
+ </li>
+ <li>
+ <a href="redir_app_rel/donations" class="m_demo" id="donations">Donations</a>
+ </li>
+ </ul>
+ </div>
+</div>
diff --git a/talermerchantdemos/blog/static/web-common/dropdown-navbar_script.js b/talermerchantdemos/blog/static/web-common/dropdown-navbar_script.js
new file mode 100644
index 0000000..65f6848
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/dropdown-navbar_script.js
@@ -0,0 +1,60 @@
+/* @licstart The following is the entire license notice for the
+ JavaScript code in this page.
+
+ Copyright (C) 2015, 2016 INRIA
+
+ The JavaScript code in this page is free software: you can
+ redistribute it and/or modify it under the terms of the GNU
+ Lesser General Public License (GNU LGPL) as published by the Free Software
+ Foundation, either version 2.1 of the License, or (at your option)
+ any later version. The code is distributed WITHOUT ANY WARRANTY;
+ without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU LGPL for more details.
+
+ As additional permission under GNU LGPL version 2.1 section 7, you
+ may distribute non-source (e.g., minimized or compacted) forms of
+ that code without the copy of the GNU LGPL normally required by
+ section 4, provided you include this license notice and a URL
+ through which recipients can access the Corresponding Source.
+
+ @licend The above is the entire license notice
+ for the JavaScript code in this page.
+
+ @author Marcello Stanisci
+*/
+window.onclick = function(e) {
+ var dropdowns = document.getElementsByClassName("dropdown-content");
+ if (!e.target.matches('.dropbtn')) {
+ for (var d = 0; d < dropdowns.length; d++) {
+ var openDropdown = dropdowns[d];
+ if (openDropdown.classList.contains('show')) {
+ openDropdown.classList.remove('show');
+ }
+ }
+ }
+ else{ // need to close other tabs belonging to tabs other than the clicked one
+ for (var d = 0; d < dropdowns.length; d++) {
+ var openDropdown = dropdowns[d];
+ if ((openDropdown.parentNode != e.target.parentNode)
+ && (openDropdown.classList.contains("show"))){
+ openDropdown.classList.remove('show');
+ }
+ }
+ }
+}
+/**
+ * Dropdown bar items which are supposed to. In the current
+ * configuration, only the 'bank' item shold dropdown.
+ */
+function injectOnclicks(){
+ var where = ["bank"];
+ for (i in where){
+ document.getElementById(where[i]).onclick = function(){
+ this.parentNode.children[1].classList.toggle("show");
+ }
+ }
+}
+function dropMenu(){
+
+}
+document.addEventListener('DOMContentLoaded', injectOnclicks);
diff --git a/talermerchantdemos/blog/static/web-common/dropdown-navbar_style.css b/talermerchantdemos/blog/static/web-common/dropdown-navbar_style.css
new file mode 100644
index 0000000..139115d
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/dropdown-navbar_style.css
@@ -0,0 +1,58 @@
+/*
+ This file is part of GNU TALER.
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+*/
+
+.dropdown-navbar ul {
+ border-radius: 5px;
+ overflow: hidden;
+ background-color: #F4F3F3;
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+ max-width: 40em;
+}
+.dropdown-navbar {
+ position: relative;
+ z-index: 1;
+ margin-left: 100px;
+}
+.dropdown-navbar li {
+ float: left;
+}
+.dropdown-content {
+ border-radius: 5px;
+ border-style: solid;
+ border-color: #B3B3B3;
+ border-width: 1px;
+ background-color: #F4F3F3;
+ position: absolute;
+ display: none;
+}
+.dropdown-navbar a {
+ font-weight: bold;
+ color: black;
+ text-decoration: none;
+ padding: 6px 8px;
+ display: inline-block;
+ position: relative;
+}
+.dropdown-content a {
+ display: block;
+}
+.show {
+ display: block;
+}
diff --git a/talermerchantdemos/blog/static/web-common/favicon-taler.ico b/talermerchantdemos/blog/static/web-common/favicon-taler.ico
new file mode 100644
index 0000000..141b93d
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/favicon-taler.ico
Binary files differ
diff --git a/talermerchantdemos/blog/static/web-common/lang.css b/talermerchantdemos/blog/static/web-common/lang.css
new file mode 100644
index 0000000..bae82f0
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/lang.css
@@ -0,0 +1,29 @@
+body.it :lang(en) {
+ display: none;
+}
+
+body.en :lang(it) {
+ display: none;
+}
+
+.bottom-bar {
+ background-color: #F8F8F8;
+ border-color: #E7E7E7;
+ bottom: 0px;
+ margin-bottom: 0px;
+ border-width: 1px 0px 0px;
+ position: fixed;
+ bottom: 0px;
+ min-height: 50px;
+ width: 100%;
+}
+
+.lang-selector {
+ padding-top: 14px;
+ padding-bottom: 10px;
+ margin-left: 50px;
+}
+
+.lang-selector a {
+ text-decoration: none;
+}
diff --git a/talermerchantdemos/blog/static/web-common/lang.html b/talermerchantdemos/blog/static/web-common/lang.html
new file mode 100644
index 0000000..8db1c0d
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/lang.html
@@ -0,0 +1,29 @@
+<div class="bottom-bar">
+ <div class="lang-selector">
+ <a href="#" onclick="setLang('en');">
+ <img>
+ <!-- https://upload.wikimedia.org/wikipedia/commons/a/ae/Flag_of_the_United_Kingdom.svg -->
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 30" width="45" height="22.5">
+ <clipPath id="t">
+ <path d="M30,15 h30 v15 z v15 h-30 z h-30 v-15 z v-15 h30 z"/>
+ </clipPath>
+ <path d="M0,0 v30 h60 v-30 z" fill="#00247d"/>
+ <path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" stroke-width="6"/>
+ <path d="M0,0 L60,30 M60,0 L0,30" clip-path="url(#t)" stroke="#cf142b" stroke-width="4"/>
+ <path d="M30,0 v30 M0,15 h60" stroke="#fff" stroke-width="10"/>
+ <path d="M30,0 v30 M0,15 h60" stroke="#cf142b" stroke-width="6"/>
+ </svg>
+ </img>
+ </a>
+ <a href="#" onclick="setLang('it');">
+ <img>
+ <!-- https://upload.wikimedia.org/wikipedia/en/0/03/Flag_of_Italy.svg -->
+ <svg xmlns="http://www.w3.org/2000/svg" width="45" height="22.5" viewBox="0 0 3 2">
+ <rect width="1" height="2" fill="#009246"/>
+ <rect width="1" height="2" x="1" fill="#fff"/>
+ <rect width="1" height="2" x="2" fill="#ce2b37"/>
+ </svg>
+ </img>
+ </a>
+ </div>
+</div>
diff --git a/talermerchantdemos/blog/static/web-common/lang.js b/talermerchantdemos/blog/static/web-common/lang.js
new file mode 100644
index 0000000..356a152
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/lang.js
@@ -0,0 +1,69 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * JavaScript code in this page.
+ *
+ * Copyright (C) 2014 GNUnet e.V.
+ *
+ * The JavaScript code in this page is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GNU GPL) as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option)
+ * any later version. The code is distributed WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
+ *
+ * As additional permission under GNU GPL version 3 section 7, you
+ * may distribute non-source (e.g., minimized or compacted) forms of
+ * that code without the copy of the GNU GPL normally required by
+ * section 4, provided you include this license notice and a URL
+ * through which recipients can access the Corresponding Source.
+ *
+ * @licend The above is the entire license notice
+ * for the JavaScript code in this page.
+ */
+
+// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3-or-Later
+
+function supports_html5_storage() {
+ try {
+ return 'sessionStorage' in window && window['sessionStorage'] !== null;
+ }
+ catch (e) {
+ return false;
+ }
+}
+
+function setLang(l)
+{
+ document.body.className=l;
+ if (supports_html5_storage()) {
+ sessionStorage.setItem('lang', l);
+ }
+ if (l == 'en') document.title = 'GNU Taler - Merchant demonstrator';
+ if (l == 'it') document.title = 'GNU Taler - Dimostrazione d\'acquisto';
+}
+
+function get_default_lang()
+{
+ langs = ['en', 'it'];
+ for (var i in navigator.languages) {
+ for (var j in langs) {
+ if (navigator.languages[i].indexOf(langs[j]) != -1)
+ return langs[j];
+ }
+ }
+ return 'en';
+}
+
+/*To be called on each onLoad*/
+function loadLang()
+{
+ l = null;
+ if (supports_html5_storage()) {
+ l = sessionStorage.getItem('lang');
+ }
+ if (!l)
+ l = get_default_lang();
+ setLang(l);
+}
+// @license-end
diff --git a/talermerchantdemos/blog/static/web-common/licensing.rst b/talermerchantdemos/blog/static/web-common/licensing.rst
new file mode 100644
index 0000000..05c13b9
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/licensing.rst
@@ -0,0 +1,2 @@
+The copyright of all the HTML/CSS/JavaScript files in this directory
+is owned by INRIA.
diff --git a/talermerchantdemos/blog/static/web-common/logo-2015-medium.png b/talermerchantdemos/blog/static/web-common/logo-2015-medium.png
new file mode 100644
index 0000000..78159bb
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/logo-2015-medium.png
Binary files differ
diff --git a/talermerchantdemos/blog/static/web-common/pure.css b/talermerchantdemos/blog/static/web-common/pure.css
new file mode 100644
index 0000000..7391139
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/pure.css
@@ -0,0 +1,1508 @@
+/*!
+Pure v0.6.2
+Copyright 2013 Yahoo!
+Licensed under the BSD License.
+https://github.com/yahoo/pure/blob/master/LICENSE.md
+*/
+/*!
+normalize.css v^3.0 | MIT License | git.io/normalize
+Copyright (c) Nicolas Gallagher and Jonathan Neal
+*/
+/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
+
+/**
+ * 1. Set default font family to sans-serif.
+ * 2. Prevent iOS and IE text size adjust after device orientation change,
+ * without disabling user zoom.
+ */
+
+html {
+ font-family: sans-serif; /* 1 */
+ -ms-text-size-adjust: 100%; /* 2 */
+ -webkit-text-size-adjust: 100%; /* 2 */
+}
+
+/**
+ * Remove default margin.
+ */
+
+body {
+ margin: 0;
+}
+
+/* HTML5 display definitions
+ ========================================================================== */
+
+/**
+ * Correct `block` display not defined for any HTML5 element in IE 8/9.
+ * Correct `block` display not defined for `details` or `summary` in IE 10/11
+ * and Firefox.
+ * Correct `block` display not defined for `main` in IE 11.
+ */
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+menu,
+nav,
+section,
+summary {
+ display: block;
+}
+
+/**
+ * 1. Correct `inline-block` display not defined in IE 8/9.
+ * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
+ */
+
+audio,
+canvas,
+progress,
+video {
+ display: inline-block; /* 1 */
+ vertical-align: baseline; /* 2 */
+}
+
+/**
+ * Prevent modern browsers from displaying `audio` without controls.
+ * Remove excess height in iOS 5 devices.
+ */
+
+audio:not([controls]) {
+ display: none;
+ height: 0;
+}
+
+/**
+ * Address `[hidden]` styling not present in IE 8/9/10.
+ * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.
+ */
+
+[hidden],
+template {
+ display: none;
+}
+
+/* Links
+ ========================================================================== */
+
+/**
+ * Remove the gray background color from active links in IE 10.
+ */
+
+a {
+ background-color: transparent;
+}
+
+/**
+ * Improve readability of focused elements when they are also in an
+ * active/hover state.
+ */
+
+a:active,
+a:hover {
+ outline: 0;
+}
+
+/* Text-level semantics
+ ========================================================================== */
+
+/**
+ * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
+ */
+
+abbr[title] {
+ border-bottom: 1px dotted;
+}
+
+/**
+ * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
+ */
+
+b,
+strong {
+ font-weight: bold;
+}
+
+/**
+ * Address styling not present in Safari and Chrome.
+ */
+
+dfn {
+ font-style: italic;
+}
+
+/**
+ * Address variable `h1` font-size and margin within `section` and `article`
+ * contexts in Firefox 4+, Safari, and Chrome.
+ */
+
+h1 {
+ font-size: 2em;
+ margin: 0.67em 0;
+}
+
+/**
+ * Address styling not present in IE 8/9.
+ */
+
+mark {
+ background: #ff0;
+ color: #000;
+}
+
+/**
+ * Address inconsistent and variable font size in all browsers.
+ */
+
+small {
+ font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` affecting `line-height` in all browsers.
+ */
+
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sup {
+ top: -0.5em;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+/* Embedded content
+ ========================================================================== */
+
+/**
+ * Remove border when inside `a` element in IE 8/9/10.
+ */
+
+img {
+ border: 0;
+}
+
+/**
+ * Correct overflow not hidden in IE 9/10/11.
+ */
+
+svg:not(:root) {
+ overflow: hidden;
+}
+
+/* Grouping content
+ ========================================================================== */
+
+/**
+ * Address margin not present in IE 8/9 and Safari.
+ */
+
+figure {
+ margin: 1em 40px;
+}
+
+/**
+ * Address differences between Firefox and other browsers.
+ */
+
+hr {
+ box-sizing: content-box;
+ height: 0;
+}
+
+/**
+ * Contain overflow in all browsers.
+ */
+
+pre {
+ overflow: auto;
+}
+
+/**
+ * Address odd `em`-unit font size rendering in all browsers.
+ */
+
+code,
+kbd,
+pre,
+samp {
+ font-family: monospace, monospace;
+ font-size: 1em;
+}
+
+/* Forms
+ ========================================================================== */
+
+/**
+ * Known limitation: by default, Chrome and Safari on OS X allow very limited
+ * styling of `select`, unless a `border` property is set.
+ */
+
+/**
+ * 1. Correct color not being inherited.
+ * Known issue: affects color of disabled elements.
+ * 2. Correct font properties not being inherited.
+ * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+ color: inherit; /* 1 */
+ font: inherit; /* 2 */
+ margin: 0; /* 3 */
+}
+
+/**
+ * Address `overflow` set to `hidden` in IE 8/9/10/11.
+ */
+
+button {
+ overflow: visible;
+}
+
+/**
+ * Address inconsistent `text-transform` inheritance for `button` and `select`.
+ * All other form control elements do not inherit `text-transform` values.
+ * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
+ * Correct `select` style inheritance in Firefox.
+ */
+
+button,
+select {
+ text-transform: none;
+}
+
+/**
+ * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
+ * and `video` controls.
+ * 2. Correct inability to style clickable `input` types in iOS.
+ * 3. Improve usability and consistency of cursor style between image-type
+ * `input` and others.
+ */
+
+button,
+html input[type="button"], /* 1 */
+input[type="reset"],
+input[type="submit"] {
+ -webkit-appearance: button; /* 2 */
+ cursor: pointer; /* 3 */
+}
+
+/**
+ * Re-set default cursor for disabled elements.
+ */
+
+button[disabled],
+html input[disabled] {
+ cursor: default;
+}
+
+/**
+ * Remove inner padding and border in Firefox 4+.
+ */
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+
+/**
+ * Address Firefox 4+ setting `line-height` on `input` using `!important` in
+ * the UA stylesheet.
+ */
+
+input {
+ line-height: normal;
+}
+
+/**
+ * It's recommended that you don't attempt to style these elements.
+ * Firefox's implementation doesn't respect box-sizing, padding, or width.
+ *
+ * 1. Address box sizing set to `content-box` in IE 8/9/10.
+ * 2. Remove excess padding in IE 8/9/10.
+ */
+
+input[type="checkbox"],
+input[type="radio"] {
+ box-sizing: border-box; /* 1 */
+ padding: 0; /* 2 */
+}
+
+/**
+ * Fix the cursor style for Chrome's increment/decrement buttons. For certain
+ * `font-size` values of the `input`, it causes the cursor style of the
+ * decrement button to change from `default` to `text`.
+ */
+
+input[type="number"]::-webkit-inner-spin-button,
+input[type="number"]::-webkit-outer-spin-button {
+ height: auto;
+}
+
+/**
+ * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
+ * 2. Address `box-sizing` set to `border-box` in Safari and Chrome.
+ */
+
+input[type="search"] {
+ -webkit-appearance: textfield; /* 1 */
+ box-sizing: content-box; /* 2 */
+}
+
+/**
+ * Remove inner padding and search cancel button in Safari and Chrome on OS X.
+ * Safari (but not Chrome) clips the cancel button when the search input has
+ * padding (and `textfield` appearance).
+ */
+
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/**
+ * Define consistent border, margin, and padding.
+ */
+
+fieldset {
+ border: 1px solid #c0c0c0;
+ margin: 0 2px;
+ padding: 0.35em 0.625em 0.75em;
+}
+
+/**
+ * 1. Correct `color` not being inherited in IE 8/9/10/11.
+ * 2. Remove padding so people aren't caught out if they zero out fieldsets.
+ */
+
+legend {
+ border: 0; /* 1 */
+ padding: 0; /* 2 */
+}
+
+/**
+ * Remove default vertical scrollbar in IE 8/9/10/11.
+ */
+
+textarea {
+ overflow: auto;
+}
+
+/**
+ * Don't inherit the `font-weight` (applied by a rule above).
+ * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
+ */
+
+optgroup {
+ font-weight: bold;
+}
+
+/* Tables
+ ========================================================================== */
+
+/**
+ * Remove most spacing between table cells.
+ */
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+td,
+th {
+ padding: 0;
+}
+
+/*csslint important:false*/
+
+/* ==========================================================================
+ Pure Base Extras
+ ========================================================================== */
+
+/**
+ * Extra rules that Pure adds on top of Normalize.css
+ */
+
+/**
+ * Always hide an element when it has the `hidden` HTML attribute.
+ */
+
+.hidden,
+[hidden] {
+ display: none !important;
+}
+
+/**
+ * Add this class to an image to make it fit within it's fluid parent wrapper while maintaining
+ * aspect ratio.
+ */
+.pure-img {
+ max-width: 100%;
+ height: auto;
+ display: block;
+}
+
+/*csslint regex-selectors:false, known-properties:false, duplicate-properties:false*/
+
+.pure-g {
+ letter-spacing: -0.31em; /* Webkit: collapse white-space between units */
+ *letter-spacing: normal; /* reset IE < 8 */
+ *word-spacing: -0.43em; /* IE < 8: collapse white-space between units */
+ text-rendering: optimizespeed; /* Webkit: fixes text-rendering: optimizeLegibility */
+
+ /*
+ Sets the font stack to fonts known to work properly with the above letter
+ and word spacings. See: https://github.com/yahoo/pure/issues/41/
+
+ The following font stack makes Pure Grids work on all known environments.
+
+ * FreeSans: Ships with many Linux distros, including Ubuntu
+
+ * Arimo: Ships with Chrome OS. Arimo has to be defined before Helvetica and
+ Arial to get picked up by the browser, even though neither is available
+ in Chrome OS.
+
+ * Droid Sans: Ships with all versions of Android.
+
+ * Helvetica, Arial, sans-serif: Common font stack on OS X and Windows.
+ */
+ font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif;
+
+ /* Use flexbox when possible to avoid `letter-spacing` side-effects. */
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-flex-flow: row wrap;
+ -ms-flex-flow: row wrap;
+ flex-flow: row wrap;
+
+ /* Prevents distributing space between rows */
+ -webkit-align-content: flex-start;
+ -ms-flex-line-pack: start;
+ align-content: flex-start;
+}
+
+/* IE10 display: -ms-flexbox (and display: flex in IE 11) does not work inside a table; fall back to block and rely on font hack */
+@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
+ table .pure-g {
+ display: block;
+ }
+}
+
+/* Opera as of 12 on Windows needs word-spacing.
+ The ".opera-only" selector is used to prevent actual prefocus styling
+ and is not required in markup.
+*/
+.opera-only :-o-prefocus,
+.pure-g {
+ word-spacing: -0.43em;
+}
+
+.pure-u {
+ display: inline-block;
+ *display: inline; /* IE < 8: fake inline-block */
+ zoom: 1;
+ letter-spacing: normal;
+ word-spacing: normal;
+ vertical-align: top;
+ text-rendering: auto;
+}
+
+/*
+Resets the font family back to the OS/browser's default sans-serif font,
+this the same font stack that Normalize.css sets for the `body`.
+*/
+.pure-g [class *= "pure-u"] {
+ font-family: sans-serif;
+}
+
+.pure-u-1,
+.pure-u-1-1,
+.pure-u-1-2,
+.pure-u-1-3,
+.pure-u-2-3,
+.pure-u-1-4,
+.pure-u-3-4,
+.pure-u-1-5,
+.pure-u-2-5,
+.pure-u-3-5,
+.pure-u-4-5,
+.pure-u-5-5,
+.pure-u-1-6,
+.pure-u-5-6,
+.pure-u-1-8,
+.pure-u-3-8,
+.pure-u-5-8,
+.pure-u-7-8,
+.pure-u-1-12,
+.pure-u-5-12,
+.pure-u-7-12,
+.pure-u-11-12,
+.pure-u-1-24,
+.pure-u-2-24,
+.pure-u-3-24,
+.pure-u-4-24,
+.pure-u-5-24,
+.pure-u-6-24,
+.pure-u-7-24,
+.pure-u-8-24,
+.pure-u-9-24,
+.pure-u-10-24,
+.pure-u-11-24,
+.pure-u-12-24,
+.pure-u-13-24,
+.pure-u-14-24,
+.pure-u-15-24,
+.pure-u-16-24,
+.pure-u-17-24,
+.pure-u-18-24,
+.pure-u-19-24,
+.pure-u-20-24,
+.pure-u-21-24,
+.pure-u-22-24,
+.pure-u-23-24,
+.pure-u-24-24 {
+ display: inline-block;
+ *display: inline;
+ zoom: 1;
+ letter-spacing: normal;
+ word-spacing: normal;
+ vertical-align: top;
+ text-rendering: auto;
+}
+
+.pure-u-1-24 {
+ width: 4.1667%;
+ *width: 4.1357%;
+}
+
+.pure-u-1-12,
+.pure-u-2-24 {
+ width: 8.3333%;
+ *width: 8.3023%;
+}
+
+.pure-u-1-8,
+.pure-u-3-24 {
+ width: 12.5000%;
+ *width: 12.4690%;
+}
+
+.pure-u-1-6,
+.pure-u-4-24 {
+ width: 16.6667%;
+ *width: 16.6357%;
+}
+
+.pure-u-1-5 {
+ width: 20%;
+ *width: 19.9690%;
+}
+
+.pure-u-5-24 {
+ width: 20.8333%;
+ *width: 20.8023%;
+}
+
+.pure-u-1-4,
+.pure-u-6-24 {
+ width: 25%;
+ *width: 24.9690%;
+}
+
+.pure-u-7-24 {
+ width: 29.1667%;
+ *width: 29.1357%;
+}
+
+.pure-u-1-3,
+.pure-u-8-24 {
+ width: 33.3333%;
+ *width: 33.3023%;
+}
+
+.pure-u-3-8,
+.pure-u-9-24 {
+ width: 37.5000%;
+ *width: 37.4690%;
+}
+
+.pure-u-2-5 {
+ width: 40%;
+ *width: 39.9690%;
+}
+
+.pure-u-5-12,
+.pure-u-10-24 {
+ width: 41.6667%;
+ *width: 41.6357%;
+}
+
+.pure-u-11-24 {
+ width: 45.8333%;
+ *width: 45.8023%;
+}
+
+.pure-u-1-2,
+.pure-u-12-24 {
+ width: 50%;
+ *width: 49.9690%;
+}
+
+.pure-u-13-24 {
+ width: 54.1667%;
+ *width: 54.1357%;
+}
+
+.pure-u-7-12,
+.pure-u-14-24 {
+ width: 58.3333%;
+ *width: 58.3023%;
+}
+
+.pure-u-3-5 {
+ width: 60%;
+ *width: 59.9690%;
+}
+
+.pure-u-5-8,
+.pure-u-15-24 {
+ width: 62.5000%;
+ *width: 62.4690%;
+}
+
+.pure-u-2-3,
+.pure-u-16-24 {
+ width: 66.6667%;
+ *width: 66.6357%;
+}
+
+.pure-u-17-24 {
+ width: 70.8333%;
+ *width: 70.8023%;
+}
+
+.pure-u-3-4,
+.pure-u-18-24 {
+ width: 75%;
+ *width: 74.9690%;
+}
+
+.pure-u-19-24 {
+ width: 79.1667%;
+ *width: 79.1357%;
+}
+
+.pure-u-4-5 {
+ width: 80%;
+ *width: 79.9690%;
+}
+
+.pure-u-5-6,
+.pure-u-20-24 {
+ width: 83.3333%;
+ *width: 83.3023%;
+}
+
+.pure-u-7-8,
+.pure-u-21-24 {
+ width: 87.5000%;
+ *width: 87.4690%;
+}
+
+.pure-u-11-12,
+.pure-u-22-24 {
+ width: 91.6667%;
+ *width: 91.6357%;
+}
+
+.pure-u-23-24 {
+ width: 95.8333%;
+ *width: 95.8023%;
+}
+
+.pure-u-1,
+.pure-u-1-1,
+.pure-u-5-5,
+.pure-u-24-24 {
+ width: 100%;
+}
+.pure-button {
+ /* Structure */
+ display: inline-block;
+ zoom: 1;
+ line-height: normal;
+ white-space: nowrap;
+ vertical-align: middle;
+ text-align: center;
+ cursor: pointer;
+ -webkit-user-drag: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ box-sizing: border-box;
+}
+
+/* Firefox: Get rid of the inner focus border */
+.pure-button::-moz-focus-inner {
+ padding: 0;
+ border: 0;
+}
+
+/* Inherit .pure-g styles */
+.pure-button-group {
+ letter-spacing: -0.31em; /* Webkit: collapse white-space between units */
+ *letter-spacing: normal; /* reset IE < 8 */
+ *word-spacing: -0.43em; /* IE < 8: collapse white-space between units */
+ text-rendering: optimizespeed; /* Webkit: fixes text-rendering: optimizeLegibility */
+}
+
+.opera-only :-o-prefocus,
+.pure-button-group {
+ word-spacing: -0.43em;
+}
+
+.pure-button-group .pure-button {
+ letter-spacing: normal;
+ word-spacing: normal;
+ vertical-align: top;
+ text-rendering: auto;
+}
+
+/*csslint outline-none:false*/
+
+.pure-button {
+ font-family: inherit;
+ font-size: 100%;
+ padding: 0.5em 1em;
+ color: #444; /* rgba not supported (IE 8) */
+ color: rgba(0, 0, 0, 0.80); /* rgba supported */
+ border: 1px solid #999; /*IE 6/7/8*/
+ border: none rgba(0, 0, 0, 0); /*IE9 + everything else*/
+ background-color: #E6E6E6;
+ text-decoration: none;
+ border-radius: 2px;
+}
+
+.pure-button-hover,
+.pure-button:hover,
+.pure-button:focus {
+ /* csslint ignore:start */
+ filter: alpha(opacity=90);
+ /* csslint ignore:end */
+ background-image: -webkit-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));
+ background-image: linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));
+}
+.pure-button:focus {
+ outline: 0;
+}
+.pure-button-active,
+.pure-button:active {
+ box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset, 0 0 6px rgba(0,0,0, 0.20) inset;
+ border-color: #000\9;
+}
+
+.pure-button[disabled],
+.pure-button-disabled,
+.pure-button-disabled:hover,
+.pure-button-disabled:focus,
+.pure-button-disabled:active {
+ border: none;
+ background-image: none;
+ /* csslint ignore:start */
+ filter: alpha(opacity=40);
+ /* csslint ignore:end */
+ opacity: 0.40;
+ cursor: not-allowed;
+ box-shadow: none;
+ pointer-events: none;
+}
+
+.pure-button-hidden {
+ display: none;
+}
+
+.pure-button-primary,
+.pure-button-selected,
+a.pure-button-primary,
+a.pure-button-selected {
+ background-color: rgb(0, 120, 231);
+ color: #fff;
+}
+
+/* Button Groups */
+.pure-button-group .pure-button {
+ margin: 0;
+ border-radius: 0;
+ border-right: 1px solid #111; /* fallback color for rgba() for IE7/8 */
+ border-right: 1px solid rgba(0, 0, 0, 0.2);
+
+}
+
+.pure-button-group .pure-button:first-child {
+ border-top-left-radius: 2px;
+ border-bottom-left-radius: 2px;
+}
+.pure-button-group .pure-button:last-child {
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-right: none;
+}
+
+/*csslint box-model:false*/
+/*
+Box-model set to false because we're setting a height on select elements, which
+also have border and padding. This is done because some browsers don't render
+the padding. We explicitly set the box-model for select elements to border-box,
+so we can ignore the csslint warning.
+*/
+
+.pure-form input[type="text"],
+.pure-form input[type="password"],
+.pure-form input[type="email"],
+.pure-form input[type="url"],
+.pure-form input[type="date"],
+.pure-form input[type="month"],
+.pure-form input[type="time"],
+.pure-form input[type="datetime"],
+.pure-form input[type="datetime-local"],
+.pure-form input[type="week"],
+.pure-form input[type="number"],
+.pure-form input[type="search"],
+.pure-form input[type="tel"],
+.pure-form input[type="color"],
+.pure-form select,
+.pure-form textarea {
+ padding: 0.5em 0.6em;
+ display: inline-block;
+ border: 1px solid #ccc;
+ box-shadow: inset 0 1px 3px #ddd;
+ border-radius: 4px;
+ vertical-align: middle;
+ box-sizing: border-box;
+}
+
+/*
+Need to separate out the :not() selector from the rest of the CSS 2.1 selectors
+since IE8 won't execute CSS that contains a CSS3 selector.
+*/
+.pure-form input:not([type]) {
+ padding: 0.5em 0.6em;
+ display: inline-block;
+ border: 1px solid #ccc;
+ box-shadow: inset 0 1px 3px #ddd;
+ border-radius: 4px;
+ box-sizing: border-box;
+}
+
+
+/* Chrome (as of v.32/34 on OS X) needs additional room for color to display. */
+/* May be able to remove this tweak as color inputs become more standardized across browsers. */
+.pure-form input[type="color"] {
+ padding: 0.2em 0.5em;
+}
+
+
+.pure-form input[type="text"]:focus,
+.pure-form input[type="password"]:focus,
+.pure-form input[type="email"]:focus,
+.pure-form input[type="url"]:focus,
+.pure-form input[type="date"]:focus,
+.pure-form input[type="month"]:focus,
+.pure-form input[type="time"]:focus,
+.pure-form input[type="datetime"]:focus,
+.pure-form input[type="datetime-local"]:focus,
+.pure-form input[type="week"]:focus,
+.pure-form input[type="number"]:focus,
+.pure-form input[type="search"]:focus,
+.pure-form input[type="tel"]:focus,
+.pure-form input[type="color"]:focus,
+.pure-form select:focus,
+.pure-form textarea:focus {
+ outline: 0;
+ border-color: #129FEA;
+}
+
+/*
+Need to separate out the :not() selector from the rest of the CSS 2.1 selectors
+since IE8 won't execute CSS that contains a CSS3 selector.
+*/
+.pure-form input:not([type]):focus {
+ outline: 0;
+ border-color: #129FEA;
+}
+
+.pure-form input[type="file"]:focus,
+.pure-form input[type="radio"]:focus,
+.pure-form input[type="checkbox"]:focus {
+ outline: thin solid #129FEA;
+ outline: 1px auto #129FEA;
+}
+.pure-form .pure-checkbox,
+.pure-form .pure-radio {
+ margin: 0.5em 0;
+ display: block;
+}
+
+.pure-form input[type="text"][disabled],
+.pure-form input[type="password"][disabled],
+.pure-form input[type="email"][disabled],
+.pure-form input[type="url"][disabled],
+.pure-form input[type="date"][disabled],
+.pure-form input[type="month"][disabled],
+.pure-form input[type="time"][disabled],
+.pure-form input[type="datetime"][disabled],
+.pure-form input[type="datetime-local"][disabled],
+.pure-form input[type="week"][disabled],
+.pure-form input[type="number"][disabled],
+.pure-form input[type="search"][disabled],
+.pure-form input[type="tel"][disabled],
+.pure-form input[type="color"][disabled],
+.pure-form select[disabled],
+.pure-form textarea[disabled] {
+ cursor: not-allowed;
+ background-color: #eaeded;
+ color: #cad2d3;
+}
+
+/*
+Need to separate out the :not() selector from the rest of the CSS 2.1 selectors
+since IE8 won't execute CSS that contains a CSS3 selector.
+*/
+.pure-form input:not([type])[disabled] {
+ cursor: not-allowed;
+ background-color: #eaeded;
+ color: #cad2d3;
+}
+.pure-form input[readonly],
+.pure-form select[readonly],
+.pure-form textarea[readonly] {
+ background-color: #eee; /* menu hover bg color */
+ color: #777; /* menu text color */
+ border-color: #ccc;
+}
+
+.pure-form input:focus:invalid,
+.pure-form textarea:focus:invalid,
+.pure-form select:focus:invalid {
+ color: #b94a48;
+ border-color: #e9322d;
+}
+.pure-form input[type="file"]:focus:invalid:focus,
+.pure-form input[type="radio"]:focus:invalid:focus,
+.pure-form input[type="checkbox"]:focus:invalid:focus {
+ outline-color: #e9322d;
+}
+.pure-form select {
+ /* Normalizes the height; padding is not sufficient. */
+ height: 2.25em;
+ border: 1px solid #ccc;
+ background-color: white;
+}
+.pure-form select[multiple] {
+ height: auto;
+}
+.pure-form label {
+ margin: 0.5em 0 0.2em;
+}
+.pure-form fieldset {
+ margin: 0;
+ padding: 0.35em 0 0.75em;
+ border: 0;
+}
+.pure-form legend {
+ display: block;
+ width: 100%;
+ padding: 0.3em 0;
+ margin-bottom: 0.3em;
+ color: #333;
+ border-bottom: 1px solid #e5e5e5;
+}
+
+.pure-form-stacked input[type="text"],
+.pure-form-stacked input[type="password"],
+.pure-form-stacked input[type="email"],
+.pure-form-stacked input[type="url"],
+.pure-form-stacked input[type="date"],
+.pure-form-stacked input[type="month"],
+.pure-form-stacked input[type="time"],
+.pure-form-stacked input[type="datetime"],
+.pure-form-stacked input[type="datetime-local"],
+.pure-form-stacked input[type="week"],
+.pure-form-stacked input[type="number"],
+.pure-form-stacked input[type="search"],
+.pure-form-stacked input[type="tel"],
+.pure-form-stacked input[type="color"],
+.pure-form-stacked input[type="file"],
+.pure-form-stacked select,
+.pure-form-stacked label,
+.pure-form-stacked textarea {
+ display: block;
+ margin: 0.25em 0;
+}
+
+/*
+Need to separate out the :not() selector from the rest of the CSS 2.1 selectors
+since IE8 won't execute CSS that contains a CSS3 selector.
+*/
+.pure-form-stacked input:not([type]) {
+ display: block;
+ margin: 0.25em 0;
+}
+.pure-form-aligned input,
+.pure-form-aligned textarea,
+.pure-form-aligned select,
+/* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline instead. */
+.pure-form-aligned .pure-help-inline,
+.pure-form-message-inline {
+ display: inline-block;
+ *display: inline;
+ *zoom: 1;
+ vertical-align: middle;
+}
+.pure-form-aligned textarea {
+ vertical-align: top;
+}
+
+/* Aligned Forms */
+.pure-form-aligned .pure-control-group {
+ margin-bottom: 0.5em;
+}
+.pure-form-aligned .pure-control-group label {
+ text-align: right;
+ display: inline-block;
+ vertical-align: middle;
+ width: 10em;
+ margin: 0 1em 0 0;
+}
+.pure-form-aligned .pure-controls {
+ margin: 1.5em 0 0 11em;
+}
+
+/* Rounded Inputs */
+.pure-form input.pure-input-rounded,
+.pure-form .pure-input-rounded {
+ border-radius: 2em;
+ padding: 0.5em 1em;
+}
+
+/* Grouped Inputs */
+.pure-form .pure-group fieldset {
+ margin-bottom: 10px;
+}
+.pure-form .pure-group input,
+.pure-form .pure-group textarea {
+ display: block;
+ padding: 10px;
+ margin: 0 0 -1px;
+ border-radius: 0;
+ position: relative;
+ top: -1px;
+}
+.pure-form .pure-group input:focus,
+.pure-form .pure-group textarea:focus {
+ z-index: 3;
+}
+.pure-form .pure-group input:first-child,
+.pure-form .pure-group textarea:first-child {
+ top: 1px;
+ border-radius: 4px 4px 0 0;
+ margin: 0;
+}
+.pure-form .pure-group input:first-child:last-child,
+.pure-form .pure-group textarea:first-child:last-child {
+ top: 1px;
+ border-radius: 4px;
+ margin: 0;
+}
+.pure-form .pure-group input:last-child,
+.pure-form .pure-group textarea:last-child {
+ top: -2px;
+ border-radius: 0 0 4px 4px;
+ margin: 0;
+}
+.pure-form .pure-group button {
+ margin: 0.35em 0;
+}
+
+.pure-form .pure-input-1 {
+ width: 100%;
+}
+.pure-form .pure-input-3-4 {
+ width: 75%;
+}
+.pure-form .pure-input-2-3 {
+ width: 66%;
+}
+.pure-form .pure-input-1-2 {
+ width: 50%;
+}
+.pure-form .pure-input-1-3 {
+ width: 33%;
+}
+.pure-form .pure-input-1-4 {
+ width: 25%;
+}
+
+/* Inline help for forms */
+/* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline instead. */
+.pure-form .pure-help-inline,
+.pure-form-message-inline {
+ display: inline-block;
+ padding-left: 0.3em;
+ color: #666;
+ vertical-align: middle;
+ font-size: 0.875em;
+}
+
+/* Block help for forms */
+.pure-form-message {
+ display: block;
+ color: #666;
+ font-size: 0.875em;
+}
+
+@media only screen and (max-width : 480px) {
+ .pure-form button[type="submit"] {
+ margin: 0.7em 0 0;
+ }
+
+ .pure-form input:not([type]),
+ .pure-form input[type="text"],
+ .pure-form input[type="password"],
+ .pure-form input[type="email"],
+ .pure-form input[type="url"],
+ .pure-form input[type="date"],
+ .pure-form input[type="month"],
+ .pure-form input[type="time"],
+ .pure-form input[type="datetime"],
+ .pure-form input[type="datetime-local"],
+ .pure-form input[type="week"],
+ .pure-form input[type="number"],
+ .pure-form input[type="search"],
+ .pure-form input[type="tel"],
+ .pure-form input[type="color"],
+ .pure-form label {
+ margin-bottom: 0.3em;
+ display: block;
+ }
+
+ .pure-group input:not([type]),
+ .pure-group input[type="text"],
+ .pure-group input[type="password"],
+ .pure-group input[type="email"],
+ .pure-group input[type="url"],
+ .pure-group input[type="date"],
+ .pure-group input[type="month"],
+ .pure-group input[type="time"],
+ .pure-group input[type="datetime"],
+ .pure-group input[type="datetime-local"],
+ .pure-group input[type="week"],
+ .pure-group input[type="number"],
+ .pure-group input[type="search"],
+ .pure-group input[type="tel"],
+ .pure-group input[type="color"] {
+ margin-bottom: 0;
+ }
+
+ .pure-form-aligned .pure-control-group label {
+ margin-bottom: 0.3em;
+ text-align: left;
+ display: block;
+ width: 100%;
+ }
+
+ .pure-form-aligned .pure-controls {
+ margin: 1.5em 0 0 0;
+ }
+
+ /* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline instead. */
+ .pure-form .pure-help-inline,
+ .pure-form-message-inline,
+ .pure-form-message {
+ display: block;
+ font-size: 0.75em;
+ /* Increased bottom padding to make it group with its related input element. */
+ padding: 0.2em 0 0.8em;
+ }
+}
+
+/*csslint adjoining-classes: false, box-model:false*/
+.pure-menu {
+ box-sizing: border-box;
+}
+
+.pure-menu-fixed {
+ position: fixed;
+ left: 0;
+ top: 0;
+ z-index: 3;
+}
+
+.pure-menu-list,
+.pure-menu-item {
+ position: relative;
+}
+
+.pure-menu-list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+.pure-menu-item {
+ padding: 0;
+ margin: 0;
+ height: 100%;
+}
+
+.pure-menu-link,
+.pure-menu-heading {
+ display: block;
+ text-decoration: none;
+ white-space: nowrap;
+}
+
+/* HORIZONTAL MENU */
+.pure-menu-horizontal {
+ width: 100%;
+ white-space: nowrap;
+}
+
+.pure-menu-horizontal .pure-menu-list {
+ display: inline-block;
+}
+
+/* Initial menus should be inline-block so that they are horizontal */
+.pure-menu-horizontal .pure-menu-item,
+.pure-menu-horizontal .pure-menu-heading,
+.pure-menu-horizontal .pure-menu-separator {
+ display: inline-block;
+ *display: inline;
+ zoom: 1;
+ vertical-align: middle;
+}
+
+/* Submenus should still be display: block; */
+.pure-menu-item .pure-menu-item {
+ display: block;
+}
+
+.pure-menu-children {
+ display: none;
+ position: absolute;
+ left: 100%;
+ top: 0;
+ margin: 0;
+ padding: 0;
+ z-index: 3;
+}
+
+.pure-menu-horizontal .pure-menu-children {
+ left: 0;
+ top: auto;
+ width: inherit;
+}
+
+.pure-menu-allow-hover:hover > .pure-menu-children,
+.pure-menu-active > .pure-menu-children {
+ display: block;
+ position: absolute;
+}
+
+/* Vertical Menus - show the dropdown arrow */
+.pure-menu-has-children > .pure-menu-link:after {
+ padding-left: 0.5em;
+ content: "\25B8";
+ font-size: small;
+}
+
+/* Horizontal Menus - show the dropdown arrow */
+.pure-menu-horizontal .pure-menu-has-children > .pure-menu-link:after {
+ content: "\25BE";
+}
+
+/* scrollable menus */
+.pure-menu-scrollable {
+ overflow-y: scroll;
+ overflow-x: hidden;
+}
+
+.pure-menu-scrollable .pure-menu-list {
+ display: block;
+}
+
+.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list {
+ display: inline-block;
+}
+
+.pure-menu-horizontal.pure-menu-scrollable {
+ white-space: nowrap;
+ overflow-y: hidden;
+ overflow-x: auto;
+ -ms-overflow-style: none;
+ -webkit-overflow-scrolling: touch;
+ /* a little extra padding for this style to allow for scrollbars */
+ padding: .5em 0;
+}
+
+.pure-menu-horizontal.pure-menu-scrollable::-webkit-scrollbar {
+ display: none;
+}
+
+/* misc default styling */
+
+.pure-menu-separator,
+.pure-menu-horizontal .pure-menu-children .pure-menu-separator {
+ background-color: #ccc;
+ height: 1px;
+ margin: .3em 0;
+}
+
+.pure-menu-horizontal .pure-menu-separator {
+ width: 1px;
+ height: 1.3em;
+ margin: 0 .3em ;
+}
+
+/* Need to reset the separator since submenu is vertical */
+.pure-menu-horizontal .pure-menu-children .pure-menu-separator {
+ display: block;
+ width: auto;
+}
+
+.pure-menu-heading {
+ text-transform: uppercase;
+ color: #565d64;
+}
+
+.pure-menu-link {
+ color: #777;
+}
+
+.pure-menu-children {
+ background-color: #fff;
+}
+
+.pure-menu-link,
+.pure-menu-disabled,
+.pure-menu-heading {
+ padding: .5em 1em;
+}
+
+.pure-menu-disabled {
+ opacity: .5;
+}
+
+.pure-menu-disabled .pure-menu-link:hover {
+ background-color: transparent;
+}
+
+.pure-menu-active > .pure-menu-link,
+.pure-menu-link:hover,
+.pure-menu-link:focus {
+ background-color: #eee;
+}
+
+.pure-menu-selected .pure-menu-link,
+.pure-menu-selected .pure-menu-link:visited {
+ color: #000;
+}
+
+.pure-table {
+ /* Remove spacing between table cells (from Normalize.css) */
+ border-collapse: collapse;
+ border-spacing: 0;
+ empty-cells: show;
+ border: 1px solid #cbcbcb;
+}
+
+.pure-table caption {
+ color: #000;
+ font: italic 85%/1 arial, sans-serif;
+ padding: 1em 0;
+ text-align: center;
+}
+
+.pure-table td,
+.pure-table th {
+ border-left: 1px solid #cbcbcb;/* inner column border */
+ border-width: 0 0 0 1px;
+ font-size: inherit;
+ margin: 0;
+ overflow: visible; /*to make ths where the title is really long work*/
+ padding: 0.5em 1em; /* cell padding */
+}
+
+/* Consider removing this next declaration block, as it causes problems when
+there's a rowspan on the first cell. Case added to the tests. issue#432 */
+.pure-table td:first-child,
+.pure-table th:first-child {
+ border-left-width: 0;
+}
+
+.pure-table thead {
+ background-color: #e0e0e0;
+ color: #000;
+ text-align: left;
+ vertical-align: bottom;
+}
+
+/*
+striping:
+ even - #fff (white)
+ odd - #f2f2f2 (light gray)
+*/
+.pure-table td {
+ background-color: transparent;
+}
+.pure-table-odd td {
+ background-color: #f2f2f2;
+}
+
+/* nth-child selector for modern browsers */
+.pure-table-striped tr:nth-child(2n-1) td {
+ background-color: #f2f2f2;
+}
+
+/* BORDERED TABLES */
+.pure-table-bordered td {
+ border-bottom: 1px solid #cbcbcb;
+}
+.pure-table-bordered tbody > tr:last-child > td {
+ border-bottom-width: 0;
+}
+
+
+/* HORIZONTAL BORDERED TABLES */
+
+.pure-table-horizontal td,
+.pure-table-horizontal th {
+ border-width: 0 0 1px 0;
+ border-bottom: 1px solid #cbcbcb;
+}
+.pure-table-horizontal tbody > tr:last-child > td {
+ border-bottom-width: 0;
+}
diff --git a/talermerchantdemos/blog/static/web-common/style.css b/talermerchantdemos/blog/static/web-common/style.css
new file mode 100644
index 0000000..b63fdd1
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/style.css
@@ -0,0 +1,198 @@
+/*
+ This file is part of GNU TALER.
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+ @author Gabor Toth
+*/
+
+body {
+ background-color: white;
+ margin: 0;
+ padding: 0;
+ font-family: Verdana, sans;
+}
+
+header {
+ width: 100%;
+ height: 100px;
+ margin: 0;
+ padding: 0;
+ border-bottom: 1px solid black;
+}
+
+/* NOTE: this class is needed to differentiate h1's which need to coexist
+ with the navigation bar, and h1's which don't (like the ones in the blog's
+ articles) */
+header h1.nav {
+ font-size: 200%;
+/* position: relative;
+ margin: 15 0 0 120px;
+ top: 50%;
+ transform: translateY(-50%);*/
+}
+
+body.article {
+ transform: translateY(-22px);
+}
+
+/* Redundant for testing reasons */
+header h1.book-title {
+ font-size: 200%;
+ transform: translateY(30px);
+ margin-left: 100px;
+}
+/*
+ margin: 15 0 0 120px;
+ position: relative;
+ top: 50%;
+ transform: translateY(-50%);
+}*/
+header #logo {
+ float: left;
+ width: 100px;
+ height: 100px;
+ padding: 0;
+ margin: 0;
+ text-align: center;
+ border-right: 1px solid black;
+}
+
+section#menu {
+ margin: 0 0 90 0;
+ padding: 5px;
+ border-right: 1px solid black;
+ height: 100%;
+ width: 90px;
+ float: left;
+}
+
+section#main {
+ margin: 0 0 0 100px;
+ padding: 20px;
+ border-left: 1px solid black;
+ height: 100%;
+ /*max-width: 40em;*/
+}
+
+div.copyright {
+ margin: 40px 1000px 0px 150px;
+ padding: 20px;
+ border-top: 1px solid black;
+ height: 100%;
+ max-width: 40em;
+ font-size: 70%;
+ text-align: center;
+}
+
+section#main h1:first-child {
+ margin-top: 0;
+}
+
+div.login-form, div.register-form {
+ border-radius: 10px;
+ background-color: #f2f2f2;
+ padding: 11px 27px 44px 27px;
+ max-width: 200px;
+}
+
+div.login-form input[type=submit],
+div.register-form input[type=submit] {
+ width-max: 30%;
+ float: right;
+ padding: 6px;
+}
+
+.selected-item {
+ border-style: solid;
+ border-width: 1px;
+}
+
+.informational {
+ border-radius: 8px;
+ padding: 8px;
+}
+
+.informational-ok {
+ background: #ccffcc;
+}
+
+.informational-fail {
+ background: #ff8566;
+}
+
+div.login-form input[type=text],
+div.login-form input[type=password],
+div.register-form input[type=text],
+div.register-form input[type=password] {
+ width: 100%;
+ padding: 12px 20px;
+ margin: 8px 0;
+ display: inline-block;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ box-sizing: border-box;
+}
+
+h1 {
+ font-size: 160%;
+}
+
+h2 {
+ font-size: 140%;
+}
+
+h3 {
+ font-size: 120%;
+}
+
+h4, h5, h6 {
+ font-size: 100%;
+}
+
+table.history {
+ margin: 30px 0px;
+ border-width: 1px;
+ border-spacing: 3px;
+ border-style: groove;
+ border-color: gray;
+ border-collapse: separate;
+ background-color: white;
+ white-space: nowrap;
+}
+table.history th {
+ border-width: 1px;
+ padding: 5px;
+ border-style: outset;
+ border-color: gray;
+ background-color: white;
+ -moz-border-radius: ;
+}
+table.history td {
+ border-width: 1px;
+ padding: 5px;
+ border-style: outset;
+ border-color: gray;
+ background-color: white;
+ -moz-border-radius: ;
+}
+div.track-results {
+ position: absolute;
+}
+
+.fade {
+ -webkit-animation: fade 0.7s;
+ animation: fade 0.7s;
+ opacity: 1;
+}
diff --git a/talermerchantdemos/blog/static/web-common/taler-fallback.css b/talermerchantdemos/blog/static/web-common/taler-fallback.css
new file mode 100644
index 0000000..e403d71
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/taler-fallback.css
@@ -0,0 +1,15 @@
+/* Fallback stylesheet. Should be included in the document as follows:
+ *
+ * <link rel="stylesheet"
+ * href="taler-fallback.css"
+ * id="taler-presence-stylesheet />
+ *
+ * When either the taler-wallet-lib is included or the wallet is presence,
+ * it will take over this stylesheet for presence detection.
+ *
+ * This fallback stylesheet makes sure that classes for presence detection
+ * are displayed correctly, even if JavaScript is disabled and the wallet
+ * is not present.
+ */
+
+.taler-installed-show { display: none; }
diff --git a/talermerchantdemos/blog/static/web-common/taler-wallet-lib.js b/talermerchantdemos/blog/static/web-common/taler-wallet-lib.js
new file mode 100644
index 0000000..2fe1c20
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/taler-wallet-lib.js
@@ -0,0 +1,446 @@
+/*
+ @source https://www.git.taler.net/?p=web-common.git;a=blob_plain;f=taler-wallet-lib.ts;hb=HEAD
+ @license magnet:?xt=urn:btih:5de60da917303dbfad4f93fb1b985ced5a89eac2&dn=lgpl-2.1.txt LGPL v21
+
+ @licstart The following is the entire license notice for the
+ JavaScript code in this page.
+
+ Copyright (C) 2015, 2016 INRIA
+
+ The JavaScript code in this page is free software: you can
+ redistribute it and/or modify it under the terms of the GNU
+ Lesser General Public License (GNU LGPL) as published by the Free Software
+ Foundation, either version 2.1 of the License, or (at your option)
+ any later version. The code is distributed WITHOUT ANY WARRANTY;
+ without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU LGPL for more details.
+
+ As additional permission under GNU LGPL version 2.1 section 7, you
+ may distribute non-source (e.g., minimized or compacted) forms of
+ that code without the copy of the GNU LGPL normally required by
+ section 4, provided you include this license notice and a URL
+ through which recipients can access the Corresponding Source.
+
+ @licend The above is the entire license notice
+ for the JavaScript code in this page.
+
+ @author Marcello Stanisci
+ @author Florian Dold
+*/
+(function (factory) {
+ if (typeof module === "object" && typeof module.exports === "object") {
+ var v = factory(require, exports);
+ if (v !== undefined) module.exports = v;
+ }
+ else if (typeof define === "function" && define.amd) {
+ define(["require", "exports"], factory);
+ }
+})(function (require, exports) {
+ "use strict";
+ var taler;
+ (function (taler) {
+ "use strict";
+ var logVerbose = false;
+ try {
+ logVerbose = !!localStorage.getItem("taler-log-verbose");
+ }
+ catch (e) {
+ }
+ var presentHandlers = [];
+ var absentHandlers = [];
+ // Are we running as the content script of an
+ // extension (and not just from a normal page)?
+ var runningInExtension = false;
+ var callSeqId = 1;
+ var installed = false;
+ var probeExecuted = false;
+ var pageLoaded = false;
+ var errorHandler = undefined;
+ var sheet;
+ function onError(handler) {
+ if (errorHandler) {
+ console.warn("Overriding error handler");
+ }
+ errorHandler = handler;
+ }
+ taler.onError = onError;
+ /**
+ * Error handler for things that go wrong in the merchant
+ * frontend browser code.
+ */
+ function raise_error(reason, detail) {
+ if (errorHandler) {
+ errorHandler(reason, detail);
+ return;
+ }
+ alert("Failure: " + reason + ". No error handler installed. Open the developer console for more information.");
+ console.error(reason, detail);
+ console.warn("No custom error handler set.");
+ }
+ function callWallet(funcName, args, onResult) {
+ var detail = JSON.parse(JSON.stringify(args || {}));
+ var callId = callSeqId++;
+ detail.callId = callId;
+ var onTimeout = function () {
+ console.warn("timeout for invocation of " + funcName);
+ };
+ var timeoutHandle = setTimeout(onTimeout, 1000);
+ var handler = function (evt) {
+ if (evt.detail.callId !== callId) {
+ return;
+ }
+ if (onResult) {
+ onResult(evt.detail);
+ }
+ clearTimeout(timeoutHandle);
+ document.removeEventListener(funcName + "-result", handler);
+ };
+ document.addEventListener(funcName + "-result", handler);
+ var evt = new CustomEvent(funcName, { detail: detail });
+ document.dispatchEvent(evt);
+ }
+ /**
+ * Confirm that a reserve was created.
+ *
+ * Used by tightly integrated bank portals.
+ */
+ function confirmReserve(reservePub) {
+ if (!installed) {
+ logVerbose && console.log("delaying confirmReserve");
+ taler.onPresent(function () {
+ confirmReserve(reservePub);
+ });
+ return;
+ }
+ callWallet("taler-confirm-reserve", { reserve_pub: reservePub });
+ }
+ taler.confirmReserve = confirmReserve;
+ function createReserve(callbackUrl, amount, wtTypes, suggestedExchangeUrl) {
+ if (!installed) {
+ logVerbose && console.log("delaying createReserve");
+ taler.onPresent(function () {
+ createReserve(callbackUrl, amount, wtTypes, suggestedExchangeUrl);
+ });
+ return;
+ }
+ var args = {
+ callback_url: callbackUrl,
+ amount: amount,
+ wt_types: wtTypes,
+ suggested_exchange_url: suggestedExchangeUrl
+ };
+ callWallet("taler-create-reserve", args);
+ }
+ taler.createReserve = createReserve;
+ function handlePaymentResponse(walletResp) {
+ /**
+ * Handle a failed payment.
+ *
+ * Try to notify the wallet first, before we show a potentially
+ * synchronous error message (such as an alert) or leave the page.
+ */
+ function handleFailedPayment(r) {
+ var timeoutHandle = null;
+ function err() {
+ raise_error("pay-failed", { status: r.status, response: r.responseText });
+ }
+ function onResp() {
+ if (timeoutHandle != null) {
+ clearTimeout(timeoutHandle);
+ timeoutHandle = null;
+ }
+ err();
+ }
+ function onTimeout() {
+ timeoutHandle = null;
+ err();
+ }
+ callWallet("taler-payment-failed", { H_contract: walletResp.H_contract }, onResp);
+ timeoutHandle = setTimeout(onTimeout, 200);
+ }
+ logVerbose && console.log("handling taler-notify-payment: ", walletResp);
+ // Payment timeout in ms.
+ var timeout_ms = 1000;
+ // Current request.
+ var r;
+ var timeoutHandle = null;
+ function sendPay() {
+ r = new XMLHttpRequest();
+ r.open("post", walletResp.contract.pay_url);
+ r.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+ r.send(JSON.stringify(walletResp.payReq));
+ r.onload = function () {
+ if (!r) {
+ return;
+ }
+ switch (r.status) {
+ case 200:
+ var merchantResp = JSON.parse(r.responseText);
+ logVerbose && console.log("got success from pay_url");
+ callWallet("taler-payment-succeeded", { H_contract: walletResp.H_contract, merchantSig: merchantResp.sig }, function () {
+ var nextUrl = walletResp.contract.fulfillment_url;
+ logVerbose && console.log("taler-payment-succeeded done, going to", nextUrl);
+ window.location.href = nextUrl;
+ window.location.reload(true);
+ });
+ break;
+ default:
+ handleFailedPayment(r);
+ break;
+ }
+ r = null;
+ if (timeoutHandle != null) {
+ clearTimeout(timeoutHandle);
+ timeoutHandle = null;
+ }
+ };
+ function retry() {
+ if (r) {
+ r.abort();
+ r = null;
+ }
+ timeout_ms = Math.min(timeout_ms * 2, 10 * 1000);
+ logVerbose && console.log("sendPay timed out, retrying in ", timeout_ms, "ms");
+ sendPay();
+ }
+ timeoutHandle = setTimeout(retry, timeout_ms);
+ }
+ sendPay();
+ }
+ function onPresent(f) {
+ presentHandlers.push(f);
+ }
+ taler.onPresent = onPresent;
+ function onAbsent(f) {
+ absentHandlers.push(f);
+ }
+ taler.onAbsent = onAbsent;
+ function internalPay(p) {
+ // either the callback gets called,
+ // or the wallet will redirect the browser
+ callWallet("taler-pay", p, handlePaymentResponse);
+ }
+ taler.internalPay = internalPay;
+ function pay(p) {
+ if (!installed) {
+ logVerbose && console.log("delaying call to 'pay' until GNU Taler wallet is present");
+ taler.onPresent(function () {
+ pay(p);
+ });
+ return;
+ }
+ internalPay(p);
+ }
+ taler.pay = pay;
+ function internalAddAuditor(d) {
+ // either the callback gets called,
+ // or the wallet will redirect the browser
+ callWallet("taler-add-auditor", d);
+ }
+ taler.internalAddAuditor = internalAddAuditor;
+ function addAuditor(d) {
+ if (!installed) {
+ logVerbose && console.log("delaying call to 'addAuditor' until GNU Taler wallet is present");
+ taler.onPresent(function () {
+ addAuditor(d);
+ });
+ return;
+ }
+ internalAddAuditor(d);
+ }
+ taler.addAuditor = addAuditor;
+ function internalCheckAuditor(url) {
+ return new Promise(function (resolve, reject) {
+ callWallet("taler-check-auditor", url, function (x) { return resolve(x); });
+ });
+ }
+ taler.internalCheckAuditor = internalCheckAuditor;
+ /**
+ * Check if an auditor is already added to the wallet.
+ *
+ * Same-origin restrictions apply.
+ */
+ function checkAuditor(url) {
+ if (!installed) {
+ logVerbose && console.log("delaying call to 'checkAuditor' until GNU Taler wallet is present");
+ return new Promise(function (resolve, reject) {
+ taler.onPresent(function () {
+ resolve(checkAuditor(url));
+ });
+ });
+ }
+ return internalCheckAuditor(url);
+ }
+ taler.checkAuditor = checkAuditor;
+ function initTaler() {
+ function handleUninstall() {
+ installed = false;
+ // not really true, but we want "uninstalled" to be shown
+ firstTimeoutCalled = true;
+ announce();
+ }
+ function handleProbe() {
+ probeExecuted = true;
+ if (!installed) {
+ logVerbose && console.log("taler install detected");
+ installed = true;
+ announce();
+ }
+ }
+ function probeTaler() {
+ probeExecuted = false;
+ var eve = new Event("taler-probe");
+ document.dispatchEvent(eve);
+ }
+ var firstTimeoutCalled = false;
+ function onProbeTimeout() {
+ if (!probeExecuted) {
+ if (installed || !firstTimeoutCalled) {
+ installed = false;
+ firstTimeoutCalled = true;
+ logVerbose && console.log("taler uninstall detected");
+ announce();
+ }
+ }
+ // try again, maybe it'll be installed ...
+ probeTaler();
+ }
+ /**
+ * Announce presence/absence and update stylesheets.
+ *
+ * Only called after document.readyState is at least "interactive".
+ */
+ function announce() {
+ if (!pageLoaded) {
+ logVerbose && console.log("page not loaded yet, announcing later");
+ return;
+ }
+ setStyles();
+ if (installed) {
+ logVerbose && console.log("announcing installed");
+ for (var i = 0; i < presentHandlers.length; i++) {
+ presentHandlers[i]();
+ }
+ }
+ else {
+ if (firstTimeoutCalled) {
+ logVerbose && console.log("announcing uninstalled");
+ for (var i = 0; i < absentHandlers.length; i++) {
+ absentHandlers[i]();
+ }
+ }
+ else {
+ logVerbose && console.log("announcing nothing");
+ }
+ }
+ }
+ function setStyles() {
+ if (!sheet || !sheet.cssRules) {
+ return;
+ }
+ while (sheet.cssRules.length > 0) {
+ sheet.deleteRule(0);
+ }
+ if (installed) {
+ sheet.insertRule(".taler-installed-hide { display: none; }", 0);
+ sheet.insertRule(".taler-probed-hide { display: none; }", 0);
+ }
+ else {
+ sheet.insertRule(".taler-installed-show { display: none; }", 0);
+ if (firstTimeoutCalled) {
+ sheet.insertRule(".taler-probed-hide { display: none; }", 0);
+ }
+ else {
+ // We're still doing the detection
+ sheet.insertRule(".taler-installed-hide { display: none; }", 0);
+ }
+ }
+ }
+ function initStyle() {
+ logVerbose && console.log("taking over styles");
+ var name = "taler-presence-stylesheet";
+ var content = "/* Taler stylesheet controlled by JS */";
+ var style = document.getElementById(name);
+ if (!style) {
+ style = document.createElement("style");
+ // Needed by WebKit
+ style.appendChild(document.createTextNode(content));
+ style.id = name;
+ document.head.appendChild(style);
+ sheet = style.sheet;
+ }
+ else {
+ // We've taken over the stylesheet now,
+ // make it clear by clearing all the rules in it
+ // and making it obvious in the DOM.
+ if (style.tagName.toLowerCase() === "style") {
+ style.innerText = content;
+ }
+ if (!style.sheet) {
+ throw Error("taler-presence-stylesheet should be a style sheet (<link> or <style>)");
+ }
+ sheet = style.sheet;
+ while (sheet.cssRules.length > 0) {
+ sheet.deleteRule(0);
+ }
+ }
+ }
+ function onPageLoad() {
+ pageLoaded = true;
+ if (document.readyState == "complete") {
+ initStyle();
+ }
+ else {
+ var listener = function () {
+ initStyle();
+ setStyles();
+ };
+ window.addEventListener("load", listener);
+ }
+ // We only start the timeout after the page is interactive.
+ window.setInterval(onProbeTimeout, 300);
+ announce();
+ }
+ probeTaler();
+ document.addEventListener("taler-probe-result", handleProbe, false);
+ document.addEventListener("taler-uninstall", handleUninstall, false);
+ // Handle the case where the JavaScript is loaded after the page
+ // has been loaded for the first time.
+ if (document.readyState == "loading") {
+ document.addEventListener("DOMContentLoaded", onPageLoad, false);
+ }
+ else {
+ onPageLoad();
+ }
+ }
+ function onPageLoadInExtension() {
+ if (document.documentElement.getAttribute("data-taler-nojs")) {
+ logVerbose && console.log("doing taler initialization from extension (nojs)");
+ initTaler();
+ }
+ }
+ var caught = false;
+ try {
+ chrome.runtime.sendMessage({ type: "ping" });
+ }
+ catch (e) {
+ caught = true;
+ }
+ if (caught) {
+ logVerbose && console.log("running taler-wallet-lib from page");
+ initTaler();
+ }
+ else {
+ logVerbose && console.log("running taler-wallet-lib from extension");
+ runningInExtension = true;
+ // Wait for even style sheets to load
+ if (document.readyState != "complete") {
+ window.addEventListener("load", function () { return onPageLoadInExtension(); });
+ }
+ else {
+ onPageLoadInExtension();
+ }
+ }
+ })(taler = exports.taler || (exports.taler = {}));
+});
+// @license-end
diff --git a/talermerchantdemos/blog/static/web-common/taler-wallet-lib.ts b/talermerchantdemos/blog/static/web-common/taler-wallet-lib.ts
new file mode 100644
index 0000000..cbf32e8
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/taler-wallet-lib.ts
@@ -0,0 +1,488 @@
+/*
+ @source https://www.git.taler.net/?p=web-common.git;a=blob_plain;f=taler-wallet-lib.ts;hb=HEAD
+ @license magnet:?xt=urn:btih:5de60da917303dbfad4f93fb1b985ced5a89eac2&dn=lgpl-2.1.txt LGPL v21
+
+ @licstart The following is the entire license notice for the
+ JavaScript code in this page.
+
+ Copyright (C) 2015, 2016 INRIA
+
+ The JavaScript code in this page is free software: you can
+ redistribute it and/or modify it under the terms of the GNU
+ Lesser General Public License (GNU LGPL) as published by the Free Software
+ Foundation, either version 2.1 of the License, or (at your option)
+ any later version. The code is distributed WITHOUT ANY WARRANTY;
+ without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU LGPL for more details.
+
+ As additional permission under GNU LGPL version 2.1 section 7, you
+ may distribute non-source (e.g., minimized or compacted) forms of
+ that code without the copy of the GNU LGPL normally required by
+ section 4, provided you include this license notice and a URL
+ through which recipients can access the Corresponding Source.
+
+ @licend The above is the entire license notice
+ for the JavaScript code in this page.
+
+ @author Marcello Stanisci
+ @author Florian Dold
+*/
+
+
+export namespace taler {
+ "use strict";
+
+ let logVerbose: boolean = false;
+ try {
+ logVerbose = !!localStorage.getItem("taler-log-verbose");
+ } catch (e) {
+ // can't read from local storage
+ }
+
+ const presentHandlers: any[] = [];
+ const absentHandlers: any[] = [];
+
+ // Are we running as the content script of an
+ // extension (and not just from a normal page)?
+ let runningInExtension = false;
+
+ let callSeqId = 1;
+
+ let installed = false;
+ let probeExecuted = false;
+ let pageLoaded = false;
+
+ let errorHandler: any = undefined;
+
+ let sheet: CSSStyleSheet;
+
+ export function onError(handler: any) {
+ if (errorHandler) {
+ console.warn("Overriding error handler");
+ }
+ errorHandler = handler;
+ }
+
+
+ /**
+ * Error handler for things that go wrong in the merchant
+ * frontend browser code.
+ */
+ function raise_error(reason: string, detail: any) {
+ if (errorHandler) {
+ errorHandler(reason, detail);
+ return;
+ }
+ alert(`Failure: ${reason}. No error handler installed. Open the developer console for more information.`);
+ console.error(reason, detail);
+ console.warn("No custom error handler set.");
+ }
+
+
+ function callWallet(funcName: string, args: any, onResult?: any): void {
+ const detail = JSON.parse(JSON.stringify(args || {}));
+ const callId = callSeqId++;
+ detail.callId = callId;
+ let onTimeout = () => {
+ console.warn("timeout for invocation of " + funcName);
+ }
+ const timeoutHandle: number = setTimeout(onTimeout, 1000);
+ let handler = (evt: CustomEvent) => {
+ if (evt.detail.callId !== callId) {
+ return;
+ }
+ if (onResult) {
+ onResult(evt.detail);
+ }
+ clearTimeout(timeoutHandle);
+ document.removeEventListener(funcName + "-result", handler);
+ };
+ document.addEventListener(funcName + "-result", handler);
+ const evt = new CustomEvent(funcName, {detail});
+ document.dispatchEvent(evt)
+ }
+
+
+ /**
+ * Confirm that a reserve was created.
+ *
+ * Used by tightly integrated bank portals.
+ */
+ export function confirmReserve(reservePub: string) {
+ if (!installed) {
+ logVerbose && console.log("delaying confirmReserve");
+ taler.onPresent(() => {
+ confirmReserve(reservePub);
+ });
+ return;
+ }
+ callWallet("taler-confirm-reserve", {reserve_pub: reservePub});
+ }
+
+
+ export function createReserve(callbackUrl: string, amount: any, wtTypes: string[], suggestedExchangeUrl?: string) {
+ if (!installed) {
+ logVerbose && console.log("delaying createReserve");
+ taler.onPresent(() => {
+ createReserve(callbackUrl, amount, wtTypes, suggestedExchangeUrl);
+ });
+ return;
+ }
+ let args = {
+ callback_url: callbackUrl,
+ amount: amount,
+ wt_types: wtTypes,
+ suggested_exchange_url: suggestedExchangeUrl
+ };
+ callWallet("taler-create-reserve", args);
+ }
+
+
+ function handlePaymentResponse(walletResp: any) {
+ /**
+ * Handle a failed payment.
+ *
+ * Try to notify the wallet first, before we show a potentially
+ * synchronous error message (such as an alert) or leave the page.
+ */
+ function handleFailedPayment(r: XMLHttpRequest) {
+ let timeoutHandle: number|null = null;
+ function err() {
+ raise_error("pay-failed", {status: r.status, response: r.responseText});
+ }
+ function onResp() {
+ if (timeoutHandle != null) {
+ clearTimeout(timeoutHandle);
+ timeoutHandle = null;
+ }
+ err();
+ }
+ function onTimeout() {
+ timeoutHandle = null
+ err();
+ }
+ callWallet("taler-payment-failed", {H_contract: walletResp.H_contract}, onResp);
+ timeoutHandle = setTimeout(onTimeout, 200);
+ }
+
+
+ logVerbose && console.log("handling taler-notify-payment: ", walletResp);
+ // Payment timeout in ms.
+ let timeout_ms = 1000;
+ // Current request.
+ let r: XMLHttpRequest|null;
+ let timeoutHandle: number|null = null;
+ function sendPay() {
+ r = new XMLHttpRequest();
+ r.open("post", walletResp.contract.pay_url);
+ r.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+ r.send(JSON.stringify(walletResp.payReq));
+ r.onload = function() {
+ if (!r) {
+ return;
+ }
+ switch (r.status) {
+ case 200:
+ const merchantResp = JSON.parse(r.responseText);
+ logVerbose && console.log("got success from pay_url");
+ callWallet("taler-payment-succeeded", {H_contract: walletResp.H_contract, merchantSig: merchantResp.sig}, () => {
+ let nextUrl = walletResp.contract.fulfillment_url;
+ logVerbose && console.log("taler-payment-succeeded done, going to", nextUrl);
+ window.location.href = nextUrl;
+ window.location.reload(true);
+ });
+ break;
+ default:
+ handleFailedPayment(r);
+ break;
+ }
+ r = null;
+ if (timeoutHandle != null) {
+ clearTimeout(timeoutHandle!);
+ timeoutHandle = null;
+ }
+ };
+ function retry() {
+ if (r) {
+ r.abort();
+ r = null;
+ }
+ timeout_ms = Math.min(timeout_ms * 2, 10 * 1000);
+ logVerbose && console.log("sendPay timed out, retrying in ", timeout_ms, "ms");
+ sendPay();
+ }
+ timeoutHandle = setTimeout(retry, timeout_ms);
+ }
+ sendPay();
+ }
+
+
+ export function onPresent(f: any) {
+ presentHandlers.push(f);
+ }
+
+ export function onAbsent(f: any) {
+ absentHandlers.push(f);
+ }
+
+ interface FulfillmentQuery {
+ type: "fulfillment_url";
+ }
+
+ interface OrderIdQuery {
+ type: "order_id";
+ order_id: string;
+ }
+
+
+ interface PayDetail {
+ contract_url?: string;
+ offer_url?: string;
+ }
+
+ export function internalPay(p: PayDetail) {
+ // either the callback gets called,
+ // or the wallet will redirect the browser
+ callWallet("taler-pay", p, handlePaymentResponse);
+ }
+
+ export function pay(p: PayDetail) {
+ if (!installed) {
+ logVerbose && console.log("delaying call to 'pay' until GNU Taler wallet is present");
+ taler.onPresent(() => {
+ pay(p);
+ });
+ return;
+ }
+ internalPay(p);
+ }
+
+ export interface AuditorDetail {
+ currency: string;
+ url: string;
+ auditorPub: string;
+ expirationStamp: number;
+ }
+
+
+ export function internalAddAuditor(d: AuditorDetail) {
+ // either the callback gets called,
+ // or the wallet will redirect the browser
+ callWallet("taler-add-auditor", d);
+ }
+
+
+ export function addAuditor(d: AuditorDetail) {
+ if (!installed) {
+ logVerbose && console.log("delaying call to 'addAuditor' until GNU Taler wallet is present");
+ taler.onPresent(() => {
+ addAuditor(d);
+ });
+ return;
+ }
+ internalAddAuditor(d);
+ }
+
+
+ export function internalCheckAuditor(url: string): Promise<AuditorDetail|undefined> {
+ return new Promise<AuditorDetail|undefined>((resolve, reject) => {
+ callWallet("taler-check-auditor", url, (x: any) => resolve(x as AuditorDetail));
+ });
+ }
+
+
+ /**
+ * Check if an auditor is already added to the wallet.
+ *
+ * Same-origin restrictions apply.
+ */
+ export function checkAuditor(url: string): Promise<AuditorDetail|undefined> {
+ if (!installed) {
+ logVerbose && console.log("delaying call to 'checkAuditor' until GNU Taler wallet is present");
+ return new Promise<AuditorDetail|undefined>((resolve, reject) => {
+ taler.onPresent(() => {
+ resolve(checkAuditor(url));
+ });
+ });
+ }
+ return internalCheckAuditor(url);
+ }
+
+
+ function initTaler() {
+
+ function handleUninstall() {
+ installed = false;
+ // not really true, but we want "uninstalled" to be shown
+ firstTimeoutCalled = true;
+ announce();
+ }
+
+ function handleProbe() {
+ probeExecuted = true;
+ if (!installed) {
+ logVerbose && console.log("taler install detected");
+ installed = true;
+ announce();
+ }
+ }
+
+ function probeTaler() {
+ probeExecuted = false;
+ var eve = new Event("taler-probe");
+ document.dispatchEvent(eve);
+ }
+
+ let firstTimeoutCalled = false;
+
+ function onProbeTimeout() {
+ if (!probeExecuted) {
+ if (installed || !firstTimeoutCalled) {
+ installed = false;
+ firstTimeoutCalled = true;
+ logVerbose && console.log("taler uninstall detected");
+ announce();
+ }
+ }
+ // try again, maybe it'll be installed ...
+ probeTaler();
+ }
+
+ /**
+ * Announce presence/absence and update stylesheets.
+ *
+ * Only called after document.readyState is at least "interactive".
+ */
+ function announce() {
+ if (!pageLoaded) {
+ logVerbose && console.log("page not loaded yet, announcing later");
+ return;
+ }
+ setStyles();
+ if (installed) {
+ logVerbose && console.log("announcing installed");
+ for (var i = 0; i < presentHandlers.length; i++) {
+ presentHandlers[i]();
+ }
+ } else {
+ if (firstTimeoutCalled) {
+ logVerbose && console.log("announcing uninstalled");
+ for (var i = 0; i < absentHandlers.length; i++) {
+ absentHandlers[i]();
+ }
+ } else {
+ logVerbose && console.log("announcing nothing");
+ }
+ }
+ }
+
+ function setStyles() {
+ if (!sheet || !sheet.cssRules) {
+ return;
+ }
+ while (sheet.cssRules.length > 0) {
+ sheet.deleteRule(0);
+ }
+ if (installed) {
+ sheet.insertRule(".taler-installed-hide { display: none; }", 0);
+ sheet.insertRule(".taler-probed-hide { display: none; }", 0);
+ } else {
+ sheet.insertRule(".taler-installed-show { display: none; }", 0);
+
+ if (firstTimeoutCalled) {
+ sheet.insertRule(".taler-probed-hide { display: none; }", 0);
+ } else {
+ // We're still doing the detection
+ sheet.insertRule(".taler-installed-hide { display: none; }", 0);
+ }
+ }
+ }
+
+ function initStyle() {
+ logVerbose && console.log("taking over styles");
+ const name = "taler-presence-stylesheet";
+ const content = "/* Taler stylesheet controlled by JS */";
+ let style = document.getElementById(name) as HTMLStyleElement|null;
+ if (!style) {
+ style = document.createElement("style");
+ // Needed by WebKit
+ style.appendChild(document.createTextNode(content));
+ style.id = name;
+ document.head.appendChild(style);
+ sheet = style.sheet as CSSStyleSheet;
+ } else {
+ // We've taken over the stylesheet now,
+ // make it clear by clearing all the rules in it
+ // and making it obvious in the DOM.
+ if (style.tagName.toLowerCase() === "style") {
+ style.innerText = content;
+ }
+ if (!style.sheet) {
+ throw Error("taler-presence-stylesheet should be a style sheet (<link> or <style>)");
+ }
+ sheet = style.sheet as CSSStyleSheet;
+ while (sheet.cssRules.length > 0) {
+ sheet.deleteRule(0);
+ }
+ }
+ }
+
+ function onPageLoad() {
+ pageLoaded = true;
+ if (document.readyState == "complete") {
+ initStyle();
+ } else {
+ let listener = () => {
+ initStyle();
+ setStyles();
+ };
+ window.addEventListener("load", listener);
+ }
+
+ // We only start the timeout after the page is interactive.
+ window.setInterval(onProbeTimeout, 300);
+
+ announce();
+ }
+
+ probeTaler();
+ document.addEventListener("taler-probe-result", handleProbe, false);
+ document.addEventListener("taler-uninstall", handleUninstall, false);
+ // Handle the case where the JavaScript is loaded after the page
+ // has been loaded for the first time.
+ if (document.readyState == "loading") {
+ document.addEventListener("DOMContentLoaded", onPageLoad, false);
+ } else {
+ onPageLoad();
+ }
+ }
+
+ function onPageLoadInExtension() {
+ if (document.documentElement.getAttribute("data-taler-nojs")) {
+ logVerbose && console.log("doing taler initialization from extension (nojs)");
+ initTaler();
+ }
+ }
+
+ let caught = false;
+ try {
+ (chrome as any).runtime.sendMessage({type: "ping"});
+ } catch (e) {
+ caught = true;
+ }
+
+ if (caught) {
+ logVerbose && console.log("running taler-wallet-lib from page");
+ initTaler();
+ } else {
+ logVerbose && console.log("running taler-wallet-lib from extension");
+ runningInExtension = true;
+ // Wait for even style sheets to load
+ if (document.readyState != "complete") {
+ window.addEventListener("load", () => onPageLoadInExtension());
+ } else {
+ onPageLoadInExtension();
+ }
+ }
+}
+// @license-end
diff --git a/talermerchantdemos/blog/static/web-common/tsconfig.json b/talermerchantdemos/blog/static/web-common/tsconfig.json
new file mode 100644
index 0000000..7d51196
--- /dev/null
+++ b/talermerchantdemos/blog/static/web-common/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "compilerOptions": {
+ "target": "es3",
+ "jsx": "react",
+ "module": "umd",
+ "experimentalDecorators": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true,
+ "strictNullChecks": true,
+ "noImplicitAny": true,
+ "lib": ["ES6", "DOM"]
+ },
+ "files": [
+ "taler-wallet-lib.ts",
+ "chrome-any.d.ts"
+ ]
+}