Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial checkin

  • Loading branch information...
commit b91439de22da374abffc81e7c4310d356e2f3286 0 parents
@maxogden authored
2  .gitignore
@@ -0,0 +1,2 @@
+bundle.js
+node_modules
4 demo/demo.js
@@ -0,0 +1,4 @@
+var toolbar = require('toolbar')
+toolbar('.bar-tab').on('select', function(selected) {
+ console.log(selected)
+})
BIN  demo/icons/coffeemug.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  demo/icons/cow.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  demo/icons/eggs.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  demo/icons/poptart.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  demo/icons/toast.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 demo/index.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Voxel HUD</title>
+
+ <!-- Sets initial viewport load and disables zooming -->
+ <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
+
+ <!-- Makes your prototype chrome-less once bookmarked to your phone's home screen -->
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+
+ <!-- Set a shorter title for iOS6 devices when saved to home screen -->
+ <meta name="apple-mobile-web-app-title" content="Voxel HUD">
+
+ <!-- Include the compiled Ratchet CSS -->
+ <link rel="stylesheet" href="ratchet.css">
+ <style type="text/css">
+ li:hover { cursor: hand; cursor: pointer; }
+ </style>
+ </head>
+ <body>
+ <div class="content">
+ <p>
+ Icons designed by <a href="http://thenounproject.com/jacob" target="_blank">Jacob Halton</a> from The Noun Project
+ </p>
+ <nav class="bar-tab">
+ <ul class="tab-inner">
+ <li class="tab-item active">
+ <img class="tab-icon" src="icons/coffeemug.png">
+ <div class="tab-label">Mug</div>
+ </li>
+ <li class="tab-item">
+ <img class="tab-icon" src="icons/cow.png">
+ <div class="tab-label">Cow</div>
+ </li>
+ <li class="tab-item">
+ <img class="tab-icon" src="icons/eggs.png">
+ <div class="tab-label">Eggs</div>
+ </li>
+ <li class="tab-item">
+ <img class="tab-icon" src="icons/poptart.png">
+ <div class="tab-label">Poptart</div>
+ </li>
+ <li class="tab-item">
+ <img class="tab-icon" src="icons/toast.png">
+ <div class="tab-label">Toast</div>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ <script type="text/javascript" src="bundle.js"></script>
+ </body>
+</html>
1,260 demo/ratchet.css
@@ -0,0 +1,1260 @@
+/**
+ * ==================================
+ * Ratchet v1.0.0
+ * Licensed under The MIT License
+ * http://opensource.org/licenses/MIT
+ * ==================================
+ */
+
+/* Hard reset
+-------------------------------------------------- */
+
+html,
+body,
+div,
+span,
+iframe,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+blockquote,
+pre,
+a,
+abbr,
+acronym,
+address,
+big,
+cite,
+code,
+del,
+dfn,
+em,
+img,
+ins,
+kbd,
+q,
+s,
+samp,
+small,
+strike,
+strong,
+sub,
+sup,
+tt,
+var,
+b,
+u,
+i,
+center,
+dl,
+dt,
+dd,
+ol,
+ul,
+li,
+fieldset,
+form,
+label,
+legend,
+table,
+caption,
+tbody,
+tfoot,
+thead,
+tr,
+th,
+td,
+article,
+aside,
+canvas,
+details,
+embed,
+figure,
+figcaption,
+footer,
+header,
+hgroup,
+menu,
+nav,
+output,
+section,
+summary,
+time,
+audio,
+video {
+ padding: 0;
+ margin: 0;
+ border: 0;
+}
+
+/* Prevents iOS text size adjust after orientation change, without disabling (Thanks to @necolas) */
+html {
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+}
+
+/* Base styles
+-------------------------------------------------- */
+
+body {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ font: 14px/1.25 "Helvetica Neue", sans-serif;
+ color: #222;
+ background-color: #fff;
+}
+
+/* Universal link styling */
+a {
+ color: #0882f0;
+ text-decoration: none;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0); /* Removes the dark touch outlines on links */
+}
+
+/* Wrapper to be used around all content not in .bar-title and .bar-tab */
+.content {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ overflow: auto;
+ background-color: #fff;
+ -webkit-transition-property: top, bottom;
+ transition-property: top, bottom;
+ -webkit-transition-duration: .2s, .2s;
+ transition-duration: .2s, .2s;
+ -webkit-transition-timing-function: linear, linear;
+ transition-timing-function: linear, linear;
+ -webkit-overflow-scrolling: touch;
+}
+
+/* Hack to force all relatively and absolutely positioned elements still render while scrolling
+ Note: This is a bug for "-webkit-overflow-scrolling: touch" */
+.content > * {
+ -webkit-transform: translateZ(0px);
+ transform: translateZ(0px);
+}
+
+/* Utility wrapper to pad in components like forms, block buttons and segmented-controllers so they're not full-bleed */
+.content-padded {
+ padding: 10px;
+}
+
+/* Pad top/bottom of content so it doesn't hide behind .bar-title and .bar-tab.
+ Note: For these to work, content must come after both bars in the markup */
+.bar-title ~ .content {
+ top: 44px;
+}
+.bar-tab ~ .content {
+ bottom: 51px;
+}
+.bar-header-secondary ~ .content {
+ top: 88px;
+}/* General bar styles
+-------------------------------------------------- */
+
+[class*="bar-"] {
+ position: fixed;
+ right: 0;
+ left: 0;
+ z-index: 10;
+ height: 44px;
+ padding: 5px;
+ box-sizing: border-box;
+}
+
+/* Modifier class to dock any bar below .bar-title */
+.bar-header-secondary {
+ top: 45px;
+}
+
+/* Modifier class to dock any bar to bottom of viewport */
+.bar-footer {
+ bottom: 0;
+}
+
+/* Generic bar for wrapping buttons, segmented controllers, etc. */
+.bar-standard {
+ background-color: #f2f2f2;
+ background-image: -webkit-linear-gradient(top, #f2f2f2 0, #e5e5e5 100%);
+ background-image: linear-gradient(to bottom, #f2f2f2 0, #e5e5e5 100%);
+ border-bottom: 1px solid #aaa;
+ box-shadow: inset 0 1px 1px -1px #fff;
+}
+
+/* Flip border position to top for footer bars */
+.bar-footer.bar-standard,
+.bar-footer-secondary.bar-standard {
+ border-top: 1px solid #aaa;
+ border-bottom-width: 0;
+}
+
+/* Title bar
+-------------------------------------------------- */
+
+/* Bar docked to top of viewport for showing page title and actions */
+.bar-title {
+ top: 0;
+ display: -webkit-box;
+ display: box;
+ background-color: #1eb0e9;
+ background-image: -webkit-linear-gradient(top, #1eb0e9 0, #109adc 100%);
+ background-image: linear-gradient(to bottom, #1eb0e9 0, #109adc 100%);
+ border-bottom: 1px solid #0e5895;
+ box-shadow: inset 0 1px 1px -1px rgba(255, 255, 255, .8);
+ -webkit-box-orient: horizontal;
+ box-orient: horizontal;
+}
+
+/* Centered text in the .bar-title */
+.bar-title .title {
+ position: absolute;
+ top: 0;
+ left: 0;
+ display: block;
+ width: 100%;
+ font-size: 20px;
+ font-weight: bold;
+ line-height: 44px;
+ color: #fff;
+ text-align: center;
+ text-shadow: 0 -1px rgba(0, 0, 0, .5);
+ white-space: nowrap;
+}
+
+.bar-title > a:not([class*="button"]) {
+ display: block;
+ width: 100%;
+ height: 100%;
+}
+
+/* Retain specified title color */
+.bar-title .title a {
+ color: inherit;
+}
+
+/* Tab bar
+-------------------------------------------------- */
+
+/* Bar docked to bottom used for primary app navigation */
+.bar-tab {
+ bottom: 0;
+ height: 50px;
+ padding: 0;
+ background-color: #393939;
+ background-image: -webkit-linear-gradient(top, #393939 0, #2b2b2b 100%);
+ background-image: linear-gradient(to bottom, #393939 0, #2b2b2b 100%);
+ border-top: 1px solid #000;
+ border-bottom-width: 0;
+ box-shadow: inset 0 1px 1px -1px rgba(255, 255, 255, .6);
+}
+
+/* Wrapper for individual tab */
+.tab-inner {
+ display: -webkit-box;
+ display: box;
+ height: 100%;
+ list-style: none;
+ -webkit-box-orient: horizontal;
+ box-orient: horizontal;
+}
+
+/* Navigational tab */
+.tab-item {
+ height: 100%;
+ padding-top: 9px;
+ text-align: center;
+ box-sizing: border-box;
+ -webkit-box-flex: 1;
+ box-flex: 1;
+}
+
+/* Active state for tab */
+.tab-item.active {
+ box-shadow: inset 0 0 20px rgba(0, 0, 0, .5);
+}
+
+/* Icon for tab */
+.tab-icon {
+ display: block;
+ height: 18px;
+ margin: 0 auto;
+}
+
+/* Label for tab */
+.tab-label {
+ margin-top: 1px;
+ font-size: 10px;
+ font-weight: bold;
+ color: #fff;
+ text-shadow: 0 1px rgba(0, 0, 0, .3);
+}
+
+/* Buttons in title bars
+-------------------------------------------------- */
+
+/* Generic style for all buttons in .bar-title */
+.bar-title [class*="button"] {
+ position: relative;
+ z-index: 10; /* Places buttons over full width title */
+ font-size: 12px;
+ line-height: 23px;
+ color: #fff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, .3);
+ background-color: #1eb0e9;
+ background-image: -webkit-linear-gradient(top, #1eb0e9 0, #0984c6 100%);
+ background-image: linear-gradient(to bottom, #1eb0e9 0, #0984c6 100%);
+ border: 1px solid #0e5895;
+ box-shadow: 0 1px rgba(255, 255, 255, .25);
+ -webkit-box-flex: 0;
+ box-flex: 0;
+}
+
+
+/* Hacky way to right align buttons outside of flex-box system
+ Note: is only absolutely positioned button, would be better if flex-box had an "align right" option */
+.bar-title .title + [class*="button"]:last-child,
+.bar-title .button + [class*="button"]:last-child,
+.bar-title [class*="button"].pull-right {
+ position: absolute;
+ top: 5px;
+ right: 5px;
+}
+
+/* Override standard button active states */
+.bar-title .button:active {
+ color: #fff;
+ background-color: #0876b1;
+}
+
+/* Directional buttons in title bars (thanks to @GregorAdams for solution - http://cssnerd.com/2011/11/30/the-best-pure-css3-ios-style-arrow-back-button/)
+-------------------------------------------------- */
+
+/* Add relative positioning so :before content is positioned properly */
+.bar-title .button-prev,
+.bar-title .button-next {
+ position: relative;
+}
+
+/* Prev/next button base styles */
+.bar-title .button-prev {
+ margin-left: 7px; /* Push over to make room for :before content */
+ border-left: 0;
+ border-bottom-left-radius: 10px 15px;
+ border-top-left-radius: 10px 15px;
+}
+.bar-title .button-next {
+ margin-right: 7px; /* Push over to make room for :before content */
+ border-right: 0;
+ border-top-right-radius: 10px 15px;
+ border-bottom-right-radius: 10px 15px;
+}
+
+/* Pointed part of directional button */
+.bar-title .button-prev:before,
+.bar-title .button-next:before {
+ position: absolute;
+ top: 2px;
+ width: 27px;
+ height: 27px;
+ border-radius: 30px 100px 2px 40px / 2px 40px 30px 100px;
+ content: '';
+ box-shadow: inset 1px 0 #0e5895, inset 0 1px #0e5895;
+ -webkit-mask-image: -webkit-gradient(linear, left top, right bottom, from(#000), color-stop(.33, #000), color-stop(.5, transparent), to(transparent));
+ mask-image: gradient(linear, left top, right bottom, from(#000), color-stop(.33, #000), color-stop(.5, transparent), to(transparent));
+}
+.bar-title .button-prev:before {
+ left: -5px;
+ background-image: -webkit-gradient(linear, left bottom, right top, from(#0984c6), to(#1eb0e9));
+ background-image: gradient(linear, left bottom, right top, from(#0984c6), to(#1eb0e9));
+ border-left: 1.5px solid rgba(255, 255, 255, .25);
+ -webkit-transform: rotate(-45deg) skew(-10deg, -10deg);
+ transform: rotate(-45deg) skew(-10deg, -10deg);
+}
+.bar-title .button-next:before {
+ right: -5px;
+ background-image: -webkit-gradient(linear, left bottom, right top, from(#1eb0e9), to(#0984c6));
+ background-image: gradient(linear, left bottom, right top, from(#1eb0e9), to(#0984c6));
+ border-top: 1.5px solid rgba(255, 255, 255, .25);
+ -webkit-transform: rotate(135deg) skew(-10deg, -10deg);
+ transform: rotate(135deg) skew(-10deg, -10deg);
+}
+
+/* Active states for the directional buttons */
+.bar-title .button-prev:active,
+.bar-title .button-next:active,
+.bar-title .button-prev:active:before,
+.bar-title .button-next:active:before {
+ color: #fff;
+ background-color: #0876b1;
+ background-image: none;
+}
+.bar-title .button-prev:active:before,
+.bar-title .button-next:active:before {
+ content: '';
+}
+.bar-title .button-prev:active:before {
+ box-shadow: inset 0 3px 3px rgba(0, 0, 0, .2);
+}
+.bar-title .button-next:active:before {
+ box-shadow: inset 0 -3px 3px rgba(0, 0, 0, .2);
+}
+
+/* Block buttons in any bar
+-------------------------------------------------- */
+
+/* Add proper padding and replace buttons normal dropshadow with a shine from bar */
+[class*="bar"] .button-block {
+ padding: 7px 0;
+ margin-bottom: 0;
+ box-shadow: inset 0 1px 1px rgba(255, 255, 255, .4), 0 1px rgba(255, 255, 255, .8);
+}
+
+/* Override standard padding changes for .button-blocks */
+[class*="bar"] .button-block:active {
+ padding: 7px 0;
+}
+
+/* Segmented controller in any bar
+-------------------------------------------------- */
+
+/* Remove standard segmented bottom margin */
+[class*="bar-"] .segmented-controller {
+ margin-bottom: 0;
+}
+
+/* Add margins between segmented controllers and buttons */
+[class*="bar-"] .segmented-controller + [class*="button"],
+[class*="bar-"] [class*="button"] + .segmented-controller {
+ margin-left: 5px;
+}
+
+/* Segmented controller in a title bar
+-------------------------------------------------- */
+
+.bar-title .segmented-controller {
+ line-height: 18px;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.3);
+ background-color: #1eb0e9;
+ background-image: -webkit-linear-gradient(top, #1eb0e9 0, #0984c6 100%);
+ background-image: linear-gradient(to bottom, #1eb0e9 0, #0984c6 100%);
+ border: 1px solid #0e5895;
+ border-radius: 3px;
+ box-shadow: 0 1px rgba(255, 255, 255, .25);
+ -webkit-box-flex: 1;
+ box-flex: 1;
+}
+
+/* Set color for tab border and highlight */
+.bar-title .segmented-controller li {
+ border-left: 1px solid #0e5895;
+ box-shadow: inset 1px 0 rgba(255, 255, 255, .25);
+}
+
+/* Remove inset shadow from first tab or one to the right of the active tab */
+.bar-title .segmented-controller .active + li,
+.bar-title .segmented-controller li:first-child {
+ box-shadow: none;
+}
+
+/* Remove left-hand border from first tab */
+.bar-title .segmented-controller li:first-child {
+ border-left-width: 0;
+}
+
+/* Depressed state (active) */
+.bar-title .segmented-controller li.active {
+ background-color: #0082c4;
+ box-shadow: inset 0 1px 6px rgba(0, 0, 0, .3);
+}
+
+/* Set color of links to white */
+.bar-title .segmented-controller li > a {
+ color: #fff;
+}
+
+
+/* Search forms in standard bar
+-------------------------------------------------- */
+
+/* Position/size search bar within the bar */
+.bar-standard input[type=search] {
+ height: 32px;
+ margin: 0;
+}/* Lists
+-------------------------------------------------- */
+
+/* Remove usual bullet styles from list */
+.list {
+ margin-bottom: 10px;
+ list-style: none;
+ background-color: #fff;
+}
+
+/* Pad each list item and add dividers */
+.list li {
+ position: relative;
+ padding: 20px 60px 20px 10px; /* Given extra right padding to accomodate counts, chevrons or buttons */
+ border-bottom: 1px solid rgba(0, 0, 0, .1);
+}
+
+/* Give top border to first list items */
+.list li:first-child {
+ border-top: 1px solid rgba(0, 0, 0, .1);
+}
+
+/* If a list of links, make sure the child <a> takes up full list item tap area (want to avoid selecting child buttons though) */
+.list li > a:not([class*="button"]) {
+ position: relative;
+ display: block;
+ padding: inherit;
+ margin: -20px -60px -20px -10px;
+ color: inherit;
+}
+
+/* Inset list
+-------------------------------------------------- */
+
+.list.inset {
+ width: auto;
+ margin-right: 10px;
+ margin-left: 10px;
+ border: 1px solid rgba(0, 0, 0, .1);
+ border-radius: 6px;
+ box-sizing: border-box;
+}
+
+/* Remove border from first/last standard list items to avoid double border at top/bottom of lists */
+.list.inset li:first-child {
+ border-top-width: 0;
+}
+.list.inset li:last-child {
+ border-bottom-width: 0;
+}
+
+
+/* List dividers
+-------------------------------------------------- */
+
+.list .list-divider {
+ position: relative;
+ top: -1px;
+ padding-top: 6px;
+ padding-bottom: 6px;
+ font-size: 12px;
+ font-weight: bold;
+ line-height: 18px;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
+ background-color: #f8f8f8;
+ background-image: -webkit-linear-gradient(top, #f8f8f8 0, #eee 100%);
+ background-image: linear-gradient(to bottom, #f8f8f8 0, #eee 100%);
+ border-top: 1px solid rgba(0, 0, 0, .1);
+ border-bottom: 1px solid rgba(0, 0, 0, .1);
+ box-shadow: inset 0 1px 1px rgba(255, 255, 255, .4);
+}
+
+/* Rounding first divider on inset lists and remove border on the top */
+.list.inset .list-divider:first-child {
+ top: 0;
+ border-top-width: 0;
+ border-radius: 6px 6px 0 0;
+}
+
+/* Rounding last divider on inset lists */
+.list.inset .list-divider:last-child {
+ border-radius: 0 0 6px 6px;
+}
+
+/* Right-aligned subcontent in lists (chevrons, buttons, counts and toggles)
+-------------------------------------------------- */
+.list .chevron,
+.list [class*="button"],
+.list [class*="count"],
+.list .toggle {
+ position: absolute;
+ top: 50%;
+ right: 10px;
+}
+
+ /* Position chevrons/counts vertically centered on the right in list items */
+.list .chevron,
+.list [class*="count"] {
+ margin-top: -10px; /* Half height of chevron */
+}
+
+/* Push count over if there's a sibling chevron */
+.list .chevron + [class*="count"] {
+ right: 30px;
+}
+
+/* Position buttons vertically centered on the right in list items */
+.list [class*="button"] {
+ left: auto;
+ margin-top: -14px; /* Half height of button */
+}
+
+.list .toggle {
+ margin-top: -15px; /* Half height of toggle */
+}/* Forms
+-------------------------------------------------- */
+
+/* Force form elements to inherit font styles */
+input,
+textarea,
+button,
+select {
+ font-family: inherit;
+ font-size: inherit;
+}
+
+/* Stretch inputs/textareas to full width and add height to maintain a consistent baseline */
+select,
+textarea,
+input[type="text"],
+input[type=search],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="tel"],
+input[type="color"],
+.input-group {
+ width: 100%;
+ height: 40px;
+ padding: 10px;
+ margin-bottom: 10px;
+ background-color: #fff;
+ border: 1px solid rgba(0, 0, 0, .2);
+ border-radius: 3px;
+ box-shadow: 0 1px 1px rgba(255, 255, 255, .2), inset 0 1px 1px rgba(0, 0, 0, .1);
+ -webkit-appearance: none;
+ box-sizing: border-box;
+}
+
+/* Fully round search input */
+input[type=search] {
+ height: 34px;
+ font-size: 14px;
+ border-radius: 30px;
+}
+
+/* Allow text area's height to grow larger than a normal input */
+textarea {
+ height: auto;
+}
+
+/* Style select button to look like part of the Ratchet's style */
+select {
+ height: auto;
+ font-size: 14px;
+ background-color: #f8f8f8;
+ background-image: -webkit-linear-gradient(top, #f8f8f8 0%, #d4d4d4 100%);
+ background-image: linear-gradient(to bottom, #f8f8f8 0%, #d4d4d4 100%);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .1);
+}
+
+
+/* Input groups (cluster multiple inputs together into a single group)
+-------------------------------------------------- */
+
+/* Reset from initial form setup styles */
+.input-group {
+ width: auto;
+ height: auto;
+ padding: 0;
+}
+
+/* Remove spacing, borders, shadows and rounding since it all belongs on the .input-group not the input */
+.input-group input {
+ margin-bottom: 0;
+ background-color: transparent;
+ border: 0;
+ border-bottom: 1px solid rgba(0, 0, 0, .2);
+ border-radius: 0;
+ box-shadow: none;
+}
+
+/* Remove bottom border on last input to avoid double bottom border */
+.input-group input:last-child {
+ border-bottom-width: 0;
+}
+
+/* Input groups with labels
+-------------------------------------------------- */
+
+/* To use labels with input groups, wrap a label and an input in an .input-row */
+.input-row {
+ overflow: hidden;
+ border-bottom: 1px solid rgba(0, 0, 0, .2);
+}
+
+/* Remove bottom border on last input-row to avoid double bottom border */
+.input-row:last-child {
+ border-bottom-width: 0;
+}
+
+/* Labels get floated left with a set percentage width */
+.input-row label {
+ float: left;
+ width: 25%;
+ padding: 11px 10px 9px 13px; /* Optimizing the baseline for mobile. */
+ font-weight: bold;
+}
+
+/* Actual inputs float to right of labels and also have a set percentage */
+.input-row label + input {
+ float: right;
+ width: 65%;
+ padding-left: 0;
+ margin-bottom: 0;
+ border-bottom: 0;
+}/* General button styles
+-------------------------------------------------- */
+
+[class*="button"] {
+ position: relative;
+ display: inline-block;
+ padding: 4px 12px;
+ margin: 0;
+ font-weight: bold;
+ line-height: 18px;
+ color: #333;
+ text-align: center;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ vertical-align: top;
+ cursor: pointer;
+ background-color: #f8f8f8;
+ background-image: -webkit-linear-gradient(top, #f8f8f8 0, #d4d4d4 100%);
+ background-image: linear-gradient(to bottom, #f8f8f8 0, #d4d4d4 100%);
+ border: 1px solid rgba(0, 0, 0, .3);
+ border-radius: 3px;
+ box-shadow: inset 0 1px 1px rgba(255, 255, 255, .4), 0 1px 2px rgba(0, 0, 0, .05);
+}
+
+/* Active */
+[class*="button"]:active {
+ padding-top: 5px;
+ padding-bottom: 3px;
+ color: #333;
+ background-color: #ccc;
+ background-image: none;
+ box-shadow: inset 0 3px 3px rgba(0, 0, 0, .2);
+}
+
+/* Button modifiers
+-------------------------------------------------- */
+
+/* Overriding styles for buttons with modifiers */
+.button-main,
+.button-positive,
+.button-negative {
+ color: #fff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, .3);
+}
+
+/* Main button */
+.button-main {
+ background-color: #1eafe7;
+ background-image: -webkit-linear-gradient(top, #1eafe7 0, #1a97c8 100%);
+ background-image: linear-gradient(to bottom, #1eafe7 0, #1a97c8 100%);
+ border: 1px solid #117aaa;
+}
+
+/* Positive button */
+.button-positive {
+ background-color: #34ba15;
+ background-image: -webkit-linear-gradient(top, #34ba15 0, #2da012 100%);
+ background-image: linear-gradient(to bottom, #34ba15 0, #2da012 100%);
+ border: 1px solid #278f0f;
+}
+
+/* Negative button */
+.button-negative {
+ background-color: #e71e1e;
+ background-image: -webkit-linear-gradient(top, #e71e1e 0,#c71a1a 100%);
+ background-image: linear-gradient(to bottom, #e71e1e 0, #c71a1a 100%);
+ border: 1px solid #b51a1a;
+}
+
+/* Active state for buttons with modifiers */
+.button-main:active,
+.button-positive:active,
+.button-negative:active {
+ color: #fff;
+}
+.button-main:active {
+ background-color: #0876b1;
+}
+.button-positive:active {
+ background-color: #298f11;
+}
+.button-negative:active {
+ background-color: #b21a1a;
+}
+
+/* Block level buttons (full width buttons) */
+.button-block {
+ display: block;
+ padding: 11px 0 13px;
+ margin-bottom: 10px;
+ font-size: 16px;
+}
+
+/* Active state for block level buttons */
+.button-block:active {
+ padding: 12px 0;
+}
+
+/* Counts in buttons
+-------------------------------------------------- */
+
+/* Generic styles for all counts within buttons */
+[class*="button"] [class*="count"] {
+ padding-top: 2px;
+ padding-bottom: 2px;
+ margin-right: -4px;
+ margin-left: 4px;
+ text-shadow: none;
+ background-color: rgba(0, 0, 0, .2);
+ box-shadow: inset 0 1px 1px -1px #000000, 0 1px 1px -1px #fff;
+}
+
+/* Position counts within block level buttons
+ Note: These are absolutely positioned so that text of button isn't "pushed" by count and always
+ stays at true center of button */
+.button-block [class*="count"] {
+ position: absolute;
+ right: 0;
+ padding-top: 4px;
+ padding-bottom: 4px;
+ margin-right: 10px;
+}/* Chevrons
+-------------------------------------------------- */
+
+.chevron {
+ display: block;
+ height: 20px;
+}
+
+/* Base styles for both 1/2's of the chevron */
+.chevron:before,
+.chevron:after {
+ position: relative;
+ display: block;
+ width: 12px;
+ height: 4px;
+ background-color: #999;
+ content: '';
+}
+
+/* Position and rotate respective 1/2's of the chevron */
+.chevron:before {
+ top: 5px;
+ -webkit-transform: rotate(45deg);
+ transform: rotate(45deg);
+}
+.chevron:after {
+ top: 7px;
+ -webkit-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+}/* General count styles
+-------------------------------------------------- */
+
+[class*="count"] {
+ display: inline-block;
+ padding: 4px 9px;
+ font-size: 12px;
+ font-weight: bold;
+ line-height: 13px;
+ color: #fff;
+ background-color: rgba(0, 0, 0, .3);
+ border-radius: 100px;
+}
+
+/* Count modifiers
+-------------------------------------------------- */
+
+/* Overriding styles for counts with modifiers */
+.count-main,
+.count-positive,
+.count-negative {
+ color: #fff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, .3);
+}
+
+/* Main count */
+.count-main {
+ background-color: #1eafe7;
+ background-image: -webkit-linear-gradient(top, #1eafe7 0, #1a97c8 100%);
+ background-image: linear-gradient(to bottom, #1eafe7 0, #1a97c8 100%);
+}
+
+/* Positive count */
+.count-positive {
+ background-color: #34ba15;
+ background-image: -webkit-linear-gradient(top, #34ba15 0, #2da012 100%);
+ background-image: linear-gradient(to bottom, #34ba15 0, #2da012 100%);
+}
+
+/* Negative count */
+.count-negative {
+ background-color: #e71e1e;
+ background-image: -webkit-linear-gradient(top, #e71e1e 0,#c71a1a 100%);
+ background-image: linear-gradient(to bottom, #e71e1e 0, #c71a1a 100%);
+}/* Segmented controllers
+-------------------------------------------------- */
+
+.segmented-controller {
+ display: -webkit-box;
+ display: box;
+ margin-bottom: 10px;
+ overflow: hidden;
+ font-size: 12px;
+ font-weight: bold;
+ text-shadow: 0 1px rgba(255, 255, 255, .5);
+ list-style: none;
+ background-color: #f8f8f8;
+ background-image: -webkit-linear-gradient(top, #f8f8f8 0, #d4d4d4 100%);
+ background-image: linear-gradient(to bottom, #f8f8f8 0, #d4d4d4 100%);
+ border: 1px solid #aaa;
+ border-radius: 3px;
+ box-shadow: inset 0 1px rgba(255, 255, 255, 0.5), 0 1px rgba(255, 255, 255, .8);
+ -webkit-box-orient: horizontal;
+ box-orient: horizontal;
+}
+
+/* Section within controller */
+.segmented-controller li {
+ overflow: hidden;
+ text-align: center;
+ white-space: nowrap;
+ border-left: 1px solid #aaa;
+ box-shadow: inset 1px 0 rgba(255, 255, 255, .5);
+ -webkit-box-flex: 1;
+ box-flex: 1;
+}
+
+/* Link that fills each section */
+.segmented-controller li > a {
+ display: block;
+ padding: 8px 16px;
+ overflow: hidden;
+ line-height: 15px;
+ color: #333;
+ text-overflow: ellipsis;
+}
+
+/* Remove border-left and shadow from first section */
+.segmented-controller li:first-child {
+ border-left-width: 0;
+ box-shadow: none;
+}
+
+/* Active segment of controller */
+.segmented-controller li.active {
+ background-color: #ccc;
+ box-shadow: inset 0 1px 5px rgba(0, 0, 0, .3);
+}
+
+.segmented-controller-item {
+ display: none;
+}
+
+.segmented-controller-item.active {
+ display: block;
+}/* Popovers (to be used with popovers.js)
+-------------------------------------------------- */
+
+.popover {
+ position: fixed;
+ top: 55px;
+ left: 50%;
+ z-index: 20;
+ display: none;
+ width: 280px;
+ padding: 5px;
+ margin-left: -146px;
+ background-color: #555;
+ background-image: -webkit-linear-gradient(top, #555 5%, #555 6%, #111 30%);
+ background-image: linear-gradient(to bottom, #555 5%, #555 6%,#111 30%);
+ border: 1px solid #111;
+ border-radius: 6px;
+ opacity: 0;
+ box-shadow: inset 0 1px 1px -1px #fff, 0 3px 10px rgba(0, 0, 0, .3);
+ -webkit-transform: translate3d(0, -15px, 0);
+ transform: translate3d(0, -15px, 0);
+ -webkit-transition: -webkit-transform 0.2s ease-in-out, opacity 0.2s ease-in-out;
+ transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out;
+}
+
+/* Caret on top of popover using CSS triangles (thanks to @chriscoyier for solution) */
+.popover:before,
+.popover:after {
+ position: absolute;
+ left: 50%;
+ width: 0;
+ height: 0;
+ content: '';
+}
+.popover:before {
+ top: -20px;
+ margin-left: -21px;
+ border-right: 21px solid transparent;
+ border-bottom: 21px solid #111;
+ border-left: 21px solid transparent;
+}
+.popover:after {
+ top: -19px;
+ margin-left: -20px;
+ border-right: 20px solid transparent;
+ border-bottom: 20px solid #555;
+ border-left: 20px solid transparent;
+}
+
+/* Wrapper for a title and buttons */
+.popover-header {
+ display: -webkit-box;
+ display: box;
+ height: 34px;
+ margin-bottom: 5px;
+}
+
+/* Centered title for popover */
+.popover-header .title {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ margin: 15px 0;
+ font-size: 16px;
+ font-weight: bold;
+ line-height: 12px;
+ color: #fff;
+ text-align: center;
+ text-shadow: 0 -1px rgba(0, 0, 0, .5);
+}
+
+/* Generic style for all buttons in .popover-header */
+.popover-header [class*="button"] {
+ z-index: 25;
+ font-size: 12px;
+ line-height: 22px;
+ color: #fff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, .3);
+ background-color: #454545;
+ background-image: -webkit-linear-gradient(top, #454545 0, #353535 100%);
+ background-image: linear-gradient(to bottom, #454545 0, #353535 100%);
+ border: 1px solid #111;
+ -webkit-box-flex: 0;
+ box-flex: 0;
+}
+
+/* Hacky way to right align buttons outside of flex-box system
+ Note: is only absolutely positioned button, would be better if flex-box had an "align right" option */
+.popover-header .title + [class*="button"]:last-child,
+.popover-header .button + [class*="button"]:last-child,
+.popover-header [class*="button"].pull-right {
+ position: absolute;
+ top: 5px;
+ right: 5px;
+}
+
+/* Active state for popover header buttons */
+.popover-header .button:active {
+ color: #fff;
+ background-color: #0876b1;
+}
+
+/* Popover animation
+-------------------------------------------------- */
+
+.popover.visible {
+ opacity: 1;
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+}
+
+/* Backdrop (used as invisible touch escape)
+-------------------------------------------------- */
+
+.backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 10;
+}
+
+/* Block level buttons in popovers
+-------------------------------------------------- */
+
+/* Positioning and giving darker border to look sharp against dark popover */
+.popover .button-block {
+ margin-bottom: 5px;
+ border: 1px solid #111;
+}
+
+/* Remove extra margin on bottom of last button */
+.popover .button-block:last-child {
+ margin-bottom: 0;
+}
+
+/* Lists in popovers
+-------------------------------------------------- */
+
+.popover .list {
+ width: auto;
+ max-height: 250px;
+ margin-right: 0;
+ margin-bottom: 0;
+ margin-left: 0;
+ overflow: auto;
+ background-color: #fff;
+ border: 1px solid #000;
+ border-radius: 3px;
+ -webkit-overflow-scrolling: touch;
+}/* Slider styles (to be used with sliders.js)
+-------------------------------------------------- */
+
+/* Width/height of slider */
+.slider,
+.slider > li {
+ width: 100%;
+ height: 200px;
+}
+
+/* Outer wrapper for slider */
+.slider {
+ overflow: hidden;
+ background-color: #000;
+}
+
+/* Inner wrapper for slider (width of all slides together) */
+.slider > ul {
+ position: relative;
+ font-size: 0; /* Remove spaces from inline-block children */
+ white-space: nowrap;
+ -webkit-transition: all 0 linear;
+ transition: all 0 linear;
+}
+
+/* Individual slide */
+.slider > ul > li {
+ display: inline-block;
+ vertical-align: top; /* Ensure that li always aligns to top */
+ width: 100%;
+ height: 100%;
+}
+
+/* Required reset of font-size to same as standard body */
+.slider > ul > li > * {
+ font-size: 14px;
+}/* Toggle styles (to be used with toggles.js)
+-------------------------------------------------- */
+
+.toggle {
+ position: relative;
+ width: 75px;
+ height: 28px;
+ background-color: #eee;
+ border: 1px solid #bbb;
+ border-radius: 20px;
+ box-shadow: inset 0 0 4px rgba(0, 0, 0, .1);
+}
+
+/* Text indicating "on" or "off". Default is "off" */
+.toggle:before {
+ position: absolute;
+ right: 13px;
+ font-weight: bold;
+ line-height: 28px;
+ color: #777;
+ text-shadow: 0 1px #fff;
+ text-transform: uppercase;
+ content: "Off";
+}
+
+/* Sliding handle */
+.toggle-handle {
+ position: absolute;
+ top: -1px;
+ left: -1px;
+ z-index: 2;
+ width: 28px;
+ height: 28px;
+ background-color: #fff;
+ background-image: -webkit-linear-gradient(top, #fff 0, #f2f2f2 100%);
+ background-image: linear-gradient(to bottom, #fff 0, #f2f2f2 100%);
+ border: 1px solid rgba(0, 0, 0, .2);
+ border-radius: 100px;
+ -webkit-transition: -webkit-transform 0.1s ease-in-out, border 0.1s ease-in-out;
+ transition: transform 0.1s ease-in-out, border 0.1s ease-in-out;
+}
+
+/* Active state for toggle */
+.toggle.active {
+ background-color: #19a8e4;
+ background-image: -webkit-linear-gradient(top, #088cd4 0, #19a8e4 100%);
+ background-image: linear-gradient(to bottom, #088cd4 0, #19a8e4 100%);
+ border: 1px solid #096c9d;
+ box-shadow: inset 0 0 15px rgba(255, 255, 255, .25);
+}
+
+/* Active state for toggle handle */
+.toggle.active .toggle-handle {
+ border-color: #0a76ad;
+ -webkit-transform: translate3d(48px,0,0);
+ transform: translate3d(48px,0,0);
+}
+
+/* Change "off" to "on" for active state */
+.toggle.active:before {
+ right: auto;
+ left: 15px;
+ color: #fff;
+ text-shadow: 0 -1px rgba(0, 0, 0, 0.25);
+ content: "On";
+}/* Push styles (to be used with push.js)
+-------------------------------------------------- */
+
+/* Fade animation */
+.content.fade {
+ left: 0;
+ opacity: 0;
+ -webkit-transition: opacity .2s ease-in-out;
+ transition: opacity .2s ease-in-out;
+}
+.content.fade.in {
+ opacity: 1;
+}
+
+/* Slide animation */
+.content.slide {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ -webkit-transition: -webkit-transform .25s ease-in-out;
+ transition: transform .25s ease-in-out;
+}
+.content.slide.left {
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+}
+.content.slide.right {
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+}
176 index.js
@@ -0,0 +1,176 @@
+var keymaster = require('./lib/keymaster.js')
+var inherits = require('inherits')
+var events = require('events')
+
+var keyTable =
+ { 8 : 'backspace'
+ , 9 : 'tab'
+ , 13 : 'enter'
+ , 16 : 'shift'
+ , 17 : 'ctrl'
+ , 18 : 'alt'
+ , 19 : 'pausebreak'
+ , 20 : 'capslock'
+ , 27 : 'esc'
+ , 32 : 'spacebar'
+ , 33 : 'pageup'
+ , 34 : 'pagedown'
+ , 35 : 'end'
+ , 36 : 'home'
+ , 37 : 'left'
+ , 38 : 'up'
+ , 39 : 'right'
+ , 40 : 'down'
+ , 45 : 'ins'
+ , 46 : 'del'
+ , 48 : '0'
+ , 49 : '1'
+ , 50 : '2'
+ , 51 : '3'
+ , 52 : '4'
+ , 53 : '5'
+ , 54 : '6'
+ , 55 : '7'
+ , 56 : '8'
+ , 57 : '9'
+ , 65 : 'a'
+ , 66 : 'b'
+ , 67 : 'c'
+ , 68 : 'd'
+ , 69 : 'e'
+ , 70 : 'f'
+ , 71 : 'g'
+ , 72 : 'h'
+ , 73 : 'i'
+ , 74 : 'j'
+ , 75 : 'k'
+ , 76 : 'l'
+ , 77 : 'm'
+ , 78 : 'n'
+ , 79 : 'o'
+ , 80 : 'p'
+ , 81 : 'q'
+ , 82 : 'r'
+ , 83 : 's'
+ , 84 : 't'
+ , 85 : 'u'
+ , 86 : 'v'
+ , 87 : 'w'
+ , 88 : 'x'
+ , 89 : 'y'
+ , 90 : 'z'
+ , 93 : 'select'
+ , 96 : 'num_0'
+ , 97 : 'num_1'
+ , 98 : 'num_2'
+ , 99 : 'num_3'
+ , 100 : 'num_4'
+ , 101 : 'num_5'
+ , 102 : 'num_6'
+ , 103 : 'num_7'
+ , 104 : 'num_8'
+ , 105 : 'num_9'
+ , 106 : 'multiply'
+ , 107 : 'add'
+ , 109 : 'subtract'
+ , 110 : 'decimalpoint'
+ , 111 : 'divide'
+ , 112 : 'f1'
+ , 113 : 'f2'
+ , 114 : 'f3'
+ , 115 : 'f4'
+ , 116 : 'f5'
+ , 117 : 'f6'
+ , 118 : 'f7'
+ , 119 : 'f8'
+ , 120 : 'f9'
+ , 121 : 'f10'
+ , 122 : 'f11'
+ , 123 : 'f12'
+ , 144 : 'numlock'
+ , 145 : 'scrolllock'
+ , 186 : 'semicolon'
+ , 187 : 'equalsign'
+ , 188 : 'comma'
+ , 189 : 'dash'
+ , 190 : 'period'
+ , 191 : 'forwardslash'
+ , 192 : 'graveaccent'
+ , 219 : 'openbracket'
+ , 220 : 'backslash'
+ , 221 : 'closebraket'
+ , 222 : 'singlequote'
+}
+
+module.exports = function(opts) {
+ return new HUD(opts)
+}
+
+function HUD(opts) {
+ if (!(this instanceof HUD)) return new HUD(opts)
+ var self = this
+ if (!opts) opts = {}
+ this.el = opts.el || 'nav'
+ if (typeof this.el !== 'object') this.el = document.querySelector(this.el)
+ this.toolbarKeys = opts.toolbarKeys || ['1','2','3','4','5','6','7','8','9','0']
+ this.bindEvents()
+}
+
+inherits(HUD, events.EventEmitter)
+
+HUD.prototype.onKeyDown = function() {
+ var self = this
+ keymaster.getPressedKeyCodes().map(function(keyCode) {
+ var pressed = keyTable[keyCode]
+ if (self.toolbarKeys.indexOf(pressed) > -1) return self.switchToolbar(pressed)
+ })
+}
+
+HUD.prototype.bindEvents = function() {
+ var self = this
+ window.addEventListener('keydown', this.onKeyDown.bind(this))
+ var list = this.el.querySelectorAll('li')
+ list = Array.prototype.slice.call(list);
+ list.map(function(li) {
+ li.addEventListener('click', self.onItemClick.bind(self))
+ })
+}
+
+HUD.prototype.onItemClick = function(ev) {
+ var idx = this.toolbarIndexOf(ev.currentTarget)
+ if (idx > -1) this.switchToolbar(idx)
+}
+
+HUD.prototype.addClass = function(el, className) {
+ if (!el) return
+ if (el.className === "") return el.className = className
+ var classes = el.className.split(' ')
+ classes.push(className)
+ el.className = classes.join(' ')
+ return classes
+}
+
+HUD.prototype.removeClass = function(el, className) {
+ if (!el) return
+ if (el.className === "") return
+ var classes = el.className.split(' ')
+ var idx = classes.indexOf(className)
+ if (idx > -1) classes.splice(idx, 1)
+ el.className = classes.join(' ')
+ return classes
+}
+
+HUD.prototype.toolbarIndexOf = function(li) {
+ var list = this.el.querySelectorAll('.tab-item')
+ list = Array.prototype.slice.call(list)
+ var idx = list.indexOf(li)
+ if (idx > -1) ++idx
+ return idx
+}
+
+HUD.prototype.switchToolbar = function(num) {
+ this.removeClass(this.el.querySelector('.active'), 'active')
+ var selected = this.el.querySelectorAll('.tab-item')[+num-1]
+ this.addClass(selected, 'active')
+ this.emit('select', this.el.querySelector('.active .tab-label').innerText)
+}
226 lib/keymaster.js
@@ -0,0 +1,226 @@
+// keymaster.js
+// (c) 2011-2012 Thomas Fuchs
+// keymaster.js may be freely distributed under the MIT license.
+
+;(function(global){
+ var k,
+ _handlers = {},
+ _mods = { 16: false, 18: false, 17: false, 91: false },
+ _scope = 'all',
+ // modifier keys
+ _MODIFIERS = {
+ '': 16, shift: 16,
+ '': 18, alt: 18, option: 18,
+ '': 17, ctrl: 17, control: 17,
+ '': 91, command: 91
+ },
+ // special keys
+ _MAP = {
+ backspace: 8, tab: 9, clear: 12,
+ enter: 13, 'return': 13,
+ esc: 27, escape: 27, space: 32,
+ left: 37, up: 38,
+ right: 39, down: 40,
+ del: 46, 'delete': 46,
+ home: 36, end: 35,
+ pageup: 33, pagedown: 34,
+ ',': 188, '.': 190, '/': 191,
+ '`': 192, '-': 189, '=': 187,
+ ';': 186, '\'': 222,
+ '[': 219, ']': 221, '\\': 220
+ },
+ _downKeys = [];
+
+ for(k=1;k<20;k++) _MODIFIERS['f'+k] = 111+k;
+
+ // IE doesn't support Array#indexOf, so have a simple replacement
+ function index(array, item){
+ var i = array.length;
+ while(i--) if(array[i]===item) return i;
+ return -1;
+ }
+
+ // handle keydown event
+ function dispatch(event, scope){
+ var key, handler, k, i, modifiersMatch;
+ key = event.keyCode;
+
+ if (index(_downKeys, key) == -1) {
+ _downKeys.push(key);
+ }
+
+ // if a modifier key, set the key.<modifierkeyname> property to true and return
+ if(key == 93 || key == 224) key = 91; // right command on webkit, command on Gecko
+ if(key in _mods) {
+ _mods[key] = true;
+ // 'assignKey' from inside this closure is exported to window.key
+ for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = true;
+ return;
+ }
+
+ // see if we need to ignore the keypress (filter() can can be overridden)
+ // by default ignore key presses if a select, textarea, or input is focused
+ if(!assignKey.filter.call(this, event)) return;
+
+ // abort if no potentially matching shortcuts found
+ if (!(key in _handlers)) return;
+
+ // for each potential shortcut
+ for (i = 0; i < _handlers[key].length; i++) {
+ handler = _handlers[key][i];
+
+ // see if it's in the current scope
+ if(handler.scope == scope || handler.scope == 'all'){
+ // check if modifiers match if any
+ modifiersMatch = handler.mods.length > 0;
+ for(k in _mods)
+ if((!_mods[k] && index(handler.mods, +k) > -1) ||
+ (_mods[k] && index(handler.mods, +k) == -1)) modifiersMatch = false;
+ // call the handler and stop the event if neccessary
+ if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){
+ if(handler.method(event, handler)===false){
+ if(event.preventDefault) event.preventDefault();
+ else event.returnValue = false;
+ if(event.stopPropagation) event.stopPropagation();
+ if(event.cancelBubble) event.cancelBubble = true;
+ }
+ }
+ }
+ }
+ };
+
+ // unset modifier keys on keyup
+ function clearModifier(event){
+ var key = event.keyCode, k,
+ i = index(_downKeys, key);
+
+ // remove key from _downKeys
+ if (i >= 0) {
+ _downKeys.splice(i, 1);
+ }
+
+ if(key == 93 || key == 224) key = 91;
+ if(key in _mods) {
+ _mods[key] = false;
+ for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = false;
+ }
+ };
+
+ function resetModifiers() {
+ for(k in _mods) _mods[k] = false;
+ for(k in _MODIFIERS) assignKey[k] = false;
+ }
+
+ // parse and assign shortcut
+ function assignKey(key, scope, method){
+ var keys, mods, i, mi;
+ if (method === undefined) {
+ method = scope;
+ scope = 'all';
+ }
+ key = key.replace(/\s/g,'');
+ keys = key.split(',');
+
+ if((keys[keys.length-1])=='')
+ keys[keys.length-2] += ',';
+ // for each shortcut
+ for (i = 0; i < keys.length; i++) {
+ // set modifier keys if any
+ mods = [];
+ key = keys[i].split('+');
+ if(key.length > 1){
+ mods = key.slice(0,key.length-1);
+ for (mi = 0; mi < mods.length; mi++)
+ mods[mi] = _MODIFIERS[mods[mi]];
+ key = [key[key.length-1]];
+ }
+ // convert to keycode and...
+ key = key[0]
+ key = _MAP[key] || key.toUpperCase().charCodeAt(0);
+ // ...store handler
+ if (!(key in _handlers)) _handlers[key] = [];
+ _handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods });
+ }
+ };
+
+ // Returns true if the key with code 'keyCode' is currently down
+ // Converts strings into key codes.
+ function isPressed(keyCode) {
+ if (typeof(keyCode)=='string') {
+ if (keyCode.length == 1) {
+ keyCode = (keyCode.toUpperCase()).charCodeAt(0);
+ } else {
+ return false;
+ }
+ }
+ return index(_downKeys, keyCode) != -1;
+ }
+
+ function getPressedKeyCodes() {
+ return _downKeys.slice(0);
+ }
+
+ function filter(event){
+ var tagName = (event.target || event.srcElement).tagName;
+ // ignore keypressed in any elements that support keyboard data input
+ return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
+ }
+
+ // initialize key.<modifier> to false
+ for(k in _MODIFIERS) assignKey[k] = false;
+
+ // set current scope (default 'all')
+ function setScope(scope){ _scope = scope || 'all' };
+ function getScope(){ return _scope || 'all' };
+
+ // delete all handlers for a given scope
+ function deleteScope(scope){
+ var key, handlers, i;
+
+ for (key in _handlers) {
+ handlers = _handlers[key];
+ for (i = 0; i < handlers.length; ) {
+ if (handlers[i].scope === scope) handlers.splice(i, 1);
+ else i++;
+ }
+ }
+ };
+
+ // cross-browser events
+ function addEvent(object, event, method) {
+ if (object.addEventListener)
+ object.addEventListener(event, method, false);
+ else if(object.attachEvent)
+ object.attachEvent('on'+event, function(){ method(window.event) });
+ };
+
+ // set the handlers globally on document
+ addEvent(document, 'keydown', function(event) { dispatch(event, _scope) }); // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48
+ addEvent(document, 'keyup', clearModifier);
+
+ // reset modifiers to false whenever the window is (re)focused.
+ addEvent(window, 'focus', resetModifiers);
+
+ // store previously defined key
+ var previousKey = global.key;
+
+ // restore previously defined key and return reference to our key object
+ function noConflict() {
+ var k = global.key;
+ global.key = previousKey;
+ return k;
+ }
+
+ // set window.key and window.key.set/get/deleteScope, and the default filter
+ global.key = assignKey;
+ global.key.setScope = setScope;
+ global.key.getScope = getScope;
+ global.key.deleteScope = deleteScope;
+ global.key.filter = filter;
+ global.key.isPressed = isPressed;
+ global.key.getPressedKeyCodes = getPressedKeyCodes;
+ global.key.noConflict = noConflict;
+
+ if(typeof module !== 'undefined') module.exports = global.key;
+
+})(this);
8 package.json
@@ -0,0 +1,8 @@
+{
+ "name": "toolbar",
+ "description": "client side module for creating toolbars",
+ "version": "0.0.1",
+ "dependencies": {
+ "inherits": "1.0.0"
+ }
+}
61 readme.md
@@ -0,0 +1,61 @@
+# toolbar
+
+keyboard selectable toolbar for selecting an active tool
+
+when users hit 0-9 on their keyboard or click an item on the toolbar `toolbar` will emit a javascript event that will tell you which item in the toolbar has been selected
+
+## install
+
+step 1:
+
+`npm install toolbar`
+
+step 2:
+
+include some html in your page that looks like this:
+
+```html
+<nav class="bar-tab">
+ <ul class="tab-inner">
+ <li class="tab-item active">
+ <a href="">
+ <img class="tab-icon" src="icons/first-tool.png">
+ <div class="tab-label">First Tool</div>
+ </a>
+ </li>
+ <li class="tab-item">
+ <a href="">
+ <img class="tab-icon" src="icons/second-tool.png">
+ <div class="tab-label">Second Tool</div>
+ </a>
+ </li>
+ </ul>
+</nav>
+```
+
+step 3:
+
+`
+var toolbar = require('toolbar')
+toolbar('.bar-tab')
+`
+
+use [browserify](http://browserify.org/) to package toolbar for use in your client side app!
+
+step 4:
+
+```javascript
+toolbar.on('select', function(item) {
+ // item is the .tab-label innerText
+})
+```
+
+## bonus advice
+
+to convert svgs from the noun project into cute little transparent pngs:
+
+`mogrify -fill "#ffffff" -opaque "#000000" -background none -format png *.svg`
+
+## license
+
+BSD
Please sign in to comment.
Something went wrong with that request. Please try again.