├── demo ├── assets │ ├── drag.png │ ├── open.png │ ├── graph.png │ ├── demo.js │ └── demo.css ├── index.html └── apps │ ├── default.html │ ├── rightDisabled.html │ ├── hyperextend.html │ ├── skinnyThreshold.html │ ├── classNames.html │ ├── toggles.html │ ├── noDrag.html │ ├── dragElement.html │ ├── expand.html │ ├── settings.html │ └── ratchet │ ├── template.html │ ├── ratchet.js │ └── ratchet.css ├── component.json ├── package.json ├── snap.css ├── .gitignore ├── snap.min.js ├── README.md └── snap.js /demo/assets/drag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemocracyOS/Snap.js/develop/demo/assets/drag.png -------------------------------------------------------------------------------- /demo/assets/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemocracyOS/Snap.js/develop/demo/assets/open.png -------------------------------------------------------------------------------- /demo/assets/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemocracyOS/Snap.js/develop/demo/assets/graph.png -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Snap.js", 3 | "description": "A Library for creating beautiful mobile shelfs in Javascript (Facebook and Path style side menus)", 4 | "version": "1.9.2", 5 | "author": "Jacob Kelley ", 6 | "keywords": [ 7 | "mobile shelfs", 8 | "nav", 9 | "menu", 10 | "mobile", 11 | "side menu" 12 | ], 13 | "license": "MIT", 14 | "main": "snap.js", 15 | "scripts": ["snap.js"] 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "democracyos-snap.js", 3 | "description": "A Library for creating beautiful mobile shelfs in Javascript (Facebook and Path style side menus)", 4 | "version": "2.0.0", 5 | "repository": "git@github.com:DemocracyOS/Snap.js.git", 6 | "author": "Jacob Kelley ", 7 | "keywords": [ 8 | "mobile shelfs", 9 | "nav", 10 | "menu", 11 | "mobile", 12 | "side menu" 13 | ], 14 | "license": "MIT", 15 | "main": "snap.js", 16 | "files": [ "snap.js", "snap.css" ] 17 | } 18 | -------------------------------------------------------------------------------- /demo/assets/demo.js: -------------------------------------------------------------------------------- 1 | var addEvent = function addEvent(element, eventName, func) { 2 | if (element.addEventListener) { 3 | return element.addEventListener(eventName, func, false); 4 | } else if (element.attachEvent) { 5 | return element.attachEvent("on" + eventName, func); 6 | } 7 | }; 8 | 9 | addEvent(document.getElementById('open-left'), 'click', function(){ 10 | snapper.open('left'); 11 | }); 12 | 13 | /* Prevent Safari opening links when viewing as a Mobile App */ 14 | (function (a, b, c) { 15 | if(c in b && b[c]) { 16 | var d, e = a.location, 17 | f = /^(a|html)$/i; 18 | a.addEventListener("click", function (a) { 19 | d = a.target; 20 | while(!f.test(d.nodeName)) d = d.parentNode; 21 | "href" in d && (d.href.indexOf("http") || ~d.href.indexOf(e.host)) && (a.preventDefault(), e.href = d.href) 22 | }, !1) 23 | } 24 | })(document, window.navigator, "standalone"); -------------------------------------------------------------------------------- /snap.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: sans-serif; 3 | margin: 0; 4 | padding: 0; 5 | width: 100%; 6 | height: 100%; 7 | overflow: hidden; 8 | } 9 | 10 | .snap-content { 11 | position: absolute; 12 | top: 0; 13 | right: 0; 14 | bottom: 0; 15 | left: 0; 16 | width: auto; 17 | height: auto; 18 | z-index: 2; 19 | overflow: auto; 20 | -webkit-overflow-scrolling: touch; 21 | -webkit-transform: translate3d(0, 0, 0); 22 | -moz-transform: translate3d(0, 0, 0); 23 | -ms-transform: translate3d(0, 0, 0); 24 | -o-transform: translate3d(0, 0, 0); 25 | transform: translate3d(0, 0, 0); 26 | } 27 | 28 | .snap-drawers { 29 | position: absolute; 30 | top: 0; 31 | right: 0; 32 | bottom: 0; 33 | left: 0; 34 | width: auto; 35 | height: auto; 36 | } 37 | 38 | .snap-drawer { 39 | position: absolute; 40 | top: 0; 41 | right: auto; 42 | bottom: 0; 43 | left: auto; 44 | width: 265px; 45 | height: auto; 46 | overflow: auto; 47 | -webkit-overflow-scrolling: touch; 48 | -webkit-transition: width 0.3s ease; 49 | -moz-transition: width 0.3s ease; 50 | -ms-transition: width 0.3s ease; 51 | -o-transition: width 0.3s ease; 52 | transition: width 0.3s ease; 53 | } 54 | 55 | .snap-drawer-left { 56 | left: 0; 57 | z-index: 1; 58 | } 59 | 60 | .snap-drawer-right { 61 | right: 0; 62 | z-index: 1; 63 | } 64 | 65 | .snapjs-left .snap-drawer-right, 66 | .snapjs-right .snap-drawer-left { 67 | display: none; 68 | } 69 | 70 | .snapjs-expand-left .snap-drawer-left, 71 | .snapjs-expand-right .snap-drawer-right { 72 | width: 100%; 73 | } 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | [Dd]ebug/ 46 | [Rr]elease/ 47 | *_i.c 48 | *_p.c 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.vspscc 63 | .builds 64 | *.dotCover 65 | 66 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 67 | #packages/ 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | 76 | # Visual Studio profiler 77 | *.psess 78 | *.vsp 79 | 80 | # ReSharper is a .NET coding add-in 81 | _ReSharper* 82 | 83 | # Installshield output folder 84 | [Ee]xpress 85 | 86 | # DocProject is a documentation generator add-in 87 | DocProject/buildhelp/ 88 | DocProject/Help/*.HxT 89 | DocProject/Help/*.HxC 90 | DocProject/Help/*.hhc 91 | DocProject/Help/*.hhk 92 | DocProject/Help/*.hhp 93 | DocProject/Help/Html2 94 | DocProject/Help/html 95 | 96 | # Click-Once directory 97 | publish 98 | 99 | # Others 100 | [Bb]in 101 | [Oo]bj 102 | sql 103 | TestResults 104 | *.Cache 105 | ClientBin 106 | stylecop.* 107 | ~$* 108 | *.dbmdl 109 | Generated_Code #added for RIA/Silverlight projects 110 | 111 | # Backup & report files from converting an old project file to a newer 112 | # Visual Studio version. Backup files are not needed, because we have git ;-) 113 | _UpgradeReport_Files/ 114 | Backup*/ 115 | UpgradeLog*.XML 116 | 117 | 118 | 119 | ############ 120 | ## Windows 121 | ############ 122 | 123 | # Windows image file caches 124 | Thumbs.db 125 | 126 | # Folder config file 127 | Desktop.ini 128 | 129 | 130 | ############# 131 | ## Python 132 | ############# 133 | 134 | *.py[co] 135 | 136 | # Packages 137 | *.egg 138 | *.egg-info 139 | dist 140 | build 141 | eggs 142 | parts 143 | bin 144 | var 145 | sdist 146 | develop-eggs 147 | .installed.cfg 148 | 149 | # Installer logs 150 | pip-log.txt 151 | 152 | # Unit test / coverage reports 153 | .coverage 154 | .tox 155 | 156 | #Translations 157 | *.mo 158 | 159 | #Mr Developer 160 | .mr.developer.cfg 161 | 162 | # Mac crap 163 | .DS_Store 164 | -------------------------------------------------------------------------------- /demo/assets/demo.css: -------------------------------------------------------------------------------- 1 | /* Show "Left" drawer for the "Right" drawer in the demo */ .snapjs-right .snap-drawer-left { display: block; right: 0; left: auto; } /* Hide the actual "Right" drawer in the demo */ .snapjs-right .snap-drawer-right { display: none; } /* Show the "Drag" background in the demo */ #content { background: #BFC7D8 url(drag.png) center 75% no-repeat; } /* Demo toolbar styles */ #toolbar { background: #536ea7; border-bottom: 1px solid #111b32; position: absolute; top: 0; right: 0; left: 0; width: auto; height: 44px; } #toolbar h1 { color: #fff; font-size: 16px; line-height: 22px; text-align: center; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.8); position: absolute; top: 0; right: 44px; left: 44px; width: auto; height: 44px; } #open-left { background: url(open.png) center center no-repeat; display: block; width: 44px; height: 44px; } /* Show the red "No Drag" box in the demo */ #no-drag, #do-drag { position: absolute; top: 50%; left: 50%; width: 50%; height: 100px; margin-left: -25%; margin-top: -50px; background: red; color: #FFF; text-align: center; line-height: 100px; } #do-drag{ background: green; } /* Styles for the social buttons in the demo */ .demo-social { padding: 0 15px; } /* Show the translucent white "Toggle" boxes in the demo */ .toggler { width: 80%; background: rgba(255,255,255,0.5); margin: 0 auto; position: relative; top: 70px; padding: 20px; margin-bottom: 20px; text-align: center; } /* Styles for expanding "Search" input in the "Expanding" demo */ .search { padding-bottom: 10px; border-bottom: 1px solid rgba(0, 0, 0, 0.2); -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1); -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1); box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1); } #search { height: 30px; width: 87%; margin: 7px 7px 0 7px; background: linear-gradient(#414A5A,#4C5464); background: -webkit-linear-gradient(#414A5A,#4C5464); padding-left: 15px; border: 1px solid #222936; -webkit-border-radius: 25px; -moz-border-radius: 25px; border-radius: 25px; -webkit-box-shadow: inset 0 1px 2px -1px rgba(0, 0, 0, 0.5),0 1px 2px -1px rgba(255, 255, 255, 0.4); -moz-box-shadow: inset 0 1px 2px -1px rgba(0, 0, 0, 0.5),0 1px 2px -1px rgba(255, 255, 255, 0.4); box-shadow: inset 0 1px 2px -1px rgba(0, 0, 0, 0.5),0 1px 2px -1px rgba(255, 255, 255, 0.4); } #search:focus { outline: none; border-color: #151515; } /* Styles for fading out the drawer content in the "Expanding" demo */ .drawer-inner { -webkit-transition: opacity 0.3s ease; -moz-transition: opacity 0.3s ease; -ms-transition: opacity 0.3s ease; -o-transition: opacity 0.3s ease; transition: opacity 0.3s ease; } .snapjs-expand-left .drawer-inner, .snapjs-expand-right .drawer-inner { opacity: 0; } /* Styles for the "Settings" demo */ .opt { padding: 20px; border-bottom: 1px solid rgba(0, 0, 0, 0.1); } .opt p { font-weight: bold; margin-top: 0; margin-bottom: 5px; color: #222; color: rgba(0, 0, 0, 0.6); } /* Default demo styles */ .snap-content { background: #BFC7D8; } .snap-drawers { background: #323949; } .snap-drawer { background: #323949; color: #eee; } .snap-drawer h3 { font-size: 36px; font-weight: normal; margin: 15px; } .snap-drawer h4 { padding: 15px; border-top: 1px solid rgba(255, 255, 255, 0.1); margin-bottom: 0; } .snap-drawer ul { padding: 0; margin: 0; list-style-type: none; } .snap-drawer li > a { display: block; border-bottom: 1px solid rgba(0,0,0, 0.1); border-top: 1px solid rgba(255, 255, 255, 0.1); padding: 15px; font-weight: bold; text-shadow: 0 1px 0 #000; text-decoration: none; color: #ccc; text-indent: 20px; } .snap-drawer p { opacity: 0.5; padding: 15px; font-size: 12px; } -------------------------------------------------------------------------------- /demo/apps/default.html: -------------------------------------------------------------------------------- 1 | Snap.js

Snap.js

Demos

The Default demo shows basic Snap.js functionality

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla non erat ac leo ultrices blandit sed vel risus. Pellentesque facilisis blandit auctor. Maecenas vestibulum vulputate tincidunt. Mauris nec quam libero. Fusce eget ligula non leo varius condimentum quis ac elit. Donec id urna ut neque semper ultrices. Proin ut suscipit felis. Nullam neque felis, ullamcorper scelerisque volutpat vel, vehicula vehicula neque. Aenean scelerisque elit ac erat sagittis ullamcorper.

Default

-------------------------------------------------------------------------------- /demo/apps/rightDisabled.html: -------------------------------------------------------------------------------- 1 | Snap.js

Snap.js

Demos

This demo has the menu on the right side disabled

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla non erat ac leo ultrices blandit sed vel risus. Pellentesque facilisis blandit auctor. Maecenas vestibulum vulputate tincidunt. Mauris nec quam libero. Fusce eget ligula non leo varius condimentum quis ac elit. Donec id urna ut neque semper ultrices. Proin ut suscipit felis. Nullam neque felis, ullamcorper scelerisque volutpat vel, vehicula vehicula neque. Aenean scelerisque elit ac erat sagittis ullamcorper.

Right Disabled

-------------------------------------------------------------------------------- /demo/apps/hyperextend.html: -------------------------------------------------------------------------------- 1 | Snap.js

Snap.js

Demos

The Default demo shows basic Snap.js functionality

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla non erat ac leo ultrices blandit sed vel risus. Pellentesque facilisis blandit auctor. Maecenas vestibulum vulputate tincidunt. Mauris nec quam libero. Fusce eget ligula non leo varius condimentum quis ac elit. Donec id urna ut neque semper ultrices. Proin ut suscipit felis. Nullam neque felis, ullamcorper scelerisque volutpat vel, vehicula vehicula neque. Aenean scelerisque elit ac erat sagittis ullamcorper.

No Hyperextension

-------------------------------------------------------------------------------- /demo/apps/skinnyThreshold.html: -------------------------------------------------------------------------------- 1 | Snap.js

Snap.js

Demos

You can define the width of your app's sliding space

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla non erat ac leo ultrices blandit sed vel risus. Pellentesque facilisis blandit auctor. Maecenas vestibulum vulputate tincidunt. Mauris nec quam libero. Fusce eget ligula non leo varius condimentum quis ac elit. Donec id urna ut neque semper ultrices. Proin ut suscipit felis. Nullam neque felis, ullamcorper scelerisque volutpat vel, vehicula vehicula neque. Aenean scelerisque elit ac erat sagittis ullamcorper.

Skinny Threshold

-------------------------------------------------------------------------------- /demo/apps/classNames.html: -------------------------------------------------------------------------------- 1 | Snap.js

Snap.js

Demos

The class names demo shows how you can utilize the classess added by Snap.js to adjust hidden content. In this case, we're showing the same menu on both sides of the app via CSS and classess.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla non erat ac leo ultrices blandit sed vel risus. Pellentesque facilisis blandit auctor. Maecenas vestibulum vulputate tincidunt. Mauris nec quam libero. Fusce eget ligula non leo varius condimentum quis ac elit. Donec id urna ut neque semper ultrices. Proin ut suscipit felis. Nullam neque felis, ullamcorper scelerisque volutpat vel, vehicula vehicula neque. Aenean scelerisque elit ac erat sagittis ullamcorper.

Class Names

-------------------------------------------------------------------------------- /demo/apps/toggles.html: -------------------------------------------------------------------------------- 1 | Snap.js

Snap.js

Demos

Toggles are easy to create. Snap.js gives you the state of the pane and allows you to make decisions simply.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla non erat ac leo ultrices blandit sed vel risus. Pellentesque facilisis blandit auctor. Maecenas vestibulum vulputate tincidunt. Mauris nec quam libero. Fusce eget ligula non leo varius condimentum quis ac elit. Donec id urna ut neque semper ultrices. Proin ut suscipit felis. Nullam neque felis, ullamcorper scelerisque volutpat vel, vehicula vehicula neque. Aenean scelerisque elit ac erat sagittis ullamcorper.

Toggles

Open Left
Open Right
-------------------------------------------------------------------------------- /demo/apps/noDrag.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Snap.js 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |

Snap.js

17 |
18 | 19 | 20 | 21 |
22 |

Demos

23 | 36 |
37 |

Data attributes can be used to define elements that you want to have prevent sliding of a pane (maybe a photo slider, etc)

38 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla non erat ac leo ultrices blandit sed vel risus. Pellentesque facilisis blandit auctor. Maecenas vestibulum vulputate tincidunt. Mauris nec quam libero. Fusce eget ligula non leo varius condimentum quis ac elit. Donec id urna ut neque semper ultrices. Proin ut suscipit felis. Nullam neque felis, ullamcorper scelerisque volutpat vel, vehicula vehicula neque. Aenean scelerisque elit ac erat sagittis ullamcorper.

39 |
40 |
41 |
42 |
43 |
44 | 45 |
46 |
47 | 48 |

No Drag

49 |
50 |
No Drag
51 |
52 | 53 | 54 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /demo/apps/dragElement.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Snap.js 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |

Snap.js

17 |
18 | 19 | 20 | 21 |
22 |

Demos

23 | 36 |
37 |

Data attributes can be used to define elements that you want to have prevent sliding of a pane (maybe a photo slider, etc)

38 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla non erat ac leo ultrices blandit sed vel risus. Pellentesque facilisis blandit auctor. Maecenas vestibulum vulputate tincidunt. Mauris nec quam libero. Fusce eget ligula non leo varius condimentum quis ac elit. Donec id urna ut neque semper ultrices. Proin ut suscipit felis. Nullam neque felis, ullamcorper scelerisque volutpat vel, vehicula vehicula neque. Aenean scelerisque elit ac erat sagittis ullamcorper.

39 |
40 |
41 |
42 |
43 |
44 | 45 |
46 |
47 | 48 |

Drag Element

49 |
50 |
Drag Handle
51 |
52 | 53 | 54 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /demo/apps/expand.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Snap.js 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 | 19 |
20 |

Snap.js

21 |
22 | 23 | 24 | 25 |
26 |

Demos

27 | 40 |
41 |

Expanding is when the app disappears completely and the entire app's drawer takes up the usable space. Good for searching

42 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla non erat ac leo ultrices blandit sed vel risus. Pellentesque facilisis blandit auctor. Maecenas vestibulum vulputate tincidunt. Mauris nec quam libero. Fusce eget ligula non leo varius condimentum quis ac elit. Donec id urna ut neque semper ultrices. Proin ut suscipit felis. Nullam neque felis, ullamcorper scelerisque volutpat vel, vehicula vehicula neque. Aenean scelerisque elit ac erat sagittis ullamcorper.

43 |
44 |
45 |
46 |
47 |
48 |
49 | 50 |
51 |
52 | 53 |

Expanding

54 |
55 |
56 | 57 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /demo/apps/settings.html: -------------------------------------------------------------------------------- 1 | Snap.js

Snap.js

Demos

You can update the settings of an instantiated snap object by calling the settings method.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla non erat ac leo ultrices blandit sed vel risus. Pellentesque facilisis blandit auctor. Maecenas vestibulum vulputate tincidunt. Mauris nec quam libero. Fusce eget ligula non leo varius condimentum quis ac elit. Donec id urna ut neque semper ultrices. Proin ut suscipit felis. Nullam neque felis, ullamcorper scelerisque volutpat vel, vehicula vehicula neque. Aenean scelerisque elit ac erat sagittis ullamcorper.

Updatable Settings

Change Speed:

Change Easing

Disable

And More!

-------------------------------------------------------------------------------- /demo/apps/ratchet/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Ratchet template page 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 42 | 43 | 44 | 45 | 46 |
47 |
48 | 67 |
68 |
69 |
    70 |
  • List item 1
  • 71 |
  • List item 2
  • 72 |
  • List item 3
  • 73 |
  • List item 4
  • 74 |
  • List item 5
  • 75 |
  • List item 6
  • 76 |
  • List item 7
  • 77 |
  • List item 8
  • 78 |
  • List item 9
  • 79 |
  • List item 10
  • 80 |
  • List item 1
  • 81 |
  • List item 12
  • 82 |
  • List item 13
  • 83 |
  • List item 14
  • 84 |
  • List item 15
  • 85 |
  • List item 16
  • 86 |
  • List item 17
  • 87 |
  • List item 18
  • 88 |
  • List item 19
  • 89 |
  • List item 20
  • 90 |
91 |
92 |
93 | 94 |
95 | 96 | 97 |
98 | 99 |

Ratchet

100 | 101 |
102 | 103 | 104 |
105 |
106 |

Thanks for downloading Ratchet. This is an example HTML page that's linked up to compiled Ratchet CSS and JS, has the proper meta tags and the HTML structure. Need some more help before you start filling this with your own content? Check out some Ratchet resorces:

107 |
108 | 109 | 159 | 160 |
161 |
162 | 163 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /snap.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Snap.js 3 | * 4 | * Copyright 2013, Jacob Kelley - http://jakiestfu.com/ 5 | * Released under the MIT Licence 6 | * http://opensource.org/licenses/MIT 7 | * 8 | * Github: http://github.com/jakiestfu/Snap.js/ 9 | * Version: 1.9.2 10 | */ 11 | (function(c,b){var a=a||function(k){var f={element:null,dragger:null,disable:"none",addBodyClasses:true,hyperextensible:true,resistance:0.5,flickThreshold:50,transitionSpeed:0.3,easing:"ease",maxPosition:266,minPosition:-266,tapToClose:true,touchToDrag:true,slideIntent:40,minDragDistance:5},e={simpleStates:{opening:null,towards:null,hyperExtending:null,halfway:null,flick:null,translation:{absolute:0,relative:0,sinceDirectionChange:0,percentage:0}}},h={},d={hasTouch:(b.ontouchstart===null),eventType:function(m){var l={down:(d.hasTouch?"touchstart":"mousedown"),move:(d.hasTouch?"touchmove":"mousemove"),up:(d.hasTouch?"touchend":"mouseup"),out:(d.hasTouch?"touchcancel":"mouseout")};return l[m]},page:function(l,m){return(d.hasTouch&&m.touches.length&&m.touches[0])?m.touches[0]["page"+l]:m["page"+l]},klass:{has:function(m,l){return(m.className).indexOf(l)!==-1},add:function(m,l){if(!d.klass.has(m,l)&&f.addBodyClasses){m.className+=" "+l}},remove:function(m,l){if(f.addBodyClasses){m.className=(m.className).replace(l,"").replace(/^\s+|\s+$/g,"")}}},dispatchEvent:function(l){if(typeof h[l]==="function"){return h[l].call()}},vendor:function(){var m=b.createElement("div"),n="webkit Moz O ms".split(" "),l;for(l in n){if(typeof m.style[n[l]+"Transition"]!=="undefined"){return n[l]}}},transitionCallback:function(){return(e.vendor==="Moz"||e.vendor==="ms")?"transitionend":e.vendor+"TransitionEnd"},canTransform:function(){return typeof f.element.style[e.vendor+"Transform"]!=="undefined"},deepExtend:function(l,n){var m;for(m in n){if(n[m]&&n[m].constructor&&n[m].constructor===Object){l[m]=l[m]||{};d.deepExtend(l[m],n[m])}else{l[m]=n[m]}}return l},angleOfDrag:function(l,o){var n,m;m=Math.atan2(-(e.startDragY-o),(e.startDragX-l));if(m<0){m+=2*Math.PI}n=Math.floor(m*(180/Math.PI)-180);if(n<0&&n>-180){n=360-Math.abs(n)}return Math.abs(n)},events:{addEvent:function g(m,l,n){if(m.addEventListener){return m.addEventListener(l,n,false)}else{if(m.attachEvent){return m.attachEvent("on"+l,n)}}},removeEvent:function g(m,l,n){if(m.addEventListener){return m.removeEventListener(l,n,false)}else{if(m.attachEvent){return m.detachEvent("on"+l,n)}}},prevent:function(l){if(l.preventDefault){l.preventDefault()}else{l.returnValue=false}}},parentUntil:function(n,l){var m=typeof l==="string";while(n.parentNode){if(m&&n.getAttribute&&n.getAttribute(l)){return n}else{if(!m&&n===l){return n}}n=n.parentNode}return null}},i={translate:{get:{matrix:function(n){if(!d.canTransform()){return parseInt(f.element.style.left,10)}else{var m=c.getComputedStyle(f.element)[e.vendor+"Transform"].match(/\((.*)\)/),l=8;if(m){m=m[1].split(",");if(m.length===16){n+=l}return parseInt(m[n],10)}return 0}}},easeCallback:function(){f.element.style[e.vendor+"Transition"]="";e.translation=i.translate.get.matrix(4);e.easing=false;clearInterval(e.animatingInterval);if(e.easingTo===0){d.klass.remove(b.body,"snapjs-right");d.klass.remove(b.body,"snapjs-left")}d.dispatchEvent("animated");d.events.removeEvent(f.element,d.transitionCallback(),i.translate.easeCallback)},easeTo:function(l){if(!d.canTransform()){e.translation=l;i.translate.x(l)}else{e.easing=true;e.easingTo=l;f.element.style[e.vendor+"Transition"]="all "+f.transitionSpeed+"s "+f.easing;e.animatingInterval=setInterval(function(){d.dispatchEvent("animating")},1);d.events.addEvent(f.element,d.transitionCallback(),i.translate.easeCallback);i.translate.x(l)}if(l===0){f.element.style[e.vendor+"Transform"]=""}},x:function(m){if((f.disable==="left"&&m>0)||(f.disable==="right"&&m<0)){return}if(!f.hyperextensible){if(m===f.maxPosition||m>f.maxPosition){m=f.maxPosition}else{if(m===f.minPosition||m0,q=n,w;if((e.intentChecked&&!e.hasIntent)){return}if(f.addBodyClasses){if((o)>0){d.klass.add(b.body,"snapjs-left");d.klass.remove(b.body,"snapjs-right")}else{if((o)<0){d.klass.add(b.body,"snapjs-right");d.klass.remove(b.body,"snapjs-left")}}}if(e.hasIntent===false||e.hasIntent===null){var m=d.angleOfDrag(v,u),l=(m>=0&&m<=f.slideIntent)||(m<=360&&m>(360-f.slideIntent)),r=(m>=180&&m<=(180+f.slideIntent))||(m<=180&&m>=(180-f.slideIntent));if(!r&&!l){e.hasIntent=false}else{e.hasIntent=true}e.intentChecked=true}if((f.minDragDistance>=Math.abs(v-e.startDragX))||(e.hasIntent===false)){return}d.events.prevent(s);d.dispatchEvent("drag");e.dragWatchers.current=v;if(e.dragWatchers.last>v){if(e.dragWatchers.state!=="left"){e.dragWatchers.state="left";e.dragWatchers.hold=v}e.dragWatchers.last=v}else{if(e.dragWatchers.last(f.maxPosition/2),flick:Math.abs(e.dragWatchers.current-e.dragWatchers.hold)>f.flickThreshold,translation:{absolute:o,relative:n,sinceDirectionChange:(e.dragWatchers.current-e.dragWatchers.hold),percentage:(o/f.maxPosition)*100}}}else{if(f.minPosition>o){w=(o-f.minPosition)*f.resistance;q=n-w}e.simpleStates={opening:"right",towards:e.dragWatchers.state,hyperExtending:f.minPosition>o,halfway:o<(f.minPosition/2),flick:Math.abs(e.dragWatchers.current-e.dragWatchers.hold)>f.flickThreshold,translation:{absolute:o,relative:n,sinceDirectionChange:(e.dragWatchers.current-e.dragWatchers.hold),percentage:(o/f.minPosition)*100}}}i.translate.x(q+t)}},endDrag:function(m){if(e.isDragging){d.dispatchEvent("end");var l=i.translate.get.matrix(4);if(e.dragWatchers.current===0&&l!==0&&f.tapToClose){d.dispatchEvent("close");d.events.prevent(m);i.translate.easeTo(0);e.isDragging=false;e.startDragX=0;return}if(e.simpleStates.opening==="left"){if((e.simpleStates.halfway||e.simpleStates.hyperExtending||e.simpleStates.flick)){if(e.simpleStates.flick&&e.simpleStates.towards==="left"){i.translate.easeTo(0)}else{if((e.simpleStates.flick&&e.simpleStates.towards==="right")||(e.simpleStates.halfway||e.simpleStates.hyperExtending)){i.translate.easeTo(f.maxPosition)}}}else{i.translate.easeTo(0)}}else{if(e.simpleStates.opening==="right"){if((e.simpleStates.halfway||e.simpleStates.hyperExtending||e.simpleStates.flick)){if(e.simpleStates.flick&&e.simpleStates.towards==="right"){i.translate.easeTo(0)}else{if((e.simpleStates.flick&&e.simpleStates.towards==="left")||(e.simpleStates.halfway||e.simpleStates.hyperExtending)){i.translate.easeTo(f.minPosition)}}}else{i.translate.easeTo(0)}}}e.isDragging=false;e.startDragX=d.page("X",m)}}}},j=function(l){if(l.element){d.deepExtend(f,l);e.vendor=d.vendor();i.drag.listen()}};this.open=function(l){d.dispatchEvent("open");d.klass.remove(b.body,"snapjs-expand-left");d.klass.remove(b.body,"snapjs-expand-right");if(l==="left"){e.simpleStates.opening="left";e.simpleStates.towards="right";d.klass.add(b.body,"snapjs-left");d.klass.remove(b.body,"snapjs-right");i.translate.easeTo(f.maxPosition)}else{if(l==="right"){e.simpleStates.opening="right";e.simpleStates.towards="left";d.klass.remove(b.body,"snapjs-left");d.klass.add(b.body,"snapjs-right");i.translate.easeTo(f.minPosition)}}};this.close=function(){d.dispatchEvent("close");i.translate.easeTo(0)};this.expand=function(l){var m=c.innerWidth||b.documentElement.clientWidth;if(l==="left"){d.dispatchEvent("expandLeft");d.klass.add(b.body,"snapjs-expand-left");d.klass.remove(b.body,"snapjs-expand-right")}else{d.dispatchEvent("expandRight");d.klass.add(b.body,"snapjs-expand-right");d.klass.remove(b.body,"snapjs-expand-left");m*=-1}i.translate.easeTo(m)};this.on=function(l,m){h[l]=m;return this};this.off=function(l){if(h[l]){h[l]=false}};this.enable=function(){d.dispatchEvent("enable");i.drag.listen()};this.disable=function(){d.dispatchEvent("disable");i.drag.stopListening()};this.settings=function(l){d.deepExtend(f,l)};this.state=function(){var l,m=i.translate.get.matrix(4);if(m===f.maxPosition){l="left"}else{if(m===f.minPosition){l="right"}else{l="closed"}}return{state:l,info:e.simpleStates}};j(k)};if((typeof module!=="undefined")&&module.exports){module.exports=a}if(typeof ender==="undefined"){this.Snap=a}if((typeof define==="function")&&define.amd){define("snap",[],function(){return a})}}).call(this,window,document); 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Snap.js 2 | A Library for creating beautiful mobile shelfs in Javascript 3 | 4 | View Video Preview 5 | 6 | View Demos 7 | 8 | 9 | 10 | * [Features](#features) 11 | * [Support](#support) 12 | * [Installation](#installation) 13 | * [Usage](#usage) 14 | * [Settings & Defaults](#settings-and-defaults) 15 | * [Public Methods](#public-methods) 16 | * [Gotchas](#gotchas) 17 | * [FAQ's](#faq) 18 | * [Compliments](#compliments) 19 | * [Licensing](#licensing) 20 | * [Extras](#extras) 21 | 22 | ## Features 23 | * Library Independent 24 | * High Customization 25 | * Flick Support 26 | * User Intent Detection 27 | * Disable Hyperextension 28 | * Event Hooks 29 | * CSS3 Powered Animations with IE fallbacks 30 | * Drag Support 31 | * Drag Handle Support 32 | * Programatic API 33 | * "No-Drag" Elements 34 | * Definable Easing Mode 35 | * Enable/Disable Events 36 | * Disabled Sides (left or right) 37 | * Supports [Ratchet](http://maker.github.com/ratchet/) (with templates!) 38 | 39 | ## Support 40 | * Firefox 10+ 41 | * Wide Webkit Support (including Android WebKit 2.3.X) 42 | * IE 10 43 | * IE 9 Supports Toggling, Dragging but no Transitions 44 | * IE 7/8 Supports Toggling but no dragging or Transitions 45 | 46 | ## Installation 47 | 48 | As standalone just include the file in a script tag: 49 | 50 | ```html 51 | 52 | ``` 53 | 54 | As a web component do: 55 | 56 | ```shell 57 | $ component install jakiestfu/Snap.js 58 | ``` 59 | 60 | ## Usage 61 | 62 | ```javascript 63 | var snapper = new Snap({ 64 | element: document.getElementById('content') 65 | }); 66 | ``` 67 | 68 | ## Settings and Defaults 69 | ```javascript 70 | settings = { 71 | element: null, 72 | dragger: null, 73 | disable: 'none', 74 | addBodyClasses: true, 75 | hyperextensible: true, 76 | resistance: 0.5, 77 | flickThreshold: 50, 78 | transitionSpeed: 0.3, 79 | easing: 'ease', 80 | maxPosition: 266, 81 | minPosition: -266, 82 | tapToClose: true, 83 | touchToDrag: true, 84 | slideIntent: 40, 85 | minDragDistance: 5 86 | } 87 | ``` 88 | 89 | * `element`: The element which the user will be sliding side to side 90 | * `dragger`: The element which the user will be using to slide the target element side to side 91 | * `disable`: String, set to 'left' or 'right' to disable the respective side 92 | * `addBodyClasses`: Add classes to the body to signify which side is being opened 93 | * `hyperextensible`: If false, pane may not be slide past the minPosition and maxPosition 94 | * `resistance`: The cooeficcient used to slow sliding when user has passed max or min threshold 95 | * `flickThreshold`: Number of pixels the user needs to swiftly travel to activate a "flick" open 96 | * `transitionSpeed`: The speed at which the pane slides open or closed 97 | * `easing`: The CSS3 Easing method you want to use for transitions 98 | * `maxPosition`: Maximum number of pixels the pane may be slid to the right 99 | * `minPosition`: Maximum number of pixels the pane may be slid to the left 100 | * `tapToClose`: If true, tapping an open pane will close it 101 | * `touchToDrag`: If true, dragging the target `settings.element` will open/close the pane 102 | * `minDragDistance`: The minimum amount of pixels the user needs to drag within the `slideIntent` degrees to move the pane 103 | * `slideIntent`: The number of degrees the user must initiate sliding in towards the left or right (see diagram below) 104 | 105 | Notes on Slide Intent: The slide intent is an int between 0 and 90, and represents the degrees in the first quadrant of a circle that you would like to have mirrored on the X *and* Y axis. For example, if you have 40 set as your `slideIntent` value, the user would only be able to slide the pane by dragging in the blue area in the diagram below. Once intent has been defined, it will not change until the user releases. 106 | 107 | 108 | 109 | 110 | ## Public Methods 111 | 112 | ### `open`: Opens the pane to the specified side 113 | 114 | ```javascript 115 | snapper.open('left'); 116 | // OR 117 | snapper.open('right'); 118 | ``` 119 | 120 | ### `close`: Closes the pane 121 | 122 | ```javascript 123 | snapper.close(); 124 | ``` 125 | 126 | ### `expand`: Opens the pane entirely 127 | 128 | ```javascript 129 | snapper.expand('left'); 130 | // OR 131 | snapper.expand('right'); 132 | ``` 133 | 134 | ### `disable`: Disables sliding events 135 | 136 | ```javascript 137 | snapper.disable(); 138 | ``` 139 | 140 | ### `enable`: Enables sliding events after disabling 141 | 142 | ```javascript 143 | snapper.enable(); 144 | ``` 145 | 146 | ### `on`: Adds an event hook 147 | 148 | ```javascript 149 | snapper.on('start', function(){ 150 | // Do Something 151 | }); 152 | ``` 153 | The available methods to hook into are as follows: 154 | 155 | * `start`: Fired when touching down on the draggable pane and it begins to move 156 | * `drag`: Fired when the pane has been moved or slid 157 | * `end`: Fired when the pane has been let go of 158 | * `animating`: Fired when the pane is animating 159 | * `animated`: Fired when the pane is finished it's animations 160 | * `ignore`: Fired when trying to drag the pane but ended up dragging on an ignored element 161 | * `close`: Fired when close is called directly or if tapToClose is set to true 162 | * `open`: Fired when the menu is opened 163 | * `expandLeft`: Fired on expand('left') 164 | * `expandRight`: Fired on expand('right') 165 | * `enable`: Fired on enable 166 | * `disable`: Fired on disable 167 | 168 | ### `off`: Removes an event hook 169 | 170 | ```javascript 171 | snapper.off('drag'); 172 | ``` 173 | The event names listed above apply for the `off` method. 174 | 175 | 176 | ### `settings`: Updates the settings for an already instantiated object 177 | ```javascript 178 | snapper.settings({yourSettings}); 179 | ``` 180 | Currently, `settings.element`, `settings.touchToDrag` cannot be updated. To update the element, instantiate a new object. To allow listening to a drag, use `snapper.enable()` 181 | 182 | ### `state`: Returns detailed information about the state of the pane 183 | 184 | ```javascript 185 | var data = snapper.state(); 186 | ``` 187 | The data returned from the `state` method will look like the following: 188 | 189 | ```javascript 190 | { 191 | state: "closed", // State of the Pane 192 | info:{ 193 | opening: "left", // Side which user intends to open 194 | towards: "right", // Direction user is dragging towards 195 | hyperExtending: false, // True if user is pulling past predefined bounds 196 | halfway: false, // True if pane is at least halfway open 197 | flick: false, // True if user has moved pane X amount of pixels in the open/close direction without changing directions 198 | translation:{ 199 | absolute: 20, // Pixels pane has translated 200 | relative: 21, // Pixels pane has translated relative to starting translation 201 | sinceDirectionChange: 10, // Pixels pane has translated since the direction of the pane has changed 202 | percentage: 40.571649 // The percentage that the Pane is open. Good or animating other things 203 | } 204 | } 205 | } 206 | ``` 207 | 208 | ## Gotchas 209 | 210 | ### Layout 211 | The layout itself is what most people will have a hard time emulating, so the simplest approach I have found is as follows: 212 | 213 | Two absolute elements, one to represent *all* the content, and another to represent *all* the drawers. The content has a higher z-index than the drawers. Within the drawers element, it's direct children should represent the containers for the drawers, these should be `fixed` or `absolute`. Assigning classes to your drawers to specify which side it is on is recommended. All absolutely positioned elements should have 0 for `top, left, right, bottom` properties, excluding your panes which will have `auto` set to their respective sides and a width assigned. The width of your drawers is usually the same number you want to use for `minPosition` and `maxPosition` 214 | 215 | ```html 216 | div.drawers {position: absolute;} 217 | div.left-drawer {position: absolute;} 218 | [content] 219 | div.right-drawer {position: absolute;} 220 | [content] 221 | div#content {position: absolute;} 222 | [top-bars] 223 | [content] {overflow: auto} 224 | [bottom-bars] 225 | ``` 226 | 227 | A sample layout is found in demo/apps/default.html. 228 | 229 | ### Independent Scrolling 230 | Some CSS is required to get some smooth ass scrolling. Utilize the CSS below to apply this to any of your elements: 231 | ```css 232 | .scrollable{ 233 | overflow: auto; 234 | -webkit-transition-property: top, bottom; 235 | transition-property: top, bottom; 236 | -webkit-transition-duration: .2s, .2s; 237 | transition-duration: .2s, .2s; 238 | -webkit-transition-timing-function: linear, linear; 239 | transition-timing-function: linear, linear; 240 | -webkit-overflow-scrolling: touch; 241 | } 242 | ``` 243 | 244 | ### Z-Indeces and Display 245 | Because of the nature of this code, drawers are just kind of stacked behind the content. To bring the proper drawer to the front, you can hook into Snaps.js' CSS classes: 246 | 247 | With `addBodyClasses` set to `true` in your initialize options, one of the two classess will be added to the body tag: `.snapjs-left` or `.snapjs-right`, depending on which pane is being open, respectively. This being said, you can apply your CSS like the following to show the proper drawers: 248 | 249 | ```css 250 | .snapjs-right .left-drawer, 251 | .snapjs-left .right-drawer { 252 | display: none; 253 | } 254 | ``` 255 | 256 | ## FAQ 257 | 258 | ### - How do I make a toggle button? 259 | Toggles have been a popular request, but rather than bog the library down with additional methods, you can utilize the powerful API of Snap.js to create your own toggle. Toggles can be done like the following: 260 | 261 | ```javascript 262 | myToggleButton.addEventListener('click', function(){ 263 | 264 | if( snapper.state().state=="left" ){ 265 | snapper.close(); 266 | } else { 267 | snapper.open('left'); 268 | } 269 | 270 | }); 271 | ``` 272 | 273 | ### - How do I disable Snap.js dragging for my touch slider? 274 | Snap.js supports cascading cancellation of events via a data attribute `data-snap-ignore`. If you were to use a slider, your markup might look like the following: 275 | 276 | ```html 277 |
278 |
    279 |
  • 280 |
  • 281 |
  • 282 |
  • 283 |
  • 284 |
285 |
286 | ``` 287 | 288 | All interactions on children elements of the element with the `data-snap-ignore` attribute will have their Snap.js events ignored. 289 | 290 | 291 | ### - I am using Push.js from Ratchet, I keep losing my events on my elements, how can I fix this? 292 | Simple. As wack as Push.js is (yes, it is in desperate need of attention as of v1.0.0), we can still solve this problem with it's only callback, `'push'`. 293 | 294 | ```javascript 295 | // The function that will initialize your Snap.js instance 296 | var doSnap = function(){ 297 | if(window.snapper){ 298 | // Snap.js already exists, we just need to re-bind events 299 | window.snapper.enable(); 300 | } else { 301 | // Initialize Snap.js 302 | window.snapper = new Snap({ 303 | element: document.getElementById('content') 304 | }); 305 | } 306 | }; 307 | 308 | window.addEventListener('push', doSnap); 309 | doSnap(); 310 | ``` 311 | 312 | ### - Snap.js works on my Android device but i cannot scroll the content in my drawers, what gives? 313 | Older Android devices (and iPhone as well) do not have native support for overflow scrolling. To solve this, you may use the wonderful library called [iScroll](https://github.com/cubiq/iscroll) 314 | 315 | ### - `transform: translate3d()` breaks my fixed child elements, how can I solve this? 316 | [This is a problem with Chromium](https://code.google.com/p/chromium/issues/detail?id=20574) and should be fixed soon. I would advise not having your direct children element set to fixed, that may possibly solve your problem. 317 | 318 | ### - I am experiencing a weird flicker when the CSS transform is applied 319 | To solve the flicker, apply the following CSS to the element in question 320 | ```css 321 | #content{ 322 | backface-visibility:hidden; 323 | -webkit-backface-visibility:hidden; /* Chrome and Safari */ 324 | -moz-backface-visibility:hidden; /* Firefox */ 325 | -ms-backface-visibility:hidden; /* Internet Explorer 10+ */ 326 | } 327 | ``` 328 | 329 | ## Compliments 330 | 331 | This code attempts to make your webapp's feel more "native". These other repos go well with it, too! 332 | 333 | * [Snap.js](https://github.com/jakiestfu/Snap.js) 334 | * [AppScroll.js](https://github.com/jakiestfu/AppScroll) 335 | * [fastclick](https://github.com/ftlabs/fastclick) 336 | 337 | ## Licensing 338 | 339 | MIT, dawg 340 | -------------------------------------------------------------------------------- /demo/apps/ratchet/ratchet.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ================================== 3 | * Ratchet v1.0.0 4 | * Licensed under The MIT License 5 | * http://opensource.org/licenses/MIT 6 | * ================================== 7 | */ 8 | 9 | /* ---------------------------------- 10 | * POPOVER v1.0.0 11 | * Licensed under The MIT License 12 | * http://opensource.org/licenses/MIT 13 | * ---------------------------------- */ 14 | 15 | !function () { 16 | 17 | var popover; 18 | 19 | var findPopovers = function (target) { 20 | var i, popovers = document.querySelectorAll('a'); 21 | for (; target && target !== document; target = target.parentNode) { 22 | for (i = popovers.length; i--;) { if (popovers[i] === target) return target; } 23 | } 24 | }; 25 | 26 | var onPopoverHidden = function () { 27 | document.body.removeChild(backdrop); 28 | popover.style.display = 'none'; 29 | popover.removeEventListener('webkitTransitionEnd', onPopoverHidden); 30 | } 31 | 32 | var backdrop = function () { 33 | var element = document.createElement('div'); 34 | 35 | element.classList.add('backdrop'); 36 | 37 | element.addEventListener('touchend', function () { 38 | popover.addEventListener('webkitTransitionEnd', onPopoverHidden); 39 | popover.classList.remove('visible'); 40 | }); 41 | 42 | return element; 43 | }(); 44 | 45 | var getPopover = function (e) { 46 | var anchor = findPopovers(e.target); 47 | 48 | if (!anchor || !anchor.hash) return; 49 | 50 | popover = document.querySelector(anchor.hash); 51 | 52 | if (!popover || !popover.classList.contains('popover')) return; 53 | 54 | return popover; 55 | } 56 | 57 | window.addEventListener('touchend', function (e) { 58 | var popover = getPopover(e); 59 | 60 | if (!popover) return; 61 | 62 | popover.style.display = 'block'; 63 | popover.offsetHeight; 64 | popover.classList.add('visible'); 65 | 66 | popover.parentNode.appendChild(backdrop); 67 | }); 68 | 69 | window.addEventListener('click', function (e) { if (getPopover(e)) e.preventDefault(); }); 70 | 71 | }(); 72 | /* ---------------------------------- 73 | * PUSH v1.0.0 74 | * Licensed under The MIT License 75 | * inspired by chris's jquery.pjax.js 76 | * http://opensource.org/licenses/MIT 77 | * ---------------------------------- */ 78 | 79 | !function () { 80 | 81 | var noop = function () {}; 82 | 83 | 84 | // Pushstate cacheing 85 | // ================== 86 | 87 | var isScrolling; 88 | var maxCacheLength = 20; 89 | var cacheMapping = sessionStorage; 90 | var domCache = {}; 91 | var transitionMap = { 92 | 'slide-in' : 'slide-out', 93 | 'slide-out' : 'slide-in', 94 | 'fade' : 'fade' 95 | }; 96 | var bars = { 97 | bartab : '.bar-tab', 98 | bartitle : '.bar-title', 99 | barfooter : '.bar-footer', 100 | barheadersecondary : '.bar-header-secondary' 101 | } 102 | 103 | var cacheReplace = function (data, updates) { 104 | PUSH.id = data.id; 105 | if (updates) data = getCached(data.id); 106 | cacheMapping[data.id] = JSON.stringify(data); 107 | window.history.replaceState(data.id, data.title, data.url); 108 | domCache[data.id] = document.body.cloneNode(true); 109 | }; 110 | 111 | var cachePush = function () { 112 | var id = PUSH.id; 113 | 114 | var cacheForwardStack = JSON.parse(cacheMapping.cacheForwardStack || '[]'); 115 | var cacheBackStack = JSON.parse(cacheMapping.cacheBackStack || '[]'); 116 | 117 | cacheBackStack.push(id); 118 | 119 | while (cacheForwardStack.length) delete cacheMapping[cacheForwardStack.shift()]; 120 | while (cacheBackStack.length > maxCacheLength) delete cacheMapping[cacheBackStack.shift()]; 121 | 122 | window.history.pushState(null, '', cacheMapping[PUSH.id].url); 123 | 124 | cacheMapping.cacheForwardStack = JSON.stringify(cacheForwardStack); 125 | cacheMapping.cacheBackStack = JSON.stringify(cacheBackStack); 126 | }; 127 | 128 | var cachePop = function (id, direction) { 129 | var forward = direction == 'forward'; 130 | var cacheForwardStack = JSON.parse(cacheMapping.cacheForwardStack || '[]'); 131 | var cacheBackStack = JSON.parse(cacheMapping.cacheBackStack || '[]'); 132 | var pushStack = forward ? cacheBackStack : cacheForwardStack; 133 | var popStack = forward ? cacheForwardStack : cacheBackStack; 134 | 135 | if (PUSH.id) pushStack.push(PUSH.id); 136 | popStack.pop(); 137 | 138 | cacheMapping.cacheForwardStack = JSON.stringify(cacheForwardStack); 139 | cacheMapping.cacheBackStack = JSON.stringify(cacheBackStack); 140 | }; 141 | 142 | var getCached = function (id) { 143 | return JSON.parse(cacheMapping[id] || null) || {}; 144 | }; 145 | 146 | var getTarget = function (e) { 147 | var target = findTarget(e.target); 148 | 149 | if ( 150 | ! target 151 | || e.which > 1 152 | || e.metaKey 153 | || e.ctrlKey 154 | || isScrolling 155 | || location.protocol !== target.protocol 156 | || location.host !== target.host 157 | || !target.hash && /#/.test(target.href) 158 | || target.hash && target.href.replace(target.hash, '') === location.href.replace(location.hash, '') 159 | || target.getAttribute('data-ignore') == 'push' 160 | ) return; 161 | 162 | return target; 163 | }; 164 | 165 | 166 | // Main event handlers (touchend, popstate) 167 | // ========================================== 168 | 169 | var touchend = function (e) { 170 | var target = getTarget(e); 171 | 172 | if (!target) return; 173 | 174 | e.preventDefault(); 175 | 176 | PUSH({ 177 | url : target.href, 178 | hash : target.hash, 179 | timeout : target.getAttribute('data-timeout'), 180 | transition : target.getAttribute('data-transition') 181 | }); 182 | }; 183 | 184 | var popstate = function (e) { 185 | var key; 186 | var barElement; 187 | var activeObj; 188 | var activeDom; 189 | var direction; 190 | var transition; 191 | var transitionFrom; 192 | var transitionFromObj; 193 | var id = e.state; 194 | 195 | if (!id || !cacheMapping[id]) return; 196 | 197 | direction = PUSH.id < id ? 'forward' : 'back'; 198 | 199 | cachePop(id, direction); 200 | 201 | activeObj = getCached(id); 202 | activeDom = domCache[id]; 203 | 204 | if (activeObj.title) document.title = activeObj.title; 205 | 206 | if (direction == 'back') { 207 | transitionFrom = JSON.parse(direction == 'back' ? cacheMapping.cacheForwardStack : cacheMapping.cacheBackStack); 208 | transitionFromObj = getCached(transitionFrom[transitionFrom.length - 1]); 209 | } else { 210 | transitionFromObj = activeObj; 211 | } 212 | 213 | if (direction == 'back' && !transitionFromObj.id) return PUSH.id = id; 214 | 215 | transition = direction == 'back' ? transitionMap[transitionFromObj.transition] : transitionFromObj.transition; 216 | 217 | if (!activeDom) { 218 | return PUSH({ 219 | id : activeObj.id, 220 | url : activeObj.url, 221 | title : activeObj.title, 222 | timeout : activeObj.timeout, 223 | transition : transition, 224 | ignorePush : true 225 | }); 226 | } 227 | 228 | if (transitionFromObj.transition) { 229 | activeObj = extendWithDom(activeObj, '.content', activeDom.cloneNode(true)); 230 | for (key in bars) { 231 | barElement = document.querySelector(bars[key]) 232 | if (activeObj[key]) swapContent(activeObj[key], barElement); 233 | else if (barElement) barElement.parentNode.removeChild(barElement); 234 | } 235 | } 236 | 237 | swapContent( 238 | (activeObj.contents || activeDom).cloneNode(true), 239 | document.querySelector('.content'), 240 | transition 241 | ); 242 | 243 | PUSH.id = id; 244 | 245 | document.body.offsetHeight; // force reflow to prevent scroll 246 | }; 247 | 248 | 249 | // Core PUSH functionality 250 | // ======================= 251 | 252 | var PUSH = function (options) { 253 | var key; 254 | var data = {}; 255 | var xhr = PUSH.xhr; 256 | 257 | options.container = options.container || options.transition ? document.querySelector('.content') : document.body; 258 | 259 | for (key in bars) { 260 | options[key] = options[key] || document.querySelector(bars[key]); 261 | } 262 | 263 | if (xhr && xhr.readyState < 4) { 264 | xhr.onreadystatechange = noop; 265 | xhr.abort() 266 | } 267 | 268 | xhr = new XMLHttpRequest(); 269 | xhr.open('GET', options.url, true); 270 | xhr.setRequestHeader('X-PUSH', 'true'); 271 | 272 | xhr.onreadystatechange = function () { 273 | if (options._timeout) clearTimeout(options._timeout); 274 | if (xhr.readyState == 4) xhr.status == 200 ? success(xhr, options) : failure(options.url); 275 | }; 276 | 277 | if (!PUSH.id) { 278 | cacheReplace({ 279 | id : +new Date, 280 | url : window.location.href, 281 | title : document.title, 282 | timeout : options.timeout, 283 | transition : null 284 | }); 285 | } 286 | 287 | if (options.timeout) { 288 | options._timeout = setTimeout(function () { xhr.abort('timeout'); }, options.timeout); 289 | } 290 | 291 | xhr.send(); 292 | 293 | if (xhr.readyState && !options.ignorePush) cachePush(); 294 | }; 295 | 296 | 297 | // Main XHR handlers 298 | // ================= 299 | 300 | var success = function (xhr, options) { 301 | var key; 302 | var barElement; 303 | var data = parseXHR(xhr, options); 304 | 305 | if (!data.contents) return locationReplace(options.url); 306 | 307 | if (data.title) document.title = data.title; 308 | 309 | if (options.transition) { 310 | for (key in bars) { 311 | barElement = document.querySelector(bars[key]) 312 | if (data[key]) swapContent(data[key], barElement); 313 | else if (barElement) barElement.parentNode.removeChild(barElement); 314 | } 315 | } 316 | 317 | swapContent(data.contents, options.container, options.transition, function () { 318 | cacheReplace({ 319 | id : options.id || +new Date, 320 | url : data.url, 321 | title : data.title, 322 | timeout : options.timeout, 323 | transition : options.transition 324 | }, options.id); 325 | triggerStateChange(); 326 | }); 327 | 328 | if (!options.ignorePush && window._gaq) _gaq.push(['_trackPageview']) // google analytics 329 | if (!options.hash) return; 330 | }; 331 | 332 | var failure = function (url) { 333 | throw new Error('Could not get: ' + url) 334 | }; 335 | 336 | 337 | // PUSH helpers 338 | // ============ 339 | 340 | var swapContent = function (swap, container, transition, complete) { 341 | var enter; 342 | var containerDirection; 343 | var swapDirection; 344 | 345 | if (!transition) { 346 | if (container) container.innerHTML = swap.innerHTML; 347 | else if (swap.classList.contains('content')) document.body.appendChild(swap); 348 | else document.body.insertBefore(swap, document.querySelector('.content')); 349 | } else { 350 | enter = /in$/.test(transition); 351 | 352 | if (transition == 'fade') { 353 | container.classList.add('in'); 354 | container.classList.add('fade'); 355 | swap.classList.add('fade'); 356 | } 357 | 358 | if (/slide/.test(transition)) { 359 | swap.classList.add(enter ? 'right' : 'left'); 360 | swap.classList.add('slide'); 361 | container.classList.add('slide'); 362 | } 363 | 364 | container.parentNode.insertBefore(swap, container); 365 | } 366 | 367 | if (!transition) complete && complete(); 368 | 369 | if (transition == 'fade') { 370 | container.offsetWidth; // force reflow 371 | container.classList.remove('in'); 372 | container.addEventListener('webkitTransitionEnd', fadeContainerEnd); 373 | 374 | function fadeContainerEnd() { 375 | container.removeEventListener('webkitTransitionEnd', fadeContainerEnd); 376 | swap.classList.add('in'); 377 | swap.addEventListener('webkitTransitionEnd', fadeSwapEnd); 378 | } 379 | function fadeSwapEnd () { 380 | swap.removeEventListener('webkitTransitionEnd', fadeSwapEnd); 381 | container.parentNode.removeChild(container); 382 | swap.classList.remove('fade'); 383 | swap.classList.remove('in'); 384 | complete && complete(); 385 | } 386 | } 387 | 388 | if (/slide/.test(transition)) { 389 | container.offsetWidth; // force reflow 390 | swapDirection = enter ? 'right' : 'left' 391 | containerDirection = enter ? 'left' : 'right' 392 | container.classList.add(containerDirection); 393 | swap.classList.remove(swapDirection); 394 | swap.addEventListener('webkitTransitionEnd', slideEnd); 395 | 396 | function slideEnd() { 397 | swap.removeEventListener('webkitTransitionEnd', slideEnd); 398 | swap.classList.remove('slide'); 399 | swap.classList.remove(swapDirection); 400 | container.parentNode.removeChild(container); 401 | complete && complete(); 402 | } 403 | } 404 | }; 405 | 406 | var triggerStateChange = function () { 407 | var e = new CustomEvent('push', { 408 | detail: { state: getCached(PUSH.id) }, 409 | bubbles: true, 410 | cancelable: true 411 | }); 412 | 413 | window.dispatchEvent(e); 414 | }; 415 | 416 | var findTarget = function (target) { 417 | var i, toggles = document.querySelectorAll('a'); 418 | for (; target && target !== document; target = target.parentNode) { 419 | for (i = toggles.length; i--;) { if (toggles[i] === target) return target; } 420 | } 421 | }; 422 | 423 | var locationReplace = function (url) { 424 | window.history.replaceState(null, '', '#'); 425 | window.location.replace(url); 426 | }; 427 | 428 | var parseURL = function (url) { 429 | var a = document.createElement('a'); a.href = url; return a; 430 | }; 431 | 432 | var extendWithDom = function (obj, fragment, dom) { 433 | var i; 434 | var result = {}; 435 | 436 | for (i in obj) result[i] = obj[i]; 437 | 438 | Object.keys(bars).forEach(function (key) { 439 | var el = dom.querySelector(bars[key]); 440 | if (el) el.parentNode.removeChild(el); 441 | result[key] = el; 442 | }); 443 | 444 | result.contents = dom.querySelector(fragment); 445 | 446 | return result; 447 | }; 448 | 449 | var parseXHR = function (xhr, options) { 450 | var head; 451 | var body; 452 | var data = {}; 453 | var responseText = xhr.responseText; 454 | 455 | data.url = options.url; 456 | 457 | if (!responseText) return data; 458 | 459 | if (/]*>([\s\S.]*)<\/head>/i)[0] 463 | body.innerHTML = responseText.match(/]*>([\s\S.]*)<\/body>/i)[0] 464 | } else { 465 | head = body = document.createElement('div'); 466 | head.innerHTML = responseText; 467 | } 468 | 469 | data.title = head.querySelector('title'); 470 | data.title = data.title && data.title.innerText.trim(); 471 | 472 | if (options.transition) data = extendWithDom(data, '.content', body); 473 | else data.contents = body; 474 | 475 | return data; 476 | }; 477 | 478 | 479 | // Attach PUSH event handlers 480 | // ========================== 481 | 482 | window.addEventListener('touchstart', function () { isScrolling = false; }); 483 | window.addEventListener('touchmove', function () { isScrolling = true; }) 484 | window.addEventListener('touchend', touchend); 485 | window.addEventListener('click', function (e) { if (getTarget(e)) e.preventDefault(); }); 486 | window.addEventListener('popstate', popstate); 487 | 488 | }();/* ---------------------------------- 489 | * TABS v1.0.0 490 | * Licensed under The MIT License 491 | * http://opensource.org/licenses/MIT 492 | * ---------------------------------- */ 493 | 494 | !function () { 495 | var getTarget = function (target) { 496 | var i, popovers = document.querySelectorAll('.segmented-controller li a'); 497 | for (; target && target !== document; target = target.parentNode) { 498 | for (i = popovers.length; i--;) { if (popovers[i] === target) return target; } 499 | } 500 | }; 501 | 502 | window.addEventListener("touchend", function (e) { 503 | var activeTab; 504 | var activeBody; 505 | var targetBody; 506 | var targetTab; 507 | var className = 'active'; 508 | var classSelector = '.' + className; 509 | var targetAnchor = getTarget(e.target); 510 | 511 | if (!targetAnchor) return; 512 | 513 | targetTab = targetAnchor.parentNode; 514 | activeTab = targetTab.parentNode.querySelector(classSelector); 515 | 516 | if (activeTab) activeTab.classList.remove(className); 517 | 518 | targetTab.classList.add(className); 519 | 520 | if (!targetAnchor.hash) return; 521 | 522 | targetBody = document.querySelector(targetAnchor.hash); 523 | 524 | if (!targetBody) return; 525 | 526 | activeBody = targetBody.parentNode.querySelector(classSelector); 527 | 528 | if (activeBody) activeBody.classList.remove(className); 529 | 530 | targetBody.classList.add(className) 531 | }); 532 | 533 | window.addEventListener('click', function (e) { if (getTarget(e.target)) e.preventDefault(); }); 534 | }();/* ---------------------------------- 535 | * SLIDER v1.0.0 536 | * Licensed under The MIT License 537 | * Adapted from Brad Birdsall's swipe 538 | * http://opensource.org/licenses/MIT 539 | * ---------------------------------- */ 540 | 541 | !function () { 542 | 543 | var pageX; 544 | var pageY; 545 | var slider; 546 | var deltaX; 547 | var deltaY; 548 | var offsetX; 549 | var lastSlide; 550 | var startTime; 551 | var resistance; 552 | var sliderWidth; 553 | var slideNumber; 554 | var isScrolling; 555 | var scrollableArea; 556 | 557 | var getSlider = function (target) { 558 | var i, sliders = document.querySelectorAll('.slider ul'); 559 | for (; target && target !== document; target = target.parentNode) { 560 | for (i = sliders.length; i--;) { if (sliders[i] === target) return target; } 561 | } 562 | } 563 | 564 | var getScroll = function () { 565 | var translate3d = slider.style.webkitTransform.match(/translate3d\(([^,]*)/); 566 | return parseInt(translate3d ? translate3d[1] : 0) 567 | }; 568 | 569 | var setSlideNumber = function (offset) { 570 | var round = offset ? (deltaX < 0 ? 'ceil' : 'floor') : 'round'; 571 | slideNumber = Math[round](getScroll() / ( scrollableArea / slider.children.length) ); 572 | slideNumber += offset; 573 | slideNumber = Math.min(slideNumber, 0); 574 | slideNumber = Math.max(-(slider.children.length - 1), slideNumber); 575 | } 576 | 577 | var onTouchStart = function (e) { 578 | slider = getSlider(e.target); 579 | 580 | if (!slider) return; 581 | 582 | var firstItem = slider.querySelector('li'); 583 | 584 | scrollableArea = firstItem.offsetWidth * slider.children.length; 585 | isScrolling = undefined; 586 | sliderWidth = slider.offsetWidth; 587 | resistance = 1; 588 | lastSlide = -(slider.children.length - 1); 589 | startTime = +new Date; 590 | pageX = e.touches[0].pageX; 591 | pageY = e.touches[0].pageY; 592 | 593 | setSlideNumber(0); 594 | 595 | slider.style['-webkit-transition-duration'] = 0; 596 | }; 597 | 598 | var onTouchMove = function (e) { 599 | if (e.touches.length > 1 || !slider) return; // Exit if a pinch || no slider 600 | 601 | deltaX = e.touches[0].pageX - pageX; 602 | deltaY = e.touches[0].pageY - pageY; 603 | pageX = e.touches[0].pageX; 604 | pageY = e.touches[0].pageY; 605 | 606 | if (typeof isScrolling == 'undefined') { 607 | isScrolling = Math.abs(deltaY) > Math.abs(deltaX); 608 | } 609 | 610 | if (isScrolling) return; 611 | 612 | offsetX = (deltaX / resistance) + getScroll(); 613 | 614 | e.preventDefault(); 615 | 616 | resistance = slideNumber == 0 && deltaX > 0 ? (pageX / sliderWidth) + 1.25 : 617 | slideNumber == lastSlide && deltaX < 0 ? (Math.abs(pageX) / sliderWidth) + 1.25 : 1; 618 | 619 | slider.style.webkitTransform = 'translate3d(' + offsetX + 'px,0,0)'; 620 | }; 621 | 622 | var onTouchEnd = function (e) { 623 | if (!slider || isScrolling) return; 624 | 625 | setSlideNumber( 626 | (+new Date) - startTime < 1000 && Math.abs(deltaX) > 15 ? (deltaX < 0 ? -1 : 1) : 0 627 | ); 628 | 629 | offsetX = slideNumber * sliderWidth; 630 | 631 | slider.style['-webkit-transition-duration'] = '.2s'; 632 | slider.style.webkitTransform = 'translate3d(' + offsetX + 'px,0,0)'; 633 | 634 | e = new CustomEvent('slide', { 635 | detail: { slideNumber: Math.abs(slideNumber) }, 636 | bubbles: true, 637 | cancelable: true 638 | }); 639 | 640 | slider.parentNode.dispatchEvent(e); 641 | }; 642 | 643 | window.addEventListener('touchstart', onTouchStart); 644 | window.addEventListener('touchmove', onTouchMove); 645 | window.addEventListener('touchend', onTouchEnd); 646 | 647 | }(); 648 | /* ---------------------------------- 649 | * TOGGLE v1.0.0 650 | * Licensed under The MIT License 651 | * http://opensource.org/licenses/MIT 652 | * ---------------------------------- */ 653 | 654 | !function () { 655 | 656 | var start = {}; 657 | var touchMove = false; 658 | var distanceX = false; 659 | var toggle = false; 660 | 661 | var findToggle = function (target) { 662 | var i, toggles = document.querySelectorAll('.toggle'); 663 | for (; target && target !== document; target = target.parentNode) { 664 | for (i = toggles.length; i--;) { if (toggles[i] === target) return target; } 665 | } 666 | } 667 | 668 | window.addEventListener('touchstart', function (e) { 669 | e = e.originalEvent || e; 670 | 671 | toggle = findToggle(e.target); 672 | 673 | if (!toggle) return; 674 | 675 | var handle = toggle.querySelector('.toggle-handle'); 676 | var toggleWidth = toggle.offsetWidth; 677 | var handleWidth = handle.offsetWidth; 678 | var offset = toggle.classList.contains('active') ? toggleWidth - handleWidth : 0; 679 | 680 | start = { pageX : e.touches[0].pageX - offset, pageY : e.touches[0].pageY }; 681 | touchMove = false; 682 | 683 | // todo: probably should be moved to the css 684 | toggle.style['-webkit-transition-duration'] = 0; 685 | }); 686 | 687 | window.addEventListener('touchmove', function (e) { 688 | e = e.originalEvent || e; 689 | 690 | if (e.touches.length > 1) return; // Exit if a pinch 691 | 692 | if (!toggle) return; 693 | 694 | var handle = toggle.querySelector('.toggle-handle'); 695 | var current = e.touches[0]; 696 | var toggleWidth = toggle.offsetWidth; 697 | var handleWidth = handle.offsetWidth; 698 | var offset = toggleWidth - handleWidth; 699 | 700 | touchMove = true; 701 | distanceX = current.pageX - start.pageX; 702 | 703 | if (Math.abs(distanceX) < Math.abs(current.pageY - start.pageY)) return; 704 | 705 | e.preventDefault(); 706 | 707 | if (distanceX < 0) return handle.style.webkitTransform = 'translate3d(0,0,0)'; 708 | if (distanceX > offset) return handle.style.webkitTransform = 'translate3d(' + offset + 'px,0,0)'; 709 | 710 | handle.style.webkitTransform = 'translate3d(' + distanceX + 'px,0,0)'; 711 | 712 | toggle.classList[(distanceX > (toggleWidth/2 - handleWidth/2)) ? 'add' : 'remove']('active'); 713 | }); 714 | 715 | window.addEventListener('touchend', function (e) { 716 | if (!toggle) return; 717 | 718 | var handle = toggle.querySelector('.toggle-handle'); 719 | var toggleWidth = toggle.offsetWidth; 720 | var handleWidth = handle.offsetWidth; 721 | var offset = toggleWidth - handleWidth; 722 | var slideOn = (!touchMove && !toggle.classList.contains('active')) || (touchMove && (distanceX > (toggleWidth/2 - handleWidth/2))); 723 | 724 | if (slideOn) handle.style.webkitTransform = 'translate3d(' + offset + 'px,0,0)'; 725 | else handle.style.webkitTransform = 'translate3d(0,0,0)'; 726 | 727 | toggle.classList[slideOn ? 'add' : 'remove']('active'); 728 | 729 | e = new CustomEvent('toggle', { 730 | detail: { isActive: slideOn }, 731 | bubbles: true, 732 | cancelable: true 733 | }); 734 | 735 | toggle.dispatchEvent(e); 736 | 737 | touchMove = false; 738 | toggle = false; 739 | }); 740 | 741 | }(); 742 | -------------------------------------------------------------------------------- /snap.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Snap.js 3 | * 4 | * Copyright 2013, Jacob Kelley - http://jakiestfu.com/ 5 | * Released under the MIT Licence 6 | * http://opensource.org/licenses/MIT 7 | * 8 | * Github: http://github.com/jakiestfu/Snap.js/ 9 | * Version: 1.9.3 10 | */ 11 | /*jslint browser: true*/ 12 | /*global define, module, ender*/ 13 | (function(win, doc) { 14 | 'use strict'; 15 | var Snap = Snap || function(userOpts) { 16 | var settings = { 17 | element: null, 18 | dragger: null, 19 | disable: 'none', 20 | addBodyClasses: true, 21 | hyperextensible: true, 22 | resistance: 0.5, 23 | flickThreshold: 50, 24 | transitionSpeed: 0.3, 25 | easing: 'ease', 26 | maxPosition: 266, 27 | minPosition: -266, 28 | tapToClose: true, 29 | touchToDrag: true, 30 | slideIntent: 40, // degrees 31 | minDragDistance: 5 32 | }, 33 | cache = { 34 | simpleStates: { 35 | opening: null, 36 | towards: null, 37 | hyperExtending: null, 38 | halfway: null, 39 | flick: null, 40 | translation: { 41 | absolute: 0, 42 | relative: 0, 43 | sinceDirectionChange: 0, 44 | percentage: 0 45 | } 46 | } 47 | }, 48 | eventList = {}, 49 | utils = { 50 | hasTouch: ('ontouchstart' in doc.documentElement || win.navigator.msPointerEnabled), 51 | eventType: function(action) { 52 | var eventTypes = { 53 | down: (utils.hasTouch ? 'touchstart' : 'mousedown'), 54 | move: (utils.hasTouch ? 'touchmove' : 'mousemove'), 55 | up: (utils.hasTouch ? 'touchend' : 'mouseup'), 56 | out: (utils.hasTouch ? 'touchcancel' : 'mouseout') 57 | }; 58 | return eventTypes[action]; 59 | }, 60 | page: function(t, e){ 61 | return (utils.hasTouch && e.touches.length && e.touches[0]) ? e.touches[0]['page'+t] : e['page'+t]; 62 | }, 63 | klass: { 64 | has: function(el, name){ 65 | return (el.className).indexOf(name) !== -1; 66 | }, 67 | add: function(el, name){ 68 | if(!utils.klass.has(el, name) && settings.addBodyClasses){ 69 | el.className += " "+name; 70 | } 71 | }, 72 | remove: function(el, name){ 73 | if(settings.addBodyClasses){ 74 | el.className = (el.className).replace(name, "").replace(/^\s+|\s+$/g, ''); 75 | } 76 | } 77 | }, 78 | dispatchEvent: function(type) { 79 | if (typeof eventList[type] === 'function') { 80 | return eventList[type].call(); 81 | } 82 | }, 83 | vendor: function(){ 84 | var tmp = doc.createElement("div"), 85 | prefixes = 'webkit Moz O ms'.split(' '), 86 | i; 87 | for (i in prefixes) { 88 | if (typeof tmp.style[prefixes[i] + 'Transition'] !== 'undefined') { 89 | return prefixes[i]; 90 | } 91 | } 92 | }, 93 | transitionCallback: function(){ 94 | return (cache.vendor==='Moz' || cache.vendor==='ms') ? 'transitionend' : cache.vendor+'TransitionEnd'; 95 | }, 96 | canTransform: function(){ 97 | return typeof settings.element.style[cache.vendor+'Transform'] !== 'undefined'; 98 | }, 99 | deepExtend: function(destination, source) { 100 | var property; 101 | for (property in source) { 102 | if (source[property] && source[property].constructor && source[property].constructor === Object) { 103 | destination[property] = destination[property] || {}; 104 | utils.deepExtend(destination[property], source[property]); 105 | } else { 106 | destination[property] = source[property]; 107 | } 108 | } 109 | return destination; 110 | }, 111 | angleOfDrag: function(x, y) { 112 | var degrees, theta; 113 | // Calc Theta 114 | theta = Math.atan2(-(cache.startDragY - y), (cache.startDragX - x)); 115 | if (theta < 0) { 116 | theta += 2 * Math.PI; 117 | } 118 | // Calc Degrees 119 | degrees = Math.floor(theta * (180 / Math.PI) - 180); 120 | if (degrees < 0 && degrees > -180) { 121 | degrees = 360 - Math.abs(degrees); 122 | } 123 | return Math.abs(degrees); 124 | }, 125 | events: { 126 | addEvent: function addEvent(element, eventName, func) { 127 | if (element.addEventListener) { 128 | return element.addEventListener(eventName, func, false); 129 | } else if (element.attachEvent) { 130 | return element.attachEvent("on" + eventName, func); 131 | } 132 | }, 133 | removeEvent: function addEvent(element, eventName, func) { 134 | if (element.addEventListener) { 135 | return element.removeEventListener(eventName, func, false); 136 | } else if (element.attachEvent) { 137 | return element.detachEvent("on" + eventName, func); 138 | } 139 | }, 140 | prevent: function(e) { 141 | if (e.preventDefault) { 142 | e.preventDefault(); 143 | } else { 144 | e.returnValue = false; 145 | } 146 | } 147 | }, 148 | parentUntil: function(el, attr) { 149 | var isStr = typeof attr === 'string'; 150 | while (el.parentNode) { 151 | if (isStr && el.getAttribute && el.getAttribute(attr)){ 152 | return el; 153 | } else if(!isStr && el === attr){ 154 | return el; 155 | } 156 | el = el.parentNode; 157 | } 158 | return null; 159 | } 160 | }, 161 | action = { 162 | translate: { 163 | get: { 164 | matrix: function(index) { 165 | 166 | if( !utils.canTransform() ){ 167 | return parseInt(settings.element.style.left, 10); 168 | } else { 169 | var matrix = win.getComputedStyle(settings.element)[cache.vendor+'Transform'].match(/\((.*)\)/), 170 | ieOffset = 8; 171 | if (matrix) { 172 | matrix = matrix[1].split(','); 173 | if(matrix.length===16){ 174 | index+=ieOffset; 175 | } 176 | return parseInt(matrix[index], 10); 177 | } 178 | return 0; 179 | } 180 | } 181 | }, 182 | easeCallback: function(){ 183 | settings.element.style[cache.vendor+'Transition'] = ''; 184 | cache.translation = action.translate.get.matrix(4); 185 | cache.easing = false; 186 | clearInterval(cache.animatingInterval); 187 | 188 | if(cache.easingTo===0){ 189 | utils.klass.remove(doc.body, 'snapjs-right'); 190 | utils.klass.remove(doc.body, 'snapjs-left'); 191 | } 192 | 193 | utils.dispatchEvent('animated'); 194 | utils.events.removeEvent(settings.element, utils.transitionCallback(), action.translate.easeCallback); 195 | }, 196 | easeTo: function(n) { 197 | 198 | if( !utils.canTransform() ){ 199 | cache.translation = n; 200 | action.translate.x(n); 201 | } else { 202 | cache.easing = true; 203 | cache.easingTo = n; 204 | 205 | settings.element.style[cache.vendor+'Transition'] = 'all ' + settings.transitionSpeed + 's ' + settings.easing; 206 | 207 | cache.animatingInterval = setInterval(function() { 208 | utils.dispatchEvent('animating'); 209 | }, 1); 210 | 211 | utils.events.addEvent(settings.element, utils.transitionCallback(), action.translate.easeCallback); 212 | action.translate.x(n); 213 | } 214 | if(n===0){ 215 | settings.element.style[cache.vendor+'Transform'] = ''; 216 | } 217 | }, 218 | x: function(n) { 219 | if( (settings.disable==='left' && n>0) || 220 | (settings.disable==='right' && n<0) 221 | ){ return; } 222 | 223 | if( !settings.hyperextensible ){ 224 | if( n===settings.maxPosition || n>settings.maxPosition ){ 225 | n=settings.maxPosition; 226 | } else if( n===settings.minPosition || n 0, 319 | translateTo = whileDragX, 320 | diff; 321 | 322 | // Shown no intent already 323 | if((cache.intentChecked && !cache.hasIntent)){ 324 | return; 325 | } 326 | 327 | if(settings.addBodyClasses){ 328 | if((absoluteTranslation)>0){ 329 | utils.klass.add(doc.body, 'snapjs-left'); 330 | utils.klass.remove(doc.body, 'snapjs-right'); 331 | } else if((absoluteTranslation)<0){ 332 | utils.klass.add(doc.body, 'snapjs-right'); 333 | utils.klass.remove(doc.body, 'snapjs-left'); 334 | } 335 | } 336 | 337 | if (cache.hasIntent === false || cache.hasIntent === null) { 338 | var deg = utils.angleOfDrag(thePageX, thePageY), 339 | inRightRange = (deg >= 0 && deg <= settings.slideIntent) || (deg <= 360 && deg > (360 - settings.slideIntent)), 340 | inLeftRange = (deg >= 180 && deg <= (180 + settings.slideIntent)) || (deg <= 180 && deg >= (180 - settings.slideIntent)); 341 | if (!inLeftRange && !inRightRange) { 342 | cache.hasIntent = false; 343 | } else { 344 | cache.hasIntent = true; 345 | } 346 | cache.intentChecked = true; 347 | } 348 | 349 | if ( 350 | (settings.minDragDistance>=Math.abs(thePageX-cache.startDragX)) || // Has user met minimum drag distance? 351 | (cache.hasIntent === false) 352 | ) { 353 | return; 354 | } 355 | 356 | utils.events.prevent(e); 357 | utils.dispatchEvent('drag'); 358 | 359 | cache.dragWatchers.current = thePageX; 360 | // Determine which direction we are going 361 | if (cache.dragWatchers.last > thePageX) { 362 | if (cache.dragWatchers.state !== 'left') { 363 | cache.dragWatchers.state = 'left'; 364 | cache.dragWatchers.hold = thePageX; 365 | } 366 | cache.dragWatchers.last = thePageX; 367 | } else if (cache.dragWatchers.last < thePageX) { 368 | if (cache.dragWatchers.state !== 'right') { 369 | cache.dragWatchers.state = 'right'; 370 | cache.dragWatchers.hold = thePageX; 371 | } 372 | cache.dragWatchers.last = thePageX; 373 | } 374 | if (openingLeft) { 375 | // Pulling too far to the right 376 | if (settings.maxPosition < absoluteTranslation) { 377 | diff = (absoluteTranslation - settings.maxPosition) * settings.resistance; 378 | translateTo = whileDragX - diff; 379 | } 380 | cache.simpleStates = { 381 | opening: 'left', 382 | towards: cache.dragWatchers.state, 383 | hyperExtending: settings.maxPosition < absoluteTranslation, 384 | halfway: absoluteTranslation > (settings.maxPosition / 2), 385 | flick: Math.abs(cache.dragWatchers.current - cache.dragWatchers.hold) > settings.flickThreshold, 386 | translation: { 387 | absolute: absoluteTranslation, 388 | relative: whileDragX, 389 | sinceDirectionChange: (cache.dragWatchers.current - cache.dragWatchers.hold), 390 | percentage: (absoluteTranslation/settings.maxPosition)*100 391 | } 392 | }; 393 | } else { 394 | // Pulling too far to the left 395 | if (settings.minPosition > absoluteTranslation) { 396 | diff = (absoluteTranslation - settings.minPosition) * settings.resistance; 397 | translateTo = whileDragX - diff; 398 | } 399 | cache.simpleStates = { 400 | opening: 'right', 401 | towards: cache.dragWatchers.state, 402 | hyperExtending: settings.minPosition > absoluteTranslation, 403 | halfway: absoluteTranslation < (settings.minPosition / 2), 404 | flick: Math.abs(cache.dragWatchers.current - cache.dragWatchers.hold) > settings.flickThreshold, 405 | translation: { 406 | absolute: absoluteTranslation, 407 | relative: whileDragX, 408 | sinceDirectionChange: (cache.dragWatchers.current - cache.dragWatchers.hold), 409 | percentage: (absoluteTranslation/settings.minPosition)*100 410 | } 411 | }; 412 | } 413 | action.translate.x(translateTo + translated); 414 | } 415 | }, 416 | endDrag: function(e) { 417 | if (cache.isDragging) { 418 | utils.dispatchEvent('end'); 419 | var translated = action.translate.get.matrix(4); 420 | 421 | // Tap Close 422 | if (cache.dragWatchers.current === 0 && translated !== 0 && settings.tapToClose) { 423 | utils.dispatchEvent('close'); 424 | utils.events.prevent(e); 425 | action.translate.easeTo(0); 426 | cache.isDragging = false; 427 | cache.startDragX = 0; 428 | return; 429 | } 430 | 431 | // Revealing Left 432 | if (cache.simpleStates.opening === 'left') { 433 | // Halfway, Flicking, or Too Far Out 434 | if ((cache.simpleStates.halfway || cache.simpleStates.hyperExtending || cache.simpleStates.flick)) { 435 | if (cache.simpleStates.flick && cache.simpleStates.towards === 'left') { // Flicking Closed 436 | action.translate.easeTo(0); 437 | } else if ( 438 | (cache.simpleStates.flick && cache.simpleStates.towards === 'right') || // Flicking Open OR 439 | (cache.simpleStates.halfway || cache.simpleStates.hyperExtending) // At least halfway open OR hyperextending 440 | ) { 441 | action.translate.easeTo(settings.maxPosition); // Open Left 442 | } 443 | } else { 444 | action.translate.easeTo(0); // Close Left 445 | } 446 | // Revealing Right 447 | } else if (cache.simpleStates.opening === 'right') { 448 | // Halfway, Flicking, or Too Far Out 449 | if ((cache.simpleStates.halfway || cache.simpleStates.hyperExtending || cache.simpleStates.flick)) { 450 | if (cache.simpleStates.flick && cache.simpleStates.towards === 'right') { // Flicking Closed 451 | action.translate.easeTo(0); 452 | } else if ( 453 | (cache.simpleStates.flick && cache.simpleStates.towards === 'left') || // Flicking Open OR 454 | (cache.simpleStates.halfway || cache.simpleStates.hyperExtending) // At least halfway open OR hyperextending 455 | ) { 456 | action.translate.easeTo(settings.minPosition); // Open Right 457 | } 458 | } else { 459 | action.translate.easeTo(0); // Close Right 460 | } 461 | } 462 | cache.isDragging = false; 463 | cache.startDragX = utils.page('X', e); 464 | } 465 | } 466 | } 467 | }, 468 | init = function(opts) { 469 | if (opts.element) { 470 | utils.deepExtend(settings, opts); 471 | cache.vendor = utils.vendor(); 472 | action.drag.listen(); 473 | } 474 | }; 475 | /* 476 | * Public 477 | */ 478 | this.open = function(side) { 479 | utils.dispatchEvent('open'); 480 | utils.klass.remove(doc.body, 'snapjs-expand-left'); 481 | utils.klass.remove(doc.body, 'snapjs-expand-right'); 482 | 483 | if (side === 'left') { 484 | cache.simpleStates.opening = 'left'; 485 | cache.simpleStates.towards = 'right'; 486 | utils.klass.add(doc.body, 'snapjs-left'); 487 | utils.klass.remove(doc.body, 'snapjs-right'); 488 | action.translate.easeTo(settings.maxPosition); 489 | } else if (side === 'right') { 490 | cache.simpleStates.opening = 'right'; 491 | cache.simpleStates.towards = 'left'; 492 | utils.klass.remove(doc.body, 'snapjs-left'); 493 | utils.klass.add(doc.body, 'snapjs-right'); 494 | action.translate.easeTo(settings.minPosition); 495 | } 496 | }; 497 | this.close = function() { 498 | utils.dispatchEvent('close'); 499 | action.translate.easeTo(0); 500 | }; 501 | this.expand = function(side){ 502 | var to = win.innerWidth || doc.documentElement.clientWidth; 503 | 504 | if(side==='left'){ 505 | utils.dispatchEvent('expandLeft'); 506 | utils.klass.add(doc.body, 'snapjs-expand-left'); 507 | utils.klass.remove(doc.body, 'snapjs-expand-right'); 508 | } else { 509 | utils.dispatchEvent('expandRight'); 510 | utils.klass.add(doc.body, 'snapjs-expand-right'); 511 | utils.klass.remove(doc.body, 'snapjs-expand-left'); 512 | to *= -1; 513 | } 514 | action.translate.easeTo(to); 515 | }; 516 | 517 | this.on = function(evt, fn) { 518 | eventList[evt] = fn; 519 | return this; 520 | }; 521 | this.off = function(evt) { 522 | if (eventList[evt]) { 523 | eventList[evt] = false; 524 | } 525 | }; 526 | 527 | this.enable = function() { 528 | utils.dispatchEvent('enable'); 529 | action.drag.listen(); 530 | }; 531 | this.disable = function() { 532 | utils.dispatchEvent('disable'); 533 | action.drag.stopListening(); 534 | }; 535 | 536 | this.settings = function(opts){ 537 | utils.deepExtend(settings, opts); 538 | }; 539 | 540 | this.state = function() { 541 | var state, 542 | fromLeft = action.translate.get.matrix(4); 543 | if (fromLeft === settings.maxPosition) { 544 | state = 'left'; 545 | } else if (fromLeft === settings.minPosition) { 546 | state = 'right'; 547 | } else { 548 | state = 'closed'; 549 | } 550 | return { 551 | state: state, 552 | info: cache.simpleStates 553 | }; 554 | }; 555 | init(userOpts); 556 | }; 557 | if ((typeof module !== 'undefined') && module.exports) { 558 | module.exports = Snap; 559 | } 560 | if (typeof ender === 'undefined') { 561 | this.Snap = Snap; 562 | } 563 | if ((typeof define === "function") && define.amd) { 564 | define("snap", [], function() { 565 | return Snap; 566 | }); 567 | } 568 | }).call(this, window, document); 569 | -------------------------------------------------------------------------------- /demo/apps/ratchet/ratchet.css: -------------------------------------------------------------------------------- 1 | /** 2 | * ================================== 3 | * Ratchet v1.0.0 4 | * Licensed under The MIT License 5 | * http://opensource.org/licenses/MIT 6 | * ================================== 7 | */ 8 | 9 | /* Hard reset 10 | -------------------------------------------------- */ 11 | 12 | html, 13 | body, 14 | div, 15 | span, 16 | iframe, 17 | h1, 18 | h2, 19 | h3, 20 | h4, 21 | h5, 22 | h6, 23 | p, 24 | blockquote, 25 | pre, 26 | a, 27 | abbr, 28 | acronym, 29 | address, 30 | big, 31 | cite, 32 | code, 33 | del, 34 | dfn, 35 | em, 36 | img, 37 | ins, 38 | kbd, 39 | q, 40 | s, 41 | samp, 42 | small, 43 | strike, 44 | strong, 45 | sub, 46 | sup, 47 | tt, 48 | var, 49 | b, 50 | u, 51 | i, 52 | center, 53 | dl, 54 | dt, 55 | dd, 56 | ol, 57 | ul, 58 | li, 59 | fieldset, 60 | form, 61 | label, 62 | legend, 63 | table, 64 | caption, 65 | tbody, 66 | tfoot, 67 | thead, 68 | tr, 69 | th, 70 | td, 71 | article, 72 | aside, 73 | canvas, 74 | details, 75 | embed, 76 | figure, 77 | figcaption, 78 | footer, 79 | header, 80 | hgroup, 81 | menu, 82 | nav, 83 | output, 84 | section, 85 | summary, 86 | time, 87 | audio, 88 | video { 89 | padding: 0; 90 | margin: 0; 91 | border: 0; 92 | } 93 | 94 | /* Prevents iOS text size adjust after orientation change, without disabling (Thanks to @necolas) */ 95 | html { 96 | -webkit-text-size-adjust: 100%; 97 | -ms-text-size-adjust: 100%; 98 | } 99 | 100 | /* Base styles 101 | -------------------------------------------------- */ 102 | 103 | body { 104 | position: fixed; 105 | top: 0; 106 | right: 0; 107 | bottom: 0; 108 | left: 0; 109 | font: 14px/1.25 "Helvetica Neue", sans-serif; 110 | color: #222; 111 | background-color: #fff; 112 | } 113 | 114 | /* Universal link styling */ 115 | a { 116 | color: #0882f0; 117 | text-decoration: none; 118 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); /* Removes the dark touch outlines on links */ 119 | } 120 | 121 | /* Wrapper to be used around all content not in .bar-title and .bar-tab */ 122 | .content { 123 | position: fixed; 124 | top: 0; 125 | right: 0; 126 | bottom: 0; 127 | left: 0; 128 | overflow: auto; 129 | background: #fff; 130 | -webkit-transition-property: top, bottom; 131 | transition-property: top, bottom; 132 | -webkit-transition-duration: .2s, .2s; 133 | transition-duration: .2s, .2s; 134 | -webkit-transition-timing-function: linear, linear; 135 | transition-timing-function: linear, linear; 136 | -webkit-overflow-scrolling: touch; 137 | } 138 | 139 | /* Hack to force all relatively and absolutely positioned elements still render while scrolling 140 | Note: This is a bug for "-webkit-overflow-scrolling: touch" */ 141 | .content > * { 142 | -webkit-transform: translateZ(0px); 143 | transform: translateZ(0px); 144 | } 145 | 146 | /* Utility wrapper to pad in components like forms, block buttons and segmented-controllers so they're not full-bleed */ 147 | .content-padded { 148 | padding: 10px; 149 | } 150 | 151 | /* Pad top/bottom of content so it doesn't hide behind .bar-title and .bar-tab. 152 | Note: For these to work, content must come after both bars in the markup */ 153 | .bar-title ~ .content { 154 | top: 44px; 155 | } 156 | .bar-tab ~ .content { 157 | bottom: 51px; 158 | } 159 | .bar-header-secondary ~ .content { 160 | top: 88px; 161 | }/* General bar styles 162 | -------------------------------------------------- */ 163 | 164 | [class*="bar-"] { 165 | position: fixed; 166 | right: 0; 167 | left: 0; 168 | z-index: 10; 169 | height: 44px; 170 | padding: 5px; 171 | box-sizing: border-box; 172 | } 173 | 174 | /* Modifier class to dock any bar below .bar-title */ 175 | .bar-header-secondary { 176 | top: 45px; 177 | } 178 | 179 | /* Modifier class to dock any bar to bottom of viewport */ 180 | .bar-footer { 181 | bottom: 0; 182 | } 183 | 184 | /* Generic bar for wrapping buttons, segmented controllers, etc. */ 185 | .bar-standard { 186 | background-color: #f2f2f2; 187 | background-image: -webkit-linear-gradient(top, #f2f2f2 0, #e5e5e5 100%); 188 | background-image: linear-gradient(to bottom, #f2f2f2 0, #e5e5e5 100%); 189 | border-bottom: 1px solid #aaa; 190 | box-shadow: inset 0 1px 1px -1px #fff; 191 | } 192 | 193 | /* Flip border position to top for footer bars */ 194 | .bar-footer.bar-standard, 195 | .bar-footer-secondary.bar-standard { 196 | border-top: 1px solid #aaa; 197 | border-bottom-width: 0; 198 | } 199 | 200 | /* Title bar 201 | -------------------------------------------------- */ 202 | 203 | /* Bar docked to top of viewport for showing page title and actions */ 204 | .bar-title { 205 | top: 0; 206 | display: -webkit-box; 207 | display: box; 208 | background-color: #1eb0e9; 209 | background-image: -webkit-linear-gradient(top, #1eb0e9 0, #109adc 100%); 210 | background-image: linear-gradient(to bottom, #1eb0e9 0, #109adc 100%); 211 | border-bottom: 1px solid #0e5895; 212 | box-shadow: inset 0 1px 1px -1px rgba(255, 255, 255, .8); 213 | -webkit-box-orient: horizontal; 214 | box-orient: horizontal; 215 | } 216 | 217 | /* Centered text in the .bar-title */ 218 | .bar-title .title { 219 | position: absolute; 220 | top: 0; 221 | left: 0; 222 | display: block; 223 | width: 100%; 224 | font-size: 20px; 225 | font-weight: bold; 226 | line-height: 44px; 227 | color: #fff; 228 | text-align: center; 229 | text-shadow: 0 -1px rgba(0, 0, 0, .5); 230 | white-space: nowrap; 231 | } 232 | 233 | .bar-title > a:not([class*="button"]) { 234 | display: block; 235 | width: 100%; 236 | height: 100%; 237 | } 238 | 239 | /* Retain specified title color */ 240 | .bar-title .title a { 241 | color: inherit; 242 | } 243 | 244 | /* Tab bar 245 | -------------------------------------------------- */ 246 | 247 | /* Bar docked to bottom used for primary app navigation */ 248 | .bar-tab { 249 | bottom: 0; 250 | height: 50px; 251 | padding: 0; 252 | background-color: #393939; 253 | background-image: -webkit-linear-gradient(top, #393939 0, #2b2b2b 100%); 254 | background-image: linear-gradient(to bottom, #393939 0, #2b2b2b 100%); 255 | border-top: 1px solid #000; 256 | border-bottom-width: 0; 257 | box-shadow: inset 0 1px 1px -1px rgba(255, 255, 255, .6); 258 | } 259 | 260 | /* Wrapper for individual tab */ 261 | .tab-inner { 262 | display: -webkit-box; 263 | display: box; 264 | height: 100%; 265 | list-style: none; 266 | -webkit-box-orient: horizontal; 267 | box-orient: horizontal; 268 | } 269 | 270 | /* Navigational tab */ 271 | .tab-item { 272 | height: 100%; 273 | padding-top: 9px; 274 | text-align: center; 275 | box-sizing: border-box; 276 | -webkit-box-flex: 1; 277 | box-flex: 1; 278 | } 279 | 280 | /* Active state for tab */ 281 | .tab-item.active { 282 | box-shadow: inset 0 0 20px rgba(0, 0, 0, .5); 283 | } 284 | 285 | /* Icon for tab */ 286 | .tab-icon { 287 | display: block; 288 | height: 18px; 289 | margin: 0 auto; 290 | } 291 | 292 | /* Label for tab */ 293 | .tab-label { 294 | margin-top: 1px; 295 | font-size: 10px; 296 | font-weight: bold; 297 | color: #fff; 298 | text-shadow: 0 1px rgba(0, 0, 0, .3); 299 | } 300 | 301 | /* Buttons in title bars 302 | -------------------------------------------------- */ 303 | 304 | /* Generic style for all buttons in .bar-title */ 305 | .bar-title [class*="button"] { 306 | position: relative; 307 | z-index: 10; /* Places buttons over full width title */ 308 | font-size: 12px; 309 | line-height: 23px; 310 | color: #fff; 311 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .3); 312 | background-color: #1eb0e9; 313 | background-image: -webkit-linear-gradient(top, #1eb0e9 0, #0984c6 100%); 314 | background-image: linear-gradient(to bottom, #1eb0e9 0, #0984c6 100%); 315 | border: 1px solid #0e5895; 316 | box-shadow: 0 1px rgba(255, 255, 255, .25); 317 | -webkit-box-flex: 0; 318 | box-flex: 0; 319 | } 320 | 321 | 322 | /* Hacky way to right align buttons outside of flex-box system 323 | Note: is only absolutely positioned button, would be better if flex-box had an "align right" option */ 324 | .bar-title .title + [class*="button"]:last-child, 325 | .bar-title .button + [class*="button"]:last-child, 326 | .bar-title [class*="button"].pull-right { 327 | position: absolute; 328 | top: 5px; 329 | right: 5px; 330 | } 331 | 332 | /* Override standard button active states */ 333 | .bar-title .button:active { 334 | color: #fff; 335 | background-color: #0876b1; 336 | } 337 | 338 | /* 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/) 339 | -------------------------------------------------- */ 340 | 341 | /* Add relative positioning so :before content is positioned properly */ 342 | .bar-title .button-prev, 343 | .bar-title .button-next { 344 | position: relative; 345 | } 346 | 347 | /* Prev/next button base styles */ 348 | .bar-title .button-prev { 349 | margin-left: 7px; /* Push over to make room for :before content */ 350 | border-left: 0; 351 | border-bottom-left-radius: 10px 15px; 352 | border-top-left-radius: 10px 15px; 353 | } 354 | .bar-title .button-next { 355 | margin-right: 7px; /* Push over to make room for :before content */ 356 | border-right: 0; 357 | border-top-right-radius: 10px 15px; 358 | border-bottom-right-radius: 10px 15px; 359 | } 360 | 361 | /* Pointed part of directional button */ 362 | .bar-title .button-prev:before, 363 | .bar-title .button-next:before { 364 | position: absolute; 365 | top: 2px; 366 | width: 27px; 367 | height: 27px; 368 | border-radius: 30px 100px 2px 40px / 2px 40px 30px 100px; 369 | content: ''; 370 | box-shadow: inset 1px 0 #0e5895, inset 0 1px #0e5895; 371 | -webkit-mask-image: -webkit-gradient(linear, left top, right bottom, from(#000), color-stop(.33, #000), color-stop(.5, transparent), to(transparent)); 372 | mask-image: gradient(linear, left top, right bottom, from(#000), color-stop(.33, #000), color-stop(.5, transparent), to(transparent)); 373 | } 374 | .bar-title .button-prev:before { 375 | left: -5px; 376 | background-image: -webkit-gradient(linear, left bottom, right top, from(#0984c6), to(#1eb0e9)); 377 | background-image: gradient(linear, left bottom, right top, from(#0984c6), to(#1eb0e9)); 378 | border-left: 1.5px solid rgba(255, 255, 255, .25); 379 | -webkit-transform: rotate(-45deg) skew(-10deg, -10deg); 380 | transform: rotate(-45deg) skew(-10deg, -10deg); 381 | } 382 | .bar-title .button-next:before { 383 | right: -5px; 384 | background-image: -webkit-gradient(linear, left bottom, right top, from(#1eb0e9), to(#0984c6)); 385 | background-image: gradient(linear, left bottom, right top, from(#1eb0e9), to(#0984c6)); 386 | border-top: 1.5px solid rgba(255, 255, 255, .25); 387 | -webkit-transform: rotate(135deg) skew(-10deg, -10deg); 388 | transform: rotate(135deg) skew(-10deg, -10deg); 389 | } 390 | 391 | /* Active states for the directional buttons */ 392 | .bar-title .button-prev:active, 393 | .bar-title .button-next:active, 394 | .bar-title .button-prev:active:before, 395 | .bar-title .button-next:active:before { 396 | color: #fff; 397 | background-color: #0876b1; 398 | background-image: none; 399 | } 400 | .bar-title .button-prev:active:before, 401 | .bar-title .button-next:active:before { 402 | content: ''; 403 | } 404 | .bar-title .button-prev:active:before { 405 | box-shadow: inset 0 3px 3px rgba(0, 0, 0, .2); 406 | } 407 | .bar-title .button-next:active:before { 408 | box-shadow: inset 0 -3px 3px rgba(0, 0, 0, .2); 409 | } 410 | 411 | /* Block buttons in any bar 412 | -------------------------------------------------- */ 413 | 414 | /* Add proper padding and replace buttons normal dropshadow with a shine from bar */ 415 | [class*="bar"] .button-block { 416 | padding: 7px 0; 417 | margin-bottom: 0; 418 | box-shadow: inset 0 1px 1px rgba(255, 255, 255, .4), 0 1px rgba(255, 255, 255, .8); 419 | } 420 | 421 | /* Override standard padding changes for .button-blocks */ 422 | [class*="bar"] .button-block:active { 423 | padding: 7px 0; 424 | } 425 | 426 | /* Segmented controller in any bar 427 | -------------------------------------------------- */ 428 | 429 | /* Remove standard segmented bottom margin */ 430 | [class*="bar-"] .segmented-controller { 431 | margin-bottom: 0; 432 | } 433 | 434 | /* Add margins between segmented controllers and buttons */ 435 | [class*="bar-"] .segmented-controller + [class*="button"], 436 | [class*="bar-"] [class*="button"] + .segmented-controller { 437 | margin-left: 5px; 438 | } 439 | 440 | /* Segmented controller in a title bar 441 | -------------------------------------------------- */ 442 | 443 | .bar-title .segmented-controller { 444 | line-height: 18px; 445 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.3); 446 | background-color: #1eb0e9; 447 | background-image: -webkit-linear-gradient(top, #1eb0e9 0, #0984c6 100%); 448 | background-image: linear-gradient(to bottom, #1eb0e9 0, #0984c6 100%); 449 | border: 1px solid #0e5895; 450 | border-radius: 3px; 451 | box-shadow: 0 1px rgba(255, 255, 255, .25); 452 | -webkit-box-flex: 1; 453 | box-flex: 1; 454 | } 455 | 456 | /* Set color for tab border and highlight */ 457 | .bar-title .segmented-controller li { 458 | border-left: 1px solid #0e5895; 459 | box-shadow: inset 1px 0 rgba(255, 255, 255, .25); 460 | } 461 | 462 | /* Remove inset shadow from first tab or one to the right of the active tab */ 463 | .bar-title .segmented-controller .active + li, 464 | .bar-title .segmented-controller li:first-child { 465 | box-shadow: none; 466 | } 467 | 468 | /* Remove left-hand border from first tab */ 469 | .bar-title .segmented-controller li:first-child { 470 | border-left-width: 0; 471 | } 472 | 473 | /* Depressed state (active) */ 474 | .bar-title .segmented-controller li.active { 475 | background-color: #0082c4; 476 | box-shadow: inset 0 1px 6px rgba(0, 0, 0, .3); 477 | } 478 | 479 | /* Set color of links to white */ 480 | .bar-title .segmented-controller li > a { 481 | color: #fff; 482 | } 483 | 484 | 485 | /* Search forms in standard bar 486 | -------------------------------------------------- */ 487 | 488 | /* Position/size search bar within the bar */ 489 | .bar-standard input[type=search] { 490 | height: 32px; 491 | margin: 0; 492 | }/* Lists 493 | -------------------------------------------------- */ 494 | 495 | /* Remove usual bullet styles from list */ 496 | .list { 497 | margin-bottom: 10px; 498 | list-style: none; 499 | background-color: #fff; 500 | } 501 | 502 | /* Pad each list item and add dividers */ 503 | .list li { 504 | position: relative; 505 | padding: 20px 60px 20px 10px; /* Given extra right padding to accomodate counts, chevrons or buttons */ 506 | border-bottom: 1px solid rgba(0, 0, 0, .1); 507 | } 508 | 509 | /* Give top border to first list items */ 510 | .list li:first-child { 511 | border-top: 1px solid rgba(0, 0, 0, .1); 512 | } 513 | 514 | /* If a list of links, make sure the child takes up full list item tap area (want to avoid selecting child buttons though) */ 515 | .list li > a:not([class*="button"]) { 516 | position: relative; 517 | display: block; 518 | padding: inherit; 519 | margin: -20px -60px -20px -10px; 520 | color: inherit; 521 | } 522 | 523 | /* Inset list 524 | -------------------------------------------------- */ 525 | 526 | .list.inset { 527 | width: auto; 528 | margin-right: 10px; 529 | margin-left: 10px; 530 | border: 1px solid rgba(0, 0, 0, .1); 531 | border-radius: 6px; 532 | box-sizing: border-box; 533 | } 534 | 535 | /* Remove border from first/last standard list items to avoid double border at top/bottom of lists */ 536 | .list.inset li:first-child { 537 | border-top-width: 0; 538 | } 539 | .list.inset li:last-child { 540 | border-bottom-width: 0; 541 | } 542 | 543 | 544 | /* List dividers 545 | -------------------------------------------------- */ 546 | 547 | .list .list-divider { 548 | position: relative; 549 | top: -1px; 550 | padding-top: 6px; 551 | padding-bottom: 6px; 552 | font-size: 12px; 553 | font-weight: bold; 554 | line-height: 18px; 555 | text-shadow: 0 1px 0 rgba(255, 255, 255, .5); 556 | background-color: #f8f8f8; 557 | background-image: -webkit-linear-gradient(top, #f8f8f8 0, #eee 100%); 558 | background-image: linear-gradient(to bottom, #f8f8f8 0, #eee 100%); 559 | border-top: 1px solid rgba(0, 0, 0, .1); 560 | border-bottom: 1px solid rgba(0, 0, 0, .1); 561 | box-shadow: inset 0 1px 1px rgba(255, 255, 255, .4); 562 | } 563 | 564 | /* Rounding first divider on inset lists and remove border on the top */ 565 | .list.inset .list-divider:first-child { 566 | top: 0; 567 | border-top-width: 0; 568 | border-radius: 6px 6px 0 0; 569 | } 570 | 571 | /* Rounding last divider on inset lists */ 572 | .list.inset .list-divider:last-child { 573 | border-radius: 0 0 6px 6px; 574 | } 575 | 576 | /* Right-aligned subcontent in lists (chevrons, buttons, counts and toggles) 577 | -------------------------------------------------- */ 578 | .list .chevron, 579 | .list [class*="button"], 580 | .list [class*="count"], 581 | .list .toggle { 582 | position: absolute; 583 | top: 50%; 584 | right: 10px; 585 | } 586 | 587 | /* Position chevrons/counts vertically centered on the right in list items */ 588 | .list .chevron, 589 | .list [class*="count"] { 590 | margin-top: -10px; /* Half height of chevron */ 591 | } 592 | 593 | /* Push count over if there's a sibling chevron */ 594 | .list .chevron + [class*="count"] { 595 | right: 30px; 596 | } 597 | 598 | /* Position buttons vertically centered on the right in list items */ 599 | .list [class*="button"] { 600 | left: auto; 601 | margin-top: -14px; /* Half height of button */ 602 | } 603 | 604 | .list .toggle { 605 | margin-top: -15px; /* Half height of toggle */ 606 | }/* Forms 607 | -------------------------------------------------- */ 608 | 609 | /* Force form elements to inherit font styles */ 610 | input, 611 | textarea, 612 | button, 613 | select { 614 | font-family: inherit; 615 | font-size: inherit; 616 | } 617 | 618 | /* Stretch inputs/textareas to full width and add height to maintain a consistent baseline */ 619 | select, 620 | textarea, 621 | input[type="text"], 622 | input[type=search], 623 | input[type="password"], 624 | input[type="datetime"], 625 | input[type="datetime-local"], 626 | input[type="date"], 627 | input[type="month"], 628 | input[type="time"], 629 | input[type="week"], 630 | input[type="number"], 631 | input[type="email"], 632 | input[type="url"], 633 | input[type="tel"], 634 | input[type="color"], 635 | .input-group { 636 | width: 100%; 637 | height: 40px; 638 | padding: 10px; 639 | margin-bottom: 10px; 640 | background: #fff; 641 | border: 1px solid rgba(0, 0, 0, .2); 642 | border-radius: 3px; 643 | box-shadow: 0 1px 1px rgba(255, 255, 255, .2), inset 0 1px 1px rgba(0, 0, 0, .1); 644 | -webkit-appearance: none; 645 | box-sizing: border-box; 646 | } 647 | 648 | /* Fully round search input */ 649 | input[type=search] { 650 | height: 34px; 651 | font-size: 14px; 652 | border-radius: 30px; 653 | } 654 | 655 | /* Allow text area's height to grow larger than a normal input */ 656 | textarea { 657 | height: auto; 658 | } 659 | 660 | /* Style select button to look like part of the Ratchet's style */ 661 | select { 662 | height: auto; 663 | font-size: 14px; 664 | background-color: #f8f8f8; 665 | background-image: -webkit-linear-gradient(top, #f8f8f8 0%, #d4d4d4 100%); 666 | background-image: linear-gradient(to bottom, #f8f8f8 0%, #d4d4d4 100%); 667 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .1); 668 | } 669 | 670 | 671 | /* Input groups (cluster multiple inputs together into a single group) 672 | -------------------------------------------------- */ 673 | 674 | /* Reset from initial form setup styles */ 675 | .input-group { 676 | width: auto; 677 | height: auto; 678 | padding: 0; 679 | } 680 | 681 | /* Remove spacing, borders, shadows and rounding since it all belongs on the .input-group not the input */ 682 | .input-group input { 683 | margin-bottom: 0; 684 | background-color: transparent; 685 | border: 0; 686 | border-bottom: 1px solid rgba(0, 0, 0, .2); 687 | border-radius: 0; 688 | box-shadow: none; 689 | } 690 | 691 | /* Remove bottom border on last input to avoid double bottom border */ 692 | .input-group input:last-child { 693 | border-bottom-width: 0; 694 | } 695 | 696 | /* Input groups with labels 697 | -------------------------------------------------- */ 698 | 699 | /* To use labels with input groups, wrap a label and an input in an .input-row */ 700 | .input-row { 701 | overflow: hidden; 702 | border-bottom: 1px solid rgba(0, 0, 0, .2); 703 | } 704 | 705 | /* Remove bottom border on last input-row to avoid double bottom border */ 706 | .input-row:last-child { 707 | border-bottom-width: 0; 708 | } 709 | 710 | /* Labels get floated left with a set percentage width */ 711 | .input-row label { 712 | float: left; 713 | width: 25%; 714 | padding: 11px 10px 9px 13px; /* Optimizing the baseline for mobile. */ 715 | font-weight: bold; 716 | } 717 | 718 | /* Actual inputs float to right of labels and also have a set percentage */ 719 | .input-row label + input { 720 | float: right; 721 | width: 65%; 722 | padding-left: 0; 723 | margin-bottom: 0; 724 | border-bottom: 0; 725 | }/* General button styles 726 | -------------------------------------------------- */ 727 | 728 | [class*="button"] { 729 | position: relative; 730 | display: inline-block; 731 | padding: 4px 12px; 732 | margin: 0; 733 | font-weight: bold; 734 | line-height: 18px; 735 | color: #333; 736 | text-align: center; 737 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 738 | vertical-align: top; 739 | cursor: pointer; 740 | background-color: #f8f8f8; 741 | background-image: -webkit-linear-gradient(top, #f8f8f8 0, #d4d4d4 100%); 742 | background-image: linear-gradient(to bottom, #f8f8f8 0, #d4d4d4 100%); 743 | border: 1px solid rgba(0, 0, 0, .3); 744 | border-radius: 3px; 745 | box-shadow: inset 0 1px 1px rgba(255, 255, 255, .4), 0 1px 2px rgba(0, 0, 0, .05); 746 | } 747 | 748 | /* Active */ 749 | [class*="button"]:active { 750 | padding-top: 5px; 751 | padding-bottom: 3px; 752 | color: #333; 753 | background-color: #ccc; 754 | background-image: none; 755 | box-shadow: inset 0 3px 3px rgba(0, 0, 0, .2); 756 | } 757 | 758 | /* Button modifiers 759 | -------------------------------------------------- */ 760 | 761 | /* Overriding styles for buttons with modifiers */ 762 | .button-main, 763 | .button-positive, 764 | .button-negative { 765 | color: #fff; 766 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .3); 767 | } 768 | 769 | /* Main button */ 770 | .button-main { 771 | background-color: #1eafe7; 772 | background-image: -webkit-linear-gradient(top, #1eafe7 0, #1a97c8 100%); 773 | background-image: linear-gradient(to bottom, #1eafe7 0, #1a97c8 100%); 774 | border: 1px solid #117aaa; 775 | } 776 | 777 | /* Positive button */ 778 | .button-positive { 779 | background-color: #34ba15; 780 | background-image: -webkit-linear-gradient(top, #34ba15 0, #2da012 100%); 781 | background-image: linear-gradient(to bottom, #34ba15 0, #2da012 100%); 782 | border: 1px solid #278f0f; 783 | } 784 | 785 | /* Negative button */ 786 | .button-negative { 787 | background-color: #e71e1e; 788 | background-image: -webkit-linear-gradient(top, #e71e1e 0,#c71a1a 100%); 789 | background-image: linear-gradient(to bottom, #e71e1e 0, #c71a1a 100%); 790 | border: 1px solid #b51a1a; 791 | } 792 | 793 | /* Active state for buttons with modifiers */ 794 | .button-main:active, 795 | .button-positive:active, 796 | .button-negative:active { 797 | color: #fff; 798 | } 799 | .button-main:active { 800 | background-color: #0876b1; 801 | } 802 | .button-positive:active { 803 | background-color: #298f11; 804 | } 805 | .button-negative:active { 806 | background-color: #b21a1a; 807 | } 808 | 809 | /* Block level buttons (full width buttons) */ 810 | .button-block { 811 | display: block; 812 | padding: 11px 0 13px; 813 | margin-bottom: 10px; 814 | font-size: 16px; 815 | } 816 | 817 | /* Active state for block level buttons */ 818 | .button-block:active { 819 | padding: 12px 0; 820 | } 821 | 822 | /* Counts in buttons 823 | -------------------------------------------------- */ 824 | 825 | /* Generic styles for all counts within buttons */ 826 | [class*="button"] [class*="count"] { 827 | padding-top: 2px; 828 | padding-bottom: 2px; 829 | margin-right: -4px; 830 | margin-left: 4px; 831 | text-shadow: none; 832 | background-color: rgba(0, 0, 0, .2); 833 | box-shadow: inset 0 1px 1px -1px #000000, 0 1px 1px -1px #fff; 834 | } 835 | 836 | /* Position counts within block level buttons 837 | Note: These are absolutely positioned so that text of button isn't "pushed" by count and always 838 | stays at true center of button */ 839 | .button-block [class*="count"] { 840 | position: absolute; 841 | right: 0; 842 | padding-top: 4px; 843 | padding-bottom: 4px; 844 | margin-right: 10px; 845 | }/* Chevrons 846 | -------------------------------------------------- */ 847 | 848 | .chevron { 849 | display: block; 850 | height: 20px; 851 | } 852 | 853 | /* Base styles for both 1/2's of the chevron */ 854 | .chevron:before, 855 | .chevron:after { 856 | position: relative; 857 | display: block; 858 | width: 12px; 859 | height: 4px; 860 | background-color: #999; 861 | content: ''; 862 | } 863 | 864 | /* Position and rotate respective 1/2's of the chevron */ 865 | .chevron:before { 866 | top: 5px; 867 | -webkit-transform: rotate(45deg); 868 | transform: rotate(45deg); 869 | } 870 | .chevron:after { 871 | top: 7px; 872 | -webkit-transform: rotate(-45deg); 873 | transform: rotate(-45deg); 874 | }/* General count styles 875 | -------------------------------------------------- */ 876 | 877 | [class*="count"] { 878 | display: inline-block; 879 | padding: 4px 9px; 880 | font-size: 12px; 881 | font-weight: bold; 882 | line-height: 13px; 883 | color: #fff; 884 | background-color: rgba(0, 0, 0, .3); 885 | border-radius: 100px; 886 | } 887 | 888 | /* Count modifiers 889 | -------------------------------------------------- */ 890 | 891 | /* Overriding styles for counts with modifiers */ 892 | .count-main, 893 | .count-positive, 894 | .count-negative { 895 | color: #fff; 896 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .3); 897 | } 898 | 899 | /* Main count */ 900 | .count-main { 901 | background-color: #1eafe7; 902 | background-image: -webkit-linear-gradient(top, #1eafe7 0, #1a97c8 100%); 903 | background-image: linear-gradient(to bottom, #1eafe7 0, #1a97c8 100%); 904 | } 905 | 906 | /* Positive count */ 907 | .count-positive { 908 | background-color: #34ba15; 909 | background-image: -webkit-linear-gradient(top, #34ba15 0, #2da012 100%); 910 | background-image: linear-gradient(to bottom, #34ba15 0, #2da012 100%); 911 | } 912 | 913 | /* Negative count */ 914 | .count-negative { 915 | background-color: #e71e1e; 916 | background-image: -webkit-linear-gradient(top, #e71e1e 0,#c71a1a 100%); 917 | background-image: linear-gradient(to bottom, #e71e1e 0, #c71a1a 100%); 918 | }/* Segmented controllers 919 | -------------------------------------------------- */ 920 | 921 | .segmented-controller { 922 | display: -webkit-box; 923 | display: box; 924 | margin-bottom: 10px; 925 | overflow: hidden; 926 | font-size: 12px; 927 | font-weight: bold; 928 | text-shadow: 0 1px rgba(255, 255, 255, .5); 929 | list-style: none; 930 | background-color: #f8f8f8; 931 | background-image: -webkit-linear-gradient(top, #f8f8f8 0, #d4d4d4 100%); 932 | background-image: linear-gradient(to bottom, #f8f8f8 0, #d4d4d4 100%); 933 | border: 1px solid #aaa; 934 | border-radius: 3px; 935 | box-shadow: inset 0 1px rgba(255, 255, 255, 0.5), 0 1px rgba(255, 255, 255, .8); 936 | -webkit-box-orient: horizontal; 937 | box-orient: horizontal; 938 | } 939 | 940 | /* Section within controller */ 941 | .segmented-controller li { 942 | overflow: hidden; 943 | text-align: center; 944 | white-space: nowrap; 945 | border-left: 1px solid #aaa; 946 | box-shadow: inset 1px 0 rgba(255, 255, 255, .5); 947 | -webkit-box-flex: 1; 948 | box-flex: 1; 949 | } 950 | 951 | /* Link that fills each section */ 952 | .segmented-controller li > a { 953 | display: block; 954 | padding: 8px 16px; 955 | overflow: hidden; 956 | line-height: 15px; 957 | color: #333; 958 | text-overflow: ellipsis; 959 | } 960 | 961 | /* Remove border-left and shadow from first section */ 962 | .segmented-controller li:first-child { 963 | border-left-width: 0; 964 | box-shadow: none; 965 | } 966 | 967 | /* Active segment of controller */ 968 | .segmented-controller li.active { 969 | background-color: #ccc; 970 | box-shadow: inset 0 1px 5px rgba(0, 0, 0, .3); 971 | } 972 | 973 | .segmented-controller-item { 974 | display: none; 975 | } 976 | 977 | .segmented-controller-item.active { 978 | display: block; 979 | }/* Popovers (to be used with popovers.js) 980 | -------------------------------------------------- */ 981 | 982 | .popover { 983 | position: fixed; 984 | top: 55px; 985 | left: 50%; 986 | z-index: 20; 987 | display: none; 988 | width: 280px; 989 | padding: 5px; 990 | margin-left: -146px; 991 | background-color: #555; 992 | background-image: -webkit-linear-gradient(top, #555 5%, #555 6%, #111 30%); 993 | background-image: linear-gradient(to bottom, #555 5%, #555 6%,#111 30%); 994 | border: 1px solid #111; 995 | border-radius: 6px; 996 | opacity: 0; 997 | box-shadow: inset 0 1px 1px -1px #fff, 0 3px 10px rgba(0, 0, 0, .3); 998 | -webkit-transform: translate3d(0, -15px, 0); 999 | transform: translate3d(0, -15px, 0); 1000 | -webkit-transition: -webkit-transform 0.2s ease-in-out, opacity 0.2s ease-in-out; 1001 | transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out; 1002 | } 1003 | 1004 | /* Caret on top of popover using CSS triangles (thanks to @chriscoyier for solution) */ 1005 | .popover:before, 1006 | .popover:after { 1007 | position: absolute; 1008 | left: 50%; 1009 | width: 0; 1010 | height: 0; 1011 | content: ''; 1012 | } 1013 | .popover:before { 1014 | top: -20px; 1015 | margin-left: -21px; 1016 | border-right: 21px solid transparent; 1017 | border-bottom: 21px solid #111; 1018 | border-left: 21px solid transparent; 1019 | } 1020 | .popover:after { 1021 | top: -19px; 1022 | margin-left: -20px; 1023 | border-right: 20px solid transparent; 1024 | border-bottom: 20px solid #555; 1025 | border-left: 20px solid transparent; 1026 | } 1027 | 1028 | /* Wrapper for a title and buttons */ 1029 | .popover-header { 1030 | display: -webkit-box; 1031 | display: box; 1032 | height: 34px; 1033 | margin-bottom: 5px; 1034 | } 1035 | 1036 | /* Centered title for popover */ 1037 | .popover-header .title { 1038 | position: absolute; 1039 | top: 0; 1040 | left: 0; 1041 | width: 100%; 1042 | margin: 15px 0; 1043 | font-size: 16px; 1044 | font-weight: bold; 1045 | line-height: 12px; 1046 | color: #fff; 1047 | text-align: center; 1048 | text-shadow: 0 -1px rgba(0, 0, 0, .5); 1049 | } 1050 | 1051 | /* Generic style for all buttons in .popover-header */ 1052 | .popover-header [class*="button"] { 1053 | z-index: 25; 1054 | font-size: 12px; 1055 | line-height: 22px; 1056 | color: #fff; 1057 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .3); 1058 | background-color: #454545; 1059 | background-image: -webkit-linear-gradient(top, #454545 0, #353535 100%); 1060 | background-image: linear-gradient(to bottom, #454545 0, #353535 100%); 1061 | border: 1px solid #111; 1062 | -webkit-box-flex: 0; 1063 | box-flex: 0; 1064 | } 1065 | 1066 | /* Hacky way to right align buttons outside of flex-box system 1067 | Note: is only absolutely positioned button, would be better if flex-box had an "align right" option */ 1068 | .popover-header .title + [class*="button"]:last-child, 1069 | .popover-header .button + [class*="button"]:last-child, 1070 | .popover-header [class*="button"].pull-right { 1071 | position: absolute; 1072 | top: 5px; 1073 | right: 5px; 1074 | } 1075 | 1076 | /* Active state for popover header buttons */ 1077 | .popover-header .button:active { 1078 | color: #fff; 1079 | background-color: #0876b1; 1080 | } 1081 | 1082 | /* Popover animation 1083 | -------------------------------------------------- */ 1084 | 1085 | .popover.visible { 1086 | opacity: 1; 1087 | -webkit-transform: translate3d(0, 0, 0); 1088 | transform: translate3d(0, 0, 0); 1089 | } 1090 | 1091 | /* Backdrop (used as invisible touch escape) 1092 | -------------------------------------------------- */ 1093 | 1094 | .backdrop { 1095 | position: fixed; 1096 | top: 0; 1097 | right: 0; 1098 | bottom: 0; 1099 | left: 0; 1100 | z-index: 10; 1101 | } 1102 | 1103 | /* Block level buttons in popovers 1104 | -------------------------------------------------- */ 1105 | 1106 | /* Positioning and giving darker border to look sharp against dark popover */ 1107 | .popover .button-block { 1108 | margin-bottom: 5px; 1109 | border: 1px solid #111; 1110 | } 1111 | 1112 | /* Remove extra margin on bottom of last button */ 1113 | .popover .button-block:last-child { 1114 | margin-bottom: 0; 1115 | } 1116 | 1117 | /* Lists in popovers 1118 | -------------------------------------------------- */ 1119 | 1120 | .popover .list { 1121 | width: auto; 1122 | max-height: 250px; 1123 | margin-right: 0; 1124 | margin-bottom: 0; 1125 | margin-left: 0; 1126 | overflow: auto; 1127 | background-color: #fff; 1128 | border: 1px solid #000; 1129 | border-radius: 3px; 1130 | -webkit-overflow-scrolling: touch; 1131 | }/* Slider styles (to be used with sliders.js) 1132 | -------------------------------------------------- */ 1133 | 1134 | /* Width/height of slider */ 1135 | .slider, 1136 | .slider > li { 1137 | width: 100%; 1138 | height: 200px; 1139 | } 1140 | 1141 | /* Outer wrapper for slider */ 1142 | .slider { 1143 | overflow: hidden; 1144 | background-color: #000; 1145 | } 1146 | 1147 | /* Inner wrapper for slider (width of all slides together) */ 1148 | .slider > ul { 1149 | position: relative; 1150 | font-size: 0; /* Remove spaces from inline-block children */ 1151 | white-space: nowrap; 1152 | -webkit-transition: all 0 linear; 1153 | transition: all 0 linear; 1154 | } 1155 | 1156 | /* Individual slide */ 1157 | .slider > ul > li { 1158 | display: inline-block; 1159 | vertical-align: top; /* Ensure that li always aligns to top */ 1160 | width: 100%; 1161 | height: 100%; 1162 | } 1163 | 1164 | /* Required reset of font-size to same as standard body */ 1165 | .slider > ul > li > * { 1166 | font-size: 14px; 1167 | }/* Toggle styles (to be used with toggles.js) 1168 | -------------------------------------------------- */ 1169 | 1170 | .toggle { 1171 | position: relative; 1172 | width: 75px; 1173 | height: 28px; 1174 | background-color: #eee; 1175 | border: 1px solid #bbb; 1176 | border-radius: 20px; 1177 | box-shadow: inset 0 0 4px rgba(0, 0, 0, .1); 1178 | } 1179 | 1180 | /* Text indicating "on" or "off". Default is "off" */ 1181 | .toggle:before { 1182 | position: absolute; 1183 | right: 13px; 1184 | font-weight: bold; 1185 | line-height: 28px; 1186 | color: #777; 1187 | text-shadow: 0 1px #fff; 1188 | text-transform: uppercase; 1189 | content: "Off"; 1190 | } 1191 | 1192 | /* Sliding handle */ 1193 | .toggle-handle { 1194 | position: absolute; 1195 | top: -1px; 1196 | left: -1px; 1197 | z-index: 2; 1198 | width: 28px; 1199 | height: 28px; 1200 | background-color: #fff; 1201 | background-image: -webkit-linear-gradient(top, #fff 0, #f2f2f2 100%); 1202 | background-image: linear-gradient(to bottom, #fff 0, #f2f2f2 100%); 1203 | border: 1px solid rgba(0, 0, 0, .2); 1204 | border-radius: 100px; 1205 | -webkit-transition: -webkit-transform 0.1s ease-in-out, border 0.1s ease-in-out; 1206 | transition: transform 0.1s ease-in-out, border 0.1s ease-in-out; 1207 | } 1208 | 1209 | /* Active state for toggle */ 1210 | .toggle.active { 1211 | background-color: #19a8e4; 1212 | background-image: -webkit-linear-gradient(top, #088cd4 0, #19a8e4 100%); 1213 | background-image: linear-gradient(to bottom, #088cd4 0, #19a8e4 100%); 1214 | border: 1px solid #096c9d; 1215 | box-shadow: inset 0 0 15px rgba(255, 255, 255, .25); 1216 | } 1217 | 1218 | /* Active state for toggle handle */ 1219 | .toggle.active .toggle-handle { 1220 | border-color: #0a76ad; 1221 | -webkit-transform: translate3d(48px,0,0); 1222 | transform: translate3d(48px,0,0); 1223 | } 1224 | 1225 | /* Change "off" to "on" for active state */ 1226 | .toggle.active:before { 1227 | right: auto; 1228 | left: 15px; 1229 | color: #fff; 1230 | text-shadow: 0 -1px rgba(0, 0, 0, 0.25); 1231 | content: "On"; 1232 | }/* Push styles (to be used with push.js) 1233 | -------------------------------------------------- */ 1234 | 1235 | /* Fade animation */ 1236 | .content.fade { 1237 | left: 0; 1238 | opacity: 0; 1239 | -webkit-transition: opacity .2s ease-in-out; 1240 | transition: opacity .2s ease-in-out; 1241 | } 1242 | .content.fade.in { 1243 | opacity: 1; 1244 | } 1245 | 1246 | /* Slide animation */ 1247 | .content.slide { 1248 | -webkit-transform: translate3d(0, 0, 0); 1249 | transform: translate3d(0, 0, 0); 1250 | -webkit-transition: -webkit-transform .25s ease-in-out; 1251 | transition: transform .25s ease-in-out; 1252 | } 1253 | .content.slide.left { 1254 | -webkit-transform: translate3d(-100%, 0, 0); 1255 | transform: translate3d(-100%, 0, 0); 1256 | } 1257 | .content.slide.right { 1258 | -webkit-transform: translate3d(100%, 0, 0); 1259 | transform: translate3d(100%, 0, 0); 1260 | } --------------------------------------------------------------------------------