diff options
author | MS <ms@taler.net> | 2020-07-22 14:53:45 +0200 |
---|---|---|
committer | MS <ms@taler.net> | 2020-07-22 14:53:45 +0200 |
commit | 2d97ecc2c1ac605ca49e8a866b309daaeb7a831c (patch) | |
tree | 173f7917c5d0af822d2d51ed491c3cf2d8eaf23f /talermerchantdemos/blog/static | |
download | taler-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')
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 Binary files differnew file mode 100644 index 0000000..141b93d --- /dev/null +++ b/talermerchantdemos/blog/static/web-common/favicon-taler.ico 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 Binary files differnew file mode 100644 index 0000000..78159bb --- /dev/null +++ b/talermerchantdemos/blog/static/web-common/logo-2015-medium.png 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" + ] +} |