diff --git a/api/action/background.js b/api/action/background.js new file mode 100644 index 00000000..4ac52c5f --- /dev/null +++ b/api/action/background.js @@ -0,0 +1,12 @@ +// Copyright 2021 Google LLC +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +// Show the demo page once the extension is installed +chrome.runtime.onInstalled.addListener((_reason) => { + chrome.tabs.create({ + url: 'demo/index.html' + }); +}); diff --git a/api/action/demo/index.css b/api/action/demo/index.css new file mode 100644 index 00000000..1617b14b --- /dev/null +++ b/api/action/demo/index.css @@ -0,0 +1,22 @@ +p { + hyphens: initial; +} + +.flex { + display: flex; + gap: .25em; + margin: .5em 0; + align-items: flex-end; +} + +.spaced { + margin: .5em 0; +} + +.full-width { + width: 100%; +} + +button { + white-space: nowrap; +} diff --git a/api/action/demo/index.html b/api/action/demo/index.html new file mode 100644 index 00000000..65d41b5a --- /dev/null +++ b/api/action/demo/index.html @@ -0,0 +1,168 @@ + + + + + + + Document + + + + + +
+
+

Action API Demo

+

Before experimenting with these APIs, we recommend you pin the extension's action button to your + toolbar in order to make it easier to see the changes.

+ +
+ +
+

enable / disable

+ +

Clicking the below toggle enabled state button will enable or disable the extensions' + action button in Chrome's toolbar and extensions menu.

+ +

When disabled, clicking the action will not open a popup or trigger action.onClicked + events.

+ + + +
+
+ +
Action enabled
+
+
+ +
Action disabled
+
+
+
+ + + + + +
+

Badge Text

+ +

The action's badge text is a text overlay with a solid background color. This provides a + passive UI surface to share information with the user. It is most commonly used to show a + notification count or number of actions taken on the current page.

+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ + +
+ +
+ + + +
+

Icon

+ +

The action.setIcon + method allows you to change the action button's icon by either providing the path of an image + or the raw ImageData.

+ + + +
+ + + +
+

Hover Text

+ +

The action's title is visible when mousing over the extension's action button.

+ +

This value can be read and changed at runtime using the action.getTitle + and action.setTitle + methods, respectively.

+ +
+ +
+ +
+ + +
+ +
+
+ +
Default appearance
+
+
+ +
Title appears on hover
+
+
+
+ + diff --git a/api/action/demo/index.js b/api/action/demo/index.js new file mode 100644 index 00000000..ba85a253 --- /dev/null +++ b/api/action/demo/index.js @@ -0,0 +1,240 @@ +// Copyright 2021 Google LLC +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +/** + * @param {number} timeout + * @param {(event: Event) => void} callback + * @return {(event: Event) => void} + */ +function debounce(timeout, callback) { + let timeoutID = 0; + return (event) => { + clearTimeout(timeoutID); + timeoutID = setTimeout(() => callback(event), timeout); + }; +} + +// ------------------ +// .enable / .disable +// ------------------ + +// The action API does not expose a way to read the action's current enabled/disabled state, so we +// have to track it ourselves. +// Relevant feature request: https://bugs.chromium.org/p/chromium/issues/detail?id=1189295 +let actionEnabled = true; +let showToggleState = document.getElementById('show-toggle-state'); +document.getElementById('toggle-state-button').addEventListener('click', (_event) => { + if (actionEnabled) { + chrome.action.disable(); + } else { + chrome.action.enable(); + } + actionEnabled = !actionEnabled; +}); + +document.getElementById('popup-options').addEventListener('change', async (event) => { + let popup = event.target.value; + await chrome.action.setPopup({ popup }); + + // Show the updated popup path + await getCurrentPopup(); +}); + +async function getCurrentPopup() { + let popup = await chrome.action.getPopup({}); + document.getElementById('current-popup-value').value = popup; + return popup; +}; + +async function showCurrentPage() { + let popup = await getCurrentPopup(); + let pathname = ''; + if (popup) { + pathname = new URL(popup).pathname; + } + + let options = document.getElementById('popup-options'); + let option = options.querySelector(`option[value="${pathname}"]`); + option.selected = true; +} + +// Populate popup inputs on on page load +showCurrentPage(); + +// ---------- +// .onClicked +// ---------- + +// If a popup is specified, our on click handler won't be called. We declare it here rather than in +// the `onclicked-button` handler to prevent the user from accidentally registering multiple +// onClicked listeners. +chrome.action.onClicked.addListener((tab) => { + chrome.tabs.create({ url: 'https://html5zombo.com/' }); +}); + +document.getElementById('onclicked-button').addEventListener('click', async () => { + // Our listener will only receive the action's click event after clear out the popup URL + await chrome.action.setPopup({ popup: '' }); + await showCurrentPage(); +}); + +document.getElementById('onclicked-reset-button').addEventListener('click', async () => { + await chrome.action.setPopup({ popup: 'popups/popup.html' }); + await showCurrentPage(); +}); + +// ---------- +// badge text +// ---------- + +async function showBadgeText() { + let text = await chrome.action.getBadgeText({}); + document.getElementById('current-badge-text').value = text; +} + +// Populate badge text inputs on on page load +showBadgeText(); + +document.getElementById('badge-text-input').addEventListener('input', async (event) => { + let text = event.target.value; + await chrome.action.setBadgeText({ text }); + + showBadgeText(); +}); + +document.getElementById('clear-badge-button').addEventListener('click', async () => { + await chrome.action.setBadgeText({ text: '' }); + + showBadgeText(); +}); + +// ---------------------- +// badge background color +// ---------------------- + +async function showBadgeColor() { + let color = await chrome.action.getBadgeBackgroundColor({}); + document.getElementById('current-badge-bg-color').value = JSON.stringify(color, null, 0); +} + +// Populate badge background color inputs on on page load +showBadgeColor(); + +document.getElementById('set-badge-background-color-button').addEventListener('click', async () => { + // To show off this method, we must first make sure the badge has text + let currentText = await chrome.action.getBadgeText({}); + if (!currentText) { + chrome.action.setBadgeText({ text: 'hi :)' }); + showBadgeText(); + } + + // Next, generate a random RGBA color + let color = [0, 0, 0].map(() => Math.floor(Math.random * 255)); + + // Use the default background color ~10% of the time. + // + // NOTE: Alpha color cannot be set due to crbug.com/1184905. At the time of writing (Chrome 89), + // an alpha value of 0 sets the default color while a value of 1-255 will make the RGB color + // fully opaque. + if (Math.random() < 0.1) { + color.push(0); + } else { + color.push(255); + } + + chrome.action.setBadgeBackgroundColor({ color }); + showBadgeColor(); +}); + +document.getElementById('reset-badge-background-color-button').addEventListener('click', async () => { + chrome.action.setBadgeBackgroundColor({ color: [0, 0, 0, 0] }); + showBadgeColor(); +}); + +// ----------- +// action icon +// ----------- + +const EMOJI = [ + 'confetti', + 'suit', + 'bow', + 'dog', + 'skull', + 'yoyo', + 'cat', +]; + +let lastIconIndex = 0; +document.getElementById('set-icon-button').addEventListener('click', async () => { + // Clear out the badge text in order to make the icon change easier to see + chrome.action.setBadgeText({ text: '' }); + + // Randomly pick a new icon + let index = lastIconIndex; + index = Math.floor(Math.random() * (EMOJI.length)); + if (index === lastIconIndex) { + // Dupe detected! Increment the index & modulo to make sure we don't go out of bounds + index = (index + 1) % EMOJI.length; + } + let emojiFile = `images/emoji-${EMOJI[index]}.png`; + lastIconIndex = index; + + // There are easier ways for a page to extract an image's imageData, but the OffscreenCanvas + // approach used here works in both extension pages and service workers. + let response = await fetch(emojiFile); + let blob = await response.blob(); + let imageBitmap = await createImageBitmap(blob); + let osc = new OffscreenCanvas(imageBitmap.width, imageBitmap.height); + let ctx = osc.getContext('2d'); + ctx.drawImage(imageBitmap, 0, 0); + let imageData = ctx.getImageData(0, 0, osc.width, osc.height); + + chrome.action.setIcon({ imageData }); +}); + +document.getElementById('reset-icon-button').addEventListener('click', () => { + let manifest = chrome.runtime.getManifest(); + chrome.action.setIcon({ path: manifest.action.default_icon }); +}); + +// ------------- +// get/set title +// ------------- + +let titleInput = document.getElementById('title-input'); +let titleInputDebounce = Number.parseInt(titleInput.dataset.debounce || 100); +titleInput.addEventListener('input', debounce(200, async (event) => { + let title = event.target.value; + chrome.action.setTitle({ title }); + + showActionTitle(); +})); + +document.getElementById('reset-title-button').addEventListener('click', async (event) => { + let manifest = chrome.runtime.getManifest(); + let title = manifest.action.default_title; + + chrome.action.setTitle({ title }); + + showActionTitle(); +}); + +async function showActionTitle() { + let title = await chrome.action.getTitle({}); + + // If empty, the title falls back to the name of the extension + if (title === '') { + // … which we can get from the extension's manifest + let manifest = chrome.runtime.getManifest(); + title = manifest.name; + } + + document.getElementById('current-title').value = title; +} + +// Populate action title inputs on on page load +showActionTitle(); diff --git a/api/action/icons/128.png b/api/action/icons/128.png new file mode 100644 index 00000000..c3f6975e Binary files /dev/null and b/api/action/icons/128.png differ diff --git a/api/action/icons/32.png b/api/action/icons/32.png new file mode 100644 index 00000000..24e728cd Binary files /dev/null and b/api/action/icons/32.png differ diff --git a/api/action/icons/512.png b/api/action/icons/512.png new file mode 100644 index 00000000..ea0dd17a Binary files /dev/null and b/api/action/icons/512.png differ diff --git a/api/action/icons/72.png b/api/action/icons/72.png new file mode 100644 index 00000000..1cbac06e Binary files /dev/null and b/api/action/icons/72.png differ diff --git a/api/action/images/action-disabled.png b/api/action/images/action-disabled.png new file mode 100644 index 00000000..ae22acfa Binary files /dev/null and b/api/action/images/action-disabled.png differ diff --git a/api/action/images/action-enabled.png b/api/action/images/action-enabled.png new file mode 100644 index 00000000..2278ed5a Binary files /dev/null and b/api/action/images/action-enabled.png differ diff --git a/api/action/images/emoji-bow.png b/api/action/images/emoji-bow.png new file mode 100644 index 00000000..c02d09ed Binary files /dev/null and b/api/action/images/emoji-bow.png differ diff --git a/api/action/images/emoji-cat.png b/api/action/images/emoji-cat.png new file mode 100644 index 00000000..d113ee3d Binary files /dev/null and b/api/action/images/emoji-cat.png differ diff --git a/api/action/images/emoji-confetti.png b/api/action/images/emoji-confetti.png new file mode 100644 index 00000000..24e728cd Binary files /dev/null and b/api/action/images/emoji-confetti.png differ diff --git a/api/action/images/emoji-dog.png b/api/action/images/emoji-dog.png new file mode 100644 index 00000000..f636d1a9 Binary files /dev/null and b/api/action/images/emoji-dog.png differ diff --git a/api/action/images/emoji-skull.png b/api/action/images/emoji-skull.png new file mode 100644 index 00000000..2c6e6743 Binary files /dev/null and b/api/action/images/emoji-skull.png differ diff --git a/api/action/images/emoji-suit.png b/api/action/images/emoji-suit.png new file mode 100644 index 00000000..5e7f36b7 Binary files /dev/null and b/api/action/images/emoji-suit.png differ diff --git a/api/action/images/emoji-yoyo.png b/api/action/images/emoji-yoyo.png new file mode 100644 index 00000000..dfdf8914 Binary files /dev/null and b/api/action/images/emoji-yoyo.png differ diff --git a/api/action/images/pin-action.png b/api/action/images/pin-action.png new file mode 100644 index 00000000..86aeba42 Binary files /dev/null and b/api/action/images/pin-action.png differ diff --git a/api/action/images/title-hover.png b/api/action/images/title-hover.png new file mode 100644 index 00000000..d5c1cf60 Binary files /dev/null and b/api/action/images/title-hover.png differ diff --git a/api/action/images/title-no-hover.png b/api/action/images/title-no-hover.png new file mode 100644 index 00000000..4ca286fc Binary files /dev/null and b/api/action/images/title-no-hover.png differ diff --git a/api/action/manifest.json b/api/action/manifest.json new file mode 100644 index 00000000..3eac1304 --- /dev/null +++ b/api/action/manifest.json @@ -0,0 +1,24 @@ +{ + "name": "Action API Demo", + "version": "1.0", + "manifest_version": 3, + "background": { + "service_worker": "background.js" + }, + "action": { + "default_title": "Default Title", + "default_popup": "popups/popup.html", + "default_icon": { + "32": "icons/32.png", + "72": "icons/72.png", + "128": "icons/128.png", + "512": "icons/512.png" + } + }, + "icons": { + "32": "icons/32.png", + "72": "icons/72.png", + "128": "icons/128.png", + "512": "icons/512.png" + } +} diff --git a/api/action/popups/a.html b/api/action/popups/a.html new file mode 100644 index 00000000..cbd03b75 --- /dev/null +++ b/api/action/popups/a.html @@ -0,0 +1,32 @@ + + + + + + + Document + + + +

Action API Demo

+
+ A +
+ + diff --git a/api/action/popups/b.html b/api/action/popups/b.html new file mode 100644 index 00000000..eda5b65f --- /dev/null +++ b/api/action/popups/b.html @@ -0,0 +1,32 @@ + + + + + + + Document + + + +

Action API Demo

+
+ B +
+ + diff --git a/api/action/popups/popup.html b/api/action/popups/popup.html new file mode 100644 index 00000000..79727541 --- /dev/null +++ b/api/action/popups/popup.html @@ -0,0 +1,32 @@ + + + + + + + Document + + + +

Action API Demo

+
+ Hello, world! +
+ + diff --git a/api/action/third-party/awsm/awsm.css b/api/action/third-party/awsm/awsm.css new file mode 100644 index 00000000..45a1ee54 --- /dev/null +++ b/api/action/third-party/awsm/awsm.css @@ -0,0 +1,7 @@ +@charset "UTF-8"; +/*! + * awsm.css v3.0.7 (https://igoradamenko.github.io/awsm.css/) + * Copyright 2015 Igor Adamenko (https://igoradamenko.com) + * Licensed under MIT (https://github.com/igoradamenko/awsm.css/blob/master/LICENSE.md) + */ +html{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"PT Sans","Open Sans","Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:100%;line-height:1.4;background:#fff;color:#000;-webkit-overflow-scrolling:touch}body{margin:1.2em;font-size:1rem}@media (min-width:20rem){body{font-size:calc(1rem + .00625*(100vw - 20rem))}}@media (min-width:40rem){body{font-size:1.125rem}}body article,body footer,body header,body main{position:relative;max-width:40rem;margin:0 auto}body>header{margin-bottom:3.5em}body>header h1{margin:0;font-size:1.5em}body>header p{margin:0;font-size:.85em}body>footer{margin-top:6em;padding-bottom:1.5em;text-align:center;font-size:.8rem;color:#aaa}details,nav{margin:1em 0}nav ul{list-style:none;margin:0;padding:0}nav li{display:inline-block;margin-right:1em;margin-bottom:.25em}nav li:last-child{margin-right:0}a,nav a:visited{color:#0064c1}article header h1 a:visited:hover,article header h2 a:visited:hover,nav a:hover{color:#f00000}ol,ul{margin-top:0;padding-top:0;padding-left:2.5em}article header h1+p,article header h2+p,ol li+li,ul li+li{margin-top:.25em}ol li>details,ul li>details{margin:0}p{margin:1em 0;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}aside:first-child,form legend:first-child+label,p:first-child{margin-top:0}aside:last-child,p:last-child{margin-bottom:0}p+ol,p+ul{margin-top:-.75em}p img,p picture{float:right;margin-bottom:.5em;margin-left:.5em}p picture img{float:none;margin:0}blockquote,dd{padding-left:2.5em}dd{margin-bottom:1em;margin-left:0}dt{font-weight:700}blockquote{margin:0}aside{margin:.5em 0;font-style:italic;color:#aaa}@media (min-width:65rem){aside{position:absolute;right:-12.5rem;width:9.375rem;max-width:9.375rem;margin:0;padding-left:.5em;font-size:.8em;border-left:1px solid #f2f2f2}}section+section{margin-top:2em}h1,h2,h3,h4,h5,h6{margin:1.25em 0 0;line-height:1.2}h1:focus>a[href^="#"][id]:empty,h1:hover>a[href^="#"][id]:empty,h2:focus>a[href^="#"][id]:empty,h2:hover>a[href^="#"][id]:empty,h3:focus>a[href^="#"][id]:empty,h3:hover>a[href^="#"][id]:empty,h4:focus>a[href^="#"][id]:empty,h4:hover>a[href^="#"][id]:empty,h5:focus>a[href^="#"][id]:empty,h5:hover>a[href^="#"][id]:empty,h6:focus>a[href^="#"][id]:empty,h6:hover>a[href^="#"][id]:empty{opacity:1}figure+p,h1+details,h1+p,h2+details,h2+p,h3+details,h3+p,h4+details,h4+p,h5+details,h5+p,h6+details,h6+p{margin-top:.5em}h1>a[href^="#"][id]:empty,h2>a[href^="#"][id]:empty,h3>a[href^="#"][id]:empty,h4>a[href^="#"][id]:empty,h5>a[href^="#"][id]:empty,h6>a[href^="#"][id]:empty{position:absolute;left:-.65em;opacity:0;text-decoration:none;font-weight:400;line-height:1;color:#aaa}@media (min-width:40rem){h1>a[href^="#"][id]:empty,h2>a[href^="#"][id]:empty,h3>a[href^="#"][id]:empty,h4>a[href^="#"][id]:empty,h5>a[href^="#"][id]:empty,h6>a[href^="#"][id]:empty{left:-.8em}}h1>a[href^="#"][id]:empty:focus,h1>a[href^="#"][id]:empty:hover,h1>a[href^="#"][id]:empty:target,h2>a[href^="#"][id]:empty:focus,h2>a[href^="#"][id]:empty:hover,h2>a[href^="#"][id]:empty:target,h3>a[href^="#"][id]:empty:focus,h3>a[href^="#"][id]:empty:hover,h3>a[href^="#"][id]:empty:target,h4>a[href^="#"][id]:empty:focus,h4>a[href^="#"][id]:empty:hover,h4>a[href^="#"][id]:empty:target,h5>a[href^="#"][id]:empty:focus,h5>a[href^="#"][id]:empty:hover,h5>a[href^="#"][id]:empty:target,h6>a[href^="#"][id]:empty:focus,h6>a[href^="#"][id]:empty:hover,h6>a[href^="#"][id]:empty:target{opacity:1;box-shadow:none;color:#000}h1>a[href^="#"][id]:empty:target:focus,h2>a[href^="#"][id]:empty:target:focus,h3>a[href^="#"][id]:empty:target:focus,h4>a[href^="#"][id]:empty:target:focus,h5>a[href^="#"][id]:empty:target:focus,h6>a[href^="#"][id]:empty:target:focus{outline:0}h1>a[href^="#"][id]:empty::before,h2>a[href^="#"][id]:empty::before,h3>a[href^="#"][id]:empty::before,h4>a[href^="#"][id]:empty::before,h5>a[href^="#"][id]:empty::before,h6>a[href^="#"][id]:empty::before{content:"§ "}h1{font-size:2.5em}h2{font-size:1.75em}h3{font-size:1.25em}h4{font-size:1.15em}a abbr,h5,h6{font-size:1em}h6{margin-top:1em}article+article{margin-top:4em}article header p{font-size:.6em;color:#aaa}article header p+h1,article header p+h2{margin-top:-.25em}article header h1 a,article header h2 a{color:#000}article header h1 a:visited,article header h2 a:visited,h6,legend{color:#aaa}article>footer{margin-top:1.5em;font-size:.85em}a:visited{color:#8d39d0}a:active,a:hover{outline-width:0}a:hover{color:#f00000}abbr{margin-right:-.075em;text-decoration:none;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none;letter-spacing:.075em;font-size:.9em}img,picture{display:block;max-width:100%;margin:0 auto}audio,video{width:100%;max-width:100%}figure{margin:1em 0 .5em;padding:0}figure figcaption{opacity:.65;font-size:.85em}table{display:inline-block;border-spacing:0;border-collapse:collapse;overflow-x:auto;max-width:100%;text-align:left;vertical-align:top;background:linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 0 0,linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 100% 0;background-attachment:scroll,scroll;background-size:1px 100%,1px 100%;background-repeat:no-repeat,no-repeat}table caption{font-size:.9em;background:#fff}table td,table th{padding:.35em .75em;vertical-align:top;font-size:.9em;border:1px solid #f2f2f2;border-top:0;border-left:0}table td:first-child,table th:first-child{padding-left:0;background-image:linear-gradient(to right,#fff 50%,rgba(255,255,255,0) 100%);background-size:2px 100%;background-repeat:no-repeat}table td:last-child,table th:last-child{padding-right:0;border-right:0;background-image:linear-gradient(to left,#fff 50%,rgba(255,255,255,0) 100%);background-position:100% 0;background-size:2px 100%;background-repeat:no-repeat}table td:only-child,table th:only-child{background-image:linear-gradient(to right,#fff 50%,rgba(255,255,255,0) 100%),linear-gradient(to left,#fff 50%,rgba(255,255,255,0) 100%);background-position:0 0,100% 0;background-size:2px 100%,2px 100%;background-repeat:no-repeat,no-repeat}table th{line-height:1.2}form{margin-right:auto;margin-left:auto}@media (min-width:40rem){form{max-width:80%}}form label,form select,output{display:block}form label:not(:first-child){margin-top:1em}form p label{display:inline}form p label+label{margin-left:1em}form input[type],form select,form textarea{margin-bottom:1em}form input[type=checkbox],form input[type=radio]{margin-bottom:0}button,fieldset{margin:0;border:1px solid #aaa}fieldset{padding:.5em 1em}button{outline:0;box-sizing:border-box;height:2em;padding:calc(.25em - 1px) .5em;font-family:inherit;font-size:1em;border-radius:2px;background:#fff;display:inline-block;width:auto;background:#f2f2f2;color:#000;cursor:pointer}button:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus,input[type^=date]:focus,select:focus{border:1px solid #000}button:not([disabled]):hover,input[type=button]:not([disabled]):hover,input[type=file]:not([disabled]):hover,input[type=reset]:not([disabled]):hover,input[type=submit]:not([disabled]):hover,select:not([disabled]):hover{border:1px solid #000}button:active,select:active{background-color:#aaa}button[disabled],select[disabled]{color:#aaa;cursor:not-allowed}select{outline:0;box-sizing:border-box;height:2em;margin:0;padding:calc(.25em - 1px) .5em;font-family:inherit;font-size:1em;border:1px solid #aaa;border-radius:2px;background:#fff;display:inline-block;width:auto;background:#f2f2f2;color:#000;cursor:pointer;padding-right:1.2em;background-position:top 55% right .35em;background-size:.5em;background-repeat:no-repeat;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 3 2'%3E%3Cpath fill='rgb(170, 170, 170)' fill-rule='nonzero' d='M1.5 2L3 0H0z'/%3E%3C/svg%3E")}select:not([disabled]):focus,select:not([disabled]):hover{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 3 2'%3E%3Cpath fill='rgb(0, 0, 0)' fill-rule='nonzero' d='M1.5 2L3 0H0z'/%3E%3C/svg%3E")}input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],input[type^=date]{outline:0;box-sizing:border-box;height:2em;margin:0;padding:calc(.25em - 1px) .5em;font-family:inherit;font-size:1em;border:1px solid #aaa;border-radius:2px;background:#fff;color:#000;display:block;width:100%;line-height:calc(2em - 1px*2 - (.25em - 1px)*2);-webkit-appearance:none;-moz-appearance:none;appearance:none}input[type=email]::-moz-placeholder,input[type=month]::-moz-placeholder,input[type=number]::-moz-placeholder,input[type=password]::-moz-placeholder,input[type=search]::-moz-placeholder,input[type=tel]::-moz-placeholder,input[type=text]::-moz-placeholder,input[type=time]::-moz-placeholder,input[type=url]::-moz-placeholder,input[type=week]::-moz-placeholder,input[type^=date]::-moz-placeholder{color:#aaa}input[type=email]::-webkit-input-placeholder,input[type=month]::-webkit-input-placeholder,input[type=number]::-webkit-input-placeholder,input[type=password]::-webkit-input-placeholder,input[type=search]::-webkit-input-placeholder,input[type=tel]::-webkit-input-placeholder,input[type=text]::-webkit-input-placeholder,input[type=time]::-webkit-input-placeholder,input[type=url]::-webkit-input-placeholder,input[type=week]::-webkit-input-placeholder,input[type^=date]::-webkit-input-placeholder{color:#aaa}input[type=email]:-ms-input-placeholder,input[type=month]:-ms-input-placeholder,input[type=number]:-ms-input-placeholder,input[type=password]:-ms-input-placeholder,input[type=search]:-ms-input-placeholder,input[type=tel]:-ms-input-placeholder,input[type=text]:-ms-input-placeholder,input[type=time]:-ms-input-placeholder,input[type=url]:-ms-input-placeholder,input[type=week]:-ms-input-placeholder,input[type^=date]:-ms-input-placeholder{color:#aaa}input[type=button],input[type=reset],input[type=submit]{outline:0;box-sizing:border-box;height:2em;margin:0;padding:calc(.25em - 1px) .5em;font-family:inherit;font-size:1em;border:1px solid #aaa;border-radius:2px;background:#fff;display:inline-block;width:auto;background:#f2f2f2;color:#000;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none}input[type=button]:focus,input[type=reset]:focus,input[type=submit]:focus{border:1px solid #000}input[type=button]:active,input[type=reset]:active,input[type=submit]:active{background-color:#aaa}input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled]{color:#aaa;cursor:not-allowed}input[type=color]{outline:0;box-sizing:border-box;height:2em;margin:0;padding:calc(.25em - 1px) .5em;font-family:inherit;font-size:1em;border:1px solid #aaa;border-radius:2px;background:#fff;color:#000;display:block;line-height:calc(2em - 1px*2 - (.25em - 1px)*2);-webkit-appearance:none;-moz-appearance:none;appearance:none;width:6em}input[type=color]:focus{border:1px solid #000}input[type=color]::-moz-placeholder,textarea::-moz-placeholder{color:#aaa}input[type=color]::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#aaa}input[type=color]:-ms-input-placeholder{color:#aaa}input[type=color]:hover{border:1px solid #000}input[type=file]{outline:0;box-sizing:border-box;margin:0;padding:calc(.25em - 1px) .5em;font-family:inherit;border:1px solid #aaa;border-radius:2px;background:#fff;background:#f2f2f2;color:#000;cursor:pointer;display:block;width:100%;height:auto;padding:.75em .5em;font-size:12px;line-height:1}input[type=file]:focus,textarea:focus{border:1px solid #000}input[type=file]:active{background-color:#aaa}input[type=file][disabled]{color:#aaa;cursor:not-allowed}input[type=checkbox],input[type=radio]{margin:-.2em .75em 0 0;vertical-align:middle}textarea{outline:0;box-sizing:border-box;margin:0;padding:calc(.25em - 1px) .5em;font-family:inherit;font-size:1em;border:1px solid #aaa;border-radius:2px;background:#fff;color:#000;display:block;width:100%;line-height:calc(2em - 1px*2 - (.25em - 1px)*2);-webkit-appearance:none;-moz-appearance:none;appearance:none;height:4.5em;resize:vertical;padding-top:.5em;padding-bottom:.5em}textarea:-ms-input-placeholder{color:#aaa}code,kbd,samp,var{font-family:Consolas,"Lucida Console",Monaco,monospace;font-style:normal}pre{overflow-x:auto;font-size:.8em;background:linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 0 0,linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 100% 0;background-attachment:scroll,scroll;background-size:1px 100%,1px 100%;background-repeat:no-repeat,no-repeat}pre>code,summary{display:inline-block}pre>code{overflow-x:visible;box-sizing:border-box;min-width:100%;border-right:3px solid #fff;border-left:1px solid #fff}hr{height:1px;margin:2em 0;border:0;background:#f2f2f2}details[open]{padding-bottom:.5em;border-bottom:1px solid #f2f2f2}summary{font-weight:700;border-bottom:1px dashed;cursor:pointer}noscript{color:#d00000}::selection{background:rgba(0,100,193,.25)}