diff options
author | Adam Spiers <git@adamspiers.org> | 2016-06-11 22:20:04 +0100 |
---|---|---|
committer | Adam Spiers <git@adamspiers.org> | 2018-05-15 13:42:16 +0100 |
commit | 2c9d23b0291157eb1096384ff76e0122747b9bdf (patch) | |
tree | 524c7b479b65a478c998c28475d52e636b919200 /git_deps/html | |
parent | 9a741f07167dcb6cc81a8f87036d1ea75c4270d3 (diff) | |
download | git-deps-2c9d23b0291157eb1096384ff76e0122747b9bdf.tar.gz |
convert into a proper Python module
Sem-Ver: api-break
Diffstat (limited to 'git_deps/html')
-rw-r--r-- | git_deps/html/.gitignore | 2 | ||||
-rw-r--r-- | git_deps/html/css/animate.css | 3158 | ||||
-rw-r--r-- | git_deps/html/css/git-deps-tips.css | 79 | ||||
-rw-r--r-- | git_deps/html/css/git-deps.css | 93 | ||||
-rw-r--r-- | git_deps/html/git-deps.html | 40 | ||||
-rw-r--r-- | git_deps/html/js/.gitignore | 1 | ||||
-rw-r--r-- | git_deps/html/js/fullscreen.js | 48 | ||||
-rw-r--r-- | git_deps/html/js/git-deps-data.coffee | 108 | ||||
-rw-r--r-- | git_deps/html/js/git-deps-graph.coffee | 595 | ||||
-rw-r--r-- | git_deps/html/js/git-deps-layout.coffee | 253 | ||||
-rw-r--r-- | git_deps/html/js/git-deps-noty.coffee | 32 | ||||
-rw-r--r-- | git_deps/html/package.json | 54 | ||||
-rw-r--r-- | git_deps/html/test.json | 442 | ||||
-rw-r--r-- | git_deps/html/tip-template.html | 11 |
14 files changed, 4916 insertions, 0 deletions
diff --git a/git_deps/html/.gitignore b/git_deps/html/.gitignore new file mode 100644 index 0000000..68b9e27 --- /dev/null +++ b/git_deps/html/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +bower_components/ diff --git a/git_deps/html/css/animate.css b/git_deps/html/css/animate.css new file mode 100644 index 0000000..f784ce8 --- /dev/null +++ b/git_deps/html/css/animate.css @@ -0,0 +1,3158 @@ +@charset "UTF-8"; +/*! +Animate.css - http://daneden.me/animate +Licensed under the MIT license - http://opensource.org/licenses/MIT + +Copyright (c) 2014 Daniel Eden +*/ + +.animated { + -webkit-animation-duration: 1s; + animation-duration: 1s; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; +} + +.animated.infinite { + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; +} + +.animated.hinge { + -webkit-animation-duration: 2s; + animation-duration: 2s; +} + +@-webkit-keyframes bounce { + 0%, 20%, 53%, 80%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + -webkit-transform: translate3d(0,0,0); + transform: translate3d(0,0,0); + } + + 40%, 43% { + -webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -30px, 0); + transform: translate3d(0, -30px, 0); + } + + 70% { + -webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -15px, 0); + transform: translate3d(0, -15px, 0); + } + + 90% { + -webkit-transform: translate3d(0,-4px,0); + transform: translate3d(0,-4px,0); + } +} + +@keyframes bounce { + 0%, 20%, 53%, 80%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + -webkit-transform: translate3d(0,0,0); + transform: translate3d(0,0,0); + } + + 40%, 43% { + -webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -30px, 0); + transform: translate3d(0, -30px, 0); + } + + 70% { + -webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -15px, 0); + transform: translate3d(0, -15px, 0); + } + + 90% { + -webkit-transform: translate3d(0,-4px,0); + transform: translate3d(0,-4px,0); + } +} + +.bounce { + -webkit-animation-name: bounce; + animation-name: bounce; + -webkit-transform-origin: center bottom; + -ms-transform-origin: center bottom; + transform-origin: center bottom; +} + +@-webkit-keyframes flash { + 0%, 50%, 100% { + opacity: 1; + } + + 25%, 75% { + opacity: 0; + } +} + +@keyframes flash { + 0%, 50%, 100% { + opacity: 1; + } + + 25%, 75% { + opacity: 0; + } +} + +.flash { + -webkit-animation-name: flash; + animation-name: flash; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes pulse { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 50% { + -webkit-transform: scale3d(1.05, 1.05, 1.05); + transform: scale3d(1.05, 1.05, 1.05); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes pulse { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 50% { + -webkit-transform: scale3d(1.05, 1.05, 1.05); + transform: scale3d(1.05, 1.05, 1.05); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.pulse { + -webkit-animation-name: pulse; + animation-name: pulse; +} + +@-webkit-keyframes rubberBand { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 30% { + -webkit-transform: scale3d(1.25, 0.75, 1); + transform: scale3d(1.25, 0.75, 1); + } + + 40% { + -webkit-transform: scale3d(0.75, 1.25, 1); + transform: scale3d(0.75, 1.25, 1); + } + + 50% { + -webkit-transform: scale3d(1.15, 0.85, 1); + transform: scale3d(1.15, 0.85, 1); + } + + 65% { + -webkit-transform: scale3d(.95, 1.05, 1); + transform: scale3d(.95, 1.05, 1); + } + + 75% { + -webkit-transform: scale3d(1.05, .95, 1); + transform: scale3d(1.05, .95, 1); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes rubberBand { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 30% { + -webkit-transform: scale3d(1.25, 0.75, 1); + transform: scale3d(1.25, 0.75, 1); + } + + 40% { + -webkit-transform: scale3d(0.75, 1.25, 1); + transform: scale3d(0.75, 1.25, 1); + } + + 50% { + -webkit-transform: scale3d(1.15, 0.85, 1); + transform: scale3d(1.15, 0.85, 1); + } + + 65% { + -webkit-transform: scale3d(.95, 1.05, 1); + transform: scale3d(.95, 1.05, 1); + } + + 75% { + -webkit-transform: scale3d(1.05, .95, 1); + transform: scale3d(1.05, .95, 1); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.rubberBand { + -webkit-animation-name: rubberBand; + animation-name: rubberBand; +} + +@-webkit-keyframes shake { + 0%, 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 10%, 30%, 50%, 70%, 90% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 20%, 40%, 60%, 80% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } +} + +@keyframes shake { + 0%, 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 10%, 30%, 50%, 70%, 90% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 20%, 40%, 60%, 80% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } +} + +.shake { + -webkit-animation-name: shake; + animation-name: shake; +} + +@-webkit-keyframes swing { + 20% { + -webkit-transform: rotate3d(0, 0, 1, 15deg); + transform: rotate3d(0, 0, 1, 15deg); + } + + 40% { + -webkit-transform: rotate3d(0, 0, 1, -10deg); + transform: rotate3d(0, 0, 1, -10deg); + } + + 60% { + -webkit-transform: rotate3d(0, 0, 1, 5deg); + transform: rotate3d(0, 0, 1, 5deg); + } + + 80% { + -webkit-transform: rotate3d(0, 0, 1, -5deg); + transform: rotate3d(0, 0, 1, -5deg); + } + + 100% { + -webkit-transform: rotate3d(0, 0, 1, 0deg); + transform: rotate3d(0, 0, 1, 0deg); + } +} + +@keyframes swing { + 20% { + -webkit-transform: rotate3d(0, 0, 1, 15deg); + transform: rotate3d(0, 0, 1, 15deg); + } + + 40% { + -webkit-transform: rotate3d(0, 0, 1, -10deg); + transform: rotate3d(0, 0, 1, -10deg); + } + + 60% { + -webkit-transform: rotate3d(0, 0, 1, 5deg); + transform: rotate3d(0, 0, 1, 5deg); + } + + 80% { + -webkit-transform: rotate3d(0, 0, 1, -5deg); + transform: rotate3d(0, 0, 1, -5deg); + } + + 100% { + -webkit-transform: rotate3d(0, 0, 1, 0deg); + transform: rotate3d(0, 0, 1, 0deg); + } +} + +.swing { + -webkit-transform-origin: top center; + -ms-transform-origin: top center; + transform-origin: top center; + -webkit-animation-name: swing; + animation-name: swing; +} + +@-webkit-keyframes tada { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 10%, 20% { + -webkit-transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); + transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); + } + + 30%, 50%, 70%, 90% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 40%, 60%, 80% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes tada { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 10%, 20% { + -webkit-transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); + transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); + } + + 30%, 50%, 70%, 90% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 40%, 60%, 80% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.tada { + -webkit-animation-name: tada; + animation-name: tada; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes wobble { + 0% { + -webkit-transform: none; + transform: none; + } + + 15% { + -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + } + + 30% { + -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + } + + 45% { + -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + } + + 60% { + -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + } + + 75% { + -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +@keyframes wobble { + 0% { + -webkit-transform: none; + transform: none; + } + + 15% { + -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + } + + 30% { + -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + } + + 45% { + -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + } + + 60% { + -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + } + + 75% { + -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +.wobble { + -webkit-animation-name: wobble; + animation-name: wobble; +} + +@-webkit-keyframes bounceIn { + 0%, 20%, 40%, 60%, 80%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 20% { + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + 40% { + -webkit-transform: scale3d(.9, .9, .9); + transform: scale3d(.9, .9, .9); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(1.03, 1.03, 1.03); + transform: scale3d(1.03, 1.03, 1.03); + } + + 80% { + -webkit-transform: scale3d(.97, .97, .97); + transform: scale3d(.97, .97, .97); + } + + 100% { + opacity: 1; + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes bounceIn { + 0%, 20%, 40%, 60%, 80%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 20% { + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + 40% { + -webkit-transform: scale3d(.9, .9, .9); + transform: scale3d(.9, .9, .9); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(1.03, 1.03, 1.03); + transform: scale3d(1.03, 1.03, 1.03); + } + + 80% { + -webkit-transform: scale3d(.97, .97, .97); + transform: scale3d(.97, .97, .97); + } + + 100% { + opacity: 1; + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.bounceIn { + -webkit-animation-name: bounceIn; + animation-name: bounceIn; + -webkit-animation-duration: .75s; + animation-duration: .75s; +} + +@-webkit-keyframes bounceInDown { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -3000px, 0); + transform: translate3d(0, -3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, 25px, 0); + transform: translate3d(0, 25px, 0); + } + + 75% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, 5px, 0); + transform: translate3d(0, 5px, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +@keyframes bounceInDown { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -3000px, 0); + transform: translate3d(0, -3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, 25px, 0); + transform: translate3d(0, 25px, 0); + } + + 75% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, 5px, 0); + transform: translate3d(0, 5px, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +.bounceInDown { + -webkit-animation-name: bounceInDown; + animation-name: bounceInDown; +} + +@-webkit-keyframes bounceInLeft { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(-3000px, 0, 0); + transform: translate3d(-3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(25px, 0, 0); + transform: translate3d(25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(5px, 0, 0); + transform: translate3d(5px, 0, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +@keyframes bounceInLeft { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(-3000px, 0, 0); + transform: translate3d(-3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(25px, 0, 0); + transform: translate3d(25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(5px, 0, 0); + transform: translate3d(5px, 0, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +.bounceInLeft { + -webkit-animation-name: bounceInLeft; + animation-name: bounceInLeft; +} + +@-webkit-keyframes bounceInRight { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(3000px, 0, 0); + transform: translate3d(3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(-25px, 0, 0); + transform: translate3d(-25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(-5px, 0, 0); + transform: translate3d(-5px, 0, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +@keyframes bounceInRight { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(3000px, 0, 0); + transform: translate3d(3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(-25px, 0, 0); + transform: translate3d(-25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(-5px, 0, 0); + transform: translate3d(-5px, 0, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +.bounceInRight { + -webkit-animation-name: bounceInRight; + animation-name: bounceInRight; +} + +@-webkit-keyframes bounceInUp { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(0, 3000px, 0); + transform: translate3d(0, 3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + 75% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, -5px, 0); + transform: translate3d(0, -5px, 0); + } + + 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes bounceInUp { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(0, 3000px, 0); + transform: translate3d(0, 3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + 75% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, -5px, 0); + transform: translate3d(0, -5px, 0); + } + + 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.bounceInUp { + -webkit-animation-name: bounceInUp; + animation-name: bounceInUp; +} + +@-webkit-keyframes bounceOut { + 20% { + -webkit-transform: scale3d(.9, .9, .9); + transform: scale3d(.9, .9, .9); + } + + 50%, 55% { + opacity: 1; + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } +} + +@keyframes bounceOut { + 20% { + -webkit-transform: scale3d(.9, .9, .9); + transform: scale3d(.9, .9, .9); + } + + 50%, 55% { + opacity: 1; + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } +} + +.bounceOut { + -webkit-animation-name: bounceOut; + animation-name: bounceOut; + -webkit-animation-duration: .75s; + animation-duration: .75s; +} + +@-webkit-keyframes bounceOutDown { + 20% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 40%, 45% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +@keyframes bounceOutDown { + 20% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 40%, 45% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +.bounceOutDown { + -webkit-animation-name: bounceOutDown; + animation-name: bounceOutDown; +} + +@-webkit-keyframes bounceOutLeft { + 20% { + opacity: 1; + -webkit-transform: translate3d(20px, 0, 0); + transform: translate3d(20px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +@keyframes bounceOutLeft { + 20% { + opacity: 1; + -webkit-transform: translate3d(20px, 0, 0); + transform: translate3d(20px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +.bounceOutLeft { + -webkit-animation-name: bounceOutLeft; + animation-name: bounceOutLeft; +} + +@-webkit-keyframes bounceOutRight { + 20% { + opacity: 1; + -webkit-transform: translate3d(-20px, 0, 0); + transform: translate3d(-20px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +@keyframes bounceOutRight { + 20% { + opacity: 1; + -webkit-transform: translate3d(-20px, 0, 0); + transform: translate3d(-20px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +.bounceOutRight { + -webkit-animation-name: bounceOutRight; + animation-name: bounceOutRight; +} + +@-webkit-keyframes bounceOutUp { + 20% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 40%, 45% { + opacity: 1; + -webkit-transform: translate3d(0, 20px, 0); + transform: translate3d(0, 20px, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +@keyframes bounceOutUp { + 20% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 40%, 45% { + opacity: 1; + -webkit-transform: translate3d(0, 20px, 0); + transform: translate3d(0, 20px, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +.bounceOutUp { + -webkit-animation-name: bounceOutUp; + animation-name: bounceOutUp; +} + +@-webkit-keyframes fadeIn { + 0% {opacity: 0;} + 100% {opacity: 1;} +} + +@keyframes fadeIn { + 0% {opacity: 0;} + 100% {opacity: 1;} +} + +.fadeIn { + -webkit-animation-name: fadeIn; + animation-name: fadeIn; +} + +@-webkit-keyframes fadeInDown { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInDown { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInDown { + -webkit-animation-name: fadeInDown; + animation-name: fadeInDown; +} + +@-webkit-keyframes fadeInDownBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInDownBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInDownBig { + -webkit-animation-name: fadeInDownBig; + animation-name: fadeInDownBig; +} + +@-webkit-keyframes fadeInLeft { + 0% { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInLeft { + 0% { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInLeft { + -webkit-animation-name: fadeInLeft; + animation-name: fadeInLeft; +} + +@-webkit-keyframes fadeInLeftBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInLeftBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInLeftBig { + -webkit-animation-name: fadeInLeftBig; + animation-name: fadeInLeftBig; +} + +@-webkit-keyframes fadeInRight { + 0% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInRight { + 0% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInRight { + -webkit-animation-name: fadeInRight; + animation-name: fadeInRight; +} + +@-webkit-keyframes fadeInRightBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInRightBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInRightBig { + -webkit-animation-name: fadeInRightBig; + animation-name: fadeInRightBig; +} + +@-webkit-keyframes fadeInUp { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInUp { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInUp { + -webkit-animation-name: fadeInUp; + animation-name: fadeInUp; +} + +@-webkit-keyframes fadeInUpBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInUpBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInUpBig { + -webkit-animation-name: fadeInUpBig; + animation-name: fadeInUpBig; +} + +@-webkit-keyframes fadeOut { + 0% {opacity: 1;} + 100% {opacity: 0;} +} + +@keyframes fadeOut { + 0% {opacity: 1;} + 100% {opacity: 0;} +} + +.fadeOut { + -webkit-animation-name: fadeOut; + animation-name: fadeOut; +} + +@-webkit-keyframes fadeOutDown { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } +} + +@keyframes fadeOutDown { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } +} + +.fadeOutDown { + -webkit-animation-name: fadeOutDown; + animation-name: fadeOutDown; +} + +@-webkit-keyframes fadeOutDownBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +@keyframes fadeOutDownBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +.fadeOutDownBig { + -webkit-animation-name: fadeOutDownBig; + animation-name: fadeOutDownBig; +} + +@-webkit-keyframes fadeOutLeft { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } +} + +@keyframes fadeOutLeft { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } +} + +.fadeOutLeft { + -webkit-animation-name: fadeOutLeft; + animation-name: fadeOutLeft; +} + +@-webkit-keyframes fadeOutLeftBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +@keyframes fadeOutLeftBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +.fadeOutLeftBig { + -webkit-animation-name: fadeOutLeftBig; + animation-name: fadeOutLeftBig; +} + +@-webkit-keyframes fadeOutRight { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } +} + +@keyframes fadeOutRight { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } +} + +.fadeOutRight { + -webkit-animation-name: fadeOutRight; + animation-name: fadeOutRight; +} + +@-webkit-keyframes fadeOutRightBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +@keyframes fadeOutRightBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +.fadeOutRightBig { + -webkit-animation-name: fadeOutRightBig; + animation-name: fadeOutRightBig; +} + +@-webkit-keyframes fadeOutUp { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } +} + +@keyframes fadeOutUp { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } +} + +.fadeOutUp { + -webkit-animation-name: fadeOutUp; + animation-name: fadeOutUp; +} + +@-webkit-keyframes fadeOutUpBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +@keyframes fadeOutUpBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +.fadeOutUpBig { + -webkit-animation-name: fadeOutUpBig; + animation-name: fadeOutUpBig; +} + +@-webkit-keyframes flip { + 0% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg); + transform: perspective(400px) rotate3d(0, 1, 0, -360deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 40% { + -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); + transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 50% { + -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); + transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 80% { + -webkit-transform: perspective(400px) scale3d(.95, .95, .95); + transform: perspective(400px) scale3d(.95, .95, .95); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } +} + +@keyframes flip { + 0% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg); + transform: perspective(400px) rotate3d(0, 1, 0, -360deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 40% { + -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); + transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 50% { + -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); + transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 80% { + -webkit-transform: perspective(400px) scale3d(.95, .95, .95); + transform: perspective(400px) scale3d(.95, .95, .95); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } +} + +.animated.flip { + -webkit-backface-visibility: visible; + backface-visibility: visible; + -webkit-animation-name: flip; + animation-name: flip; +} + +@-webkit-keyframes flipInX { + 0% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +@keyframes flipInX { + 0% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +.flipInX { + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; + -webkit-animation-name: flipInX; + animation-name: flipInX; +} + +@-webkit-keyframes flipInY { + 0% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +@keyframes flipInY { + 0% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +.flipInY { + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; + -webkit-animation-name: flipInY; + animation-name: flipInY; +} + +@-webkit-keyframes flipOutX { + 0% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + opacity: 1; + } + + 100% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + opacity: 0; + } +} + +@keyframes flipOutX { + 0% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + opacity: 1; + } + + 100% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + opacity: 0; + } +} + +.flipOutX { + -webkit-animation-name: flipOutX; + animation-name: flipOutX; + -webkit-animation-duration: .75s; + animation-duration: .75s; + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; +} + +@-webkit-keyframes flipOutY { + 0% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + opacity: 1; + } + + 100% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + opacity: 0; + } +} + +@keyframes flipOutY { + 0% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + opacity: 1; + } + + 100% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + opacity: 0; + } +} + +.flipOutY { + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; + -webkit-animation-name: flipOutY; + animation-name: flipOutY; + -webkit-animation-duration: .75s; + animation-duration: .75s; +} + +@-webkit-keyframes lightSpeedIn { + 0% { + -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); + transform: translate3d(100%, 0, 0) skewX(-30deg); + opacity: 0; + } + + 60% { + -webkit-transform: skewX(20deg); + transform: skewX(20deg); + opacity: 1; + } + + 80% { + -webkit-transform: skewX(-5deg); + transform: skewX(-5deg); + opacity: 1; + } + + 100% { + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes lightSpeedIn { + 0% { + -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); + transform: translate3d(100%, 0, 0) skewX(-30deg); + opacity: 0; + } + + 60% { + -webkit-transform: skewX(20deg); + transform: skewX(20deg); + opacity: 1; + } + + 80% { + -webkit-transform: skewX(-5deg); + transform: skewX(-5deg); + opacity: 1; + } + + 100% { + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.lightSpeedIn { + -webkit-animation-name: lightSpeedIn; + animation-name: lightSpeedIn; + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; +} + +@-webkit-keyframes lightSpeedOut { + 0% { + opacity: 1; + } + + 100% { + -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); + transform: translate3d(100%, 0, 0) skewX(30deg); + opacity: 0; + } +} + +@keyframes lightSpeedOut { + 0% { + opacity: 1; + } + + 100% { + -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); + transform: translate3d(100%, 0, 0) skewX(30deg); + opacity: 0; + } +} + +.lightSpeedOut { + -webkit-animation-name: lightSpeedOut; + animation-name: lightSpeedOut; + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; +} + +@-webkit-keyframes rotateIn { + 0% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, -200deg); + transform: rotate3d(0, 0, 1, -200deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes rotateIn { + 0% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, -200deg); + transform: rotate3d(0, 0, 1, -200deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.rotateIn { + -webkit-animation-name: rotateIn; + animation-name: rotateIn; +} + +@-webkit-keyframes rotateInDownLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes rotateInDownLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.rotateInDownLeft { + -webkit-animation-name: rotateInDownLeft; + animation-name: rotateInDownLeft; +} + +@-webkit-keyframes rotateInDownRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes rotateInDownRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.rotateInDownRight { + -webkit-animation-name: rotateInDownRight; + animation-name: rotateInDownRight; +} + +@-webkit-keyframes rotateInUpLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes rotateInUpLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.rotateInUpLeft { + -webkit-animation-name: rotateInUpLeft; + animation-name: rotateInUpLeft; +} + +@-webkit-keyframes rotateInUpRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -90deg); + transform: rotate3d(0, 0, 1, -90deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes rotateInUpRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -90deg); + transform: rotate3d(0, 0, 1, -90deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.rotateInUpRight { + -webkit-animation-name: rotateInUpRight; + animation-name: rotateInUpRight; +} + +@-webkit-keyframes rotateOut { + 0% { + -webkit-transform-origin: center; + transform-origin: center; + opacity: 1; + } + + 100% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, 200deg); + transform: rotate3d(0, 0, 1, 200deg); + opacity: 0; + } +} + +@keyframes rotateOut { + 0% { + -webkit-transform-origin: center; + transform-origin: center; + opacity: 1; + } + + 100% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, 200deg); + transform: rotate3d(0, 0, 1, 200deg); + opacity: 0; + } +} + +.rotateOut { + -webkit-animation-name: rotateOut; + animation-name: rotateOut; +} + +@-webkit-keyframes rotateOutDownLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } +} + +@keyframes rotateOutDownLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } +} + +.rotateOutDownLeft { + -webkit-animation-name: rotateOutDownLeft; + animation-name: rotateOutDownLeft; +} + +@-webkit-keyframes rotateOutDownRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +@keyframes rotateOutDownRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +.rotateOutDownRight { + -webkit-animation-name: rotateOutDownRight; + animation-name: rotateOutDownRight; +} + +@-webkit-keyframes rotateOutUpLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +@keyframes rotateOutUpLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +.rotateOutUpLeft { + -webkit-animation-name: rotateOutUpLeft; + animation-name: rotateOutUpLeft; +} + +@-webkit-keyframes rotateOutUpRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 90deg); + transform: rotate3d(0, 0, 1, 90deg); + opacity: 0; + } +} + +@keyframes rotateOutUpRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 90deg); + transform: rotate3d(0, 0, 1, 90deg); + opacity: 0; + } +} + +.rotateOutUpRight { + -webkit-animation-name: rotateOutUpRight; + animation-name: rotateOutUpRight; +} + +@-webkit-keyframes hinge { + 0% { + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 20%, 60% { + -webkit-transform: rotate3d(0, 0, 1, 80deg); + transform: rotate3d(0, 0, 1, 80deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 40%, 80% { + -webkit-transform: rotate3d(0, 0, 1, 60deg); + transform: rotate3d(0, 0, 1, 60deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + opacity: 1; + } + + 100% { + -webkit-transform: translate3d(0, 700px, 0); + transform: translate3d(0, 700px, 0); + opacity: 0; + } +} + +@keyframes hinge { + 0% { + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 20%, 60% { + -webkit-transform: rotate3d(0, 0, 1, 80deg); + transform: rotate3d(0, 0, 1, 80deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 40%, 80% { + -webkit-transform: rotate3d(0, 0, 1, 60deg); + transform: rotate3d(0, 0, 1, 60deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + opacity: 1; + } + + 100% { + -webkit-transform: translate3d(0, 700px, 0); + transform: translate3d(0, 700px, 0); + opacity: 0; + } +} + +.hinge { + -webkit-animation-name: hinge; + animation-name: hinge; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes rollIn { + 0% { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes rollIn { + 0% { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.rollIn { + -webkit-animation-name: rollIn; + animation-name: rollIn; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes rollOut { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + } +} + +@keyframes rollOut { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + } +} + +.rollOut { + -webkit-animation-name: rollOut; + animation-name: rollOut; +} + +@-webkit-keyframes zoomIn { + 0% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 50% { + opacity: 1; + } +} + +@keyframes zoomIn { + 0% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 50% { + opacity: 1; + } +} + +.zoomIn { + -webkit-animation-name: zoomIn; + animation-name: zoomIn; +} + +@-webkit-keyframes zoomInDown { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomInDown { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomInDown { + -webkit-animation-name: zoomInDown; + animation-name: zoomInDown; +} + +@-webkit-keyframes zoomInLeft { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); + transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomInLeft { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); + transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomInLeft { + -webkit-animation-name: zoomInLeft; + animation-name: zoomInLeft; +} + +@-webkit-keyframes zoomInRight { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); + transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomInRight { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); + transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomInRight { + -webkit-animation-name: zoomInRight; + animation-name: zoomInRight; +} + +@-webkit-keyframes zoomInUp { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomInUp { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomInUp { + -webkit-animation-name: zoomInUp; + animation-name: zoomInUp; +} + +@-webkit-keyframes zoomOut { + 0% { + opacity: 1; + } + + 50% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 100% { + opacity: 0; + } +} + +@keyframes zoomOut { + 0% { + opacity: 1; + } + + 50% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 100% { + opacity: 0; + } +} + +.zoomOut { + -webkit-animation-name: zoomOut; + animation-name: zoomOut; +} + +@-webkit-keyframes zoomOutDown { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomOutDown { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomOutDown { + -webkit-animation-name: zoomOutDown; + animation-name: zoomOutDown; +} + +@-webkit-keyframes zoomOutLeft { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: scale(.1) translate3d(-2000px, 0, 0); + transform: scale(.1) translate3d(-2000px, 0, 0); + -webkit-transform-origin: left center; + transform-origin: left center; + } +} + +@keyframes zoomOutLeft { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: scale(.1) translate3d(-2000px, 0, 0); + transform: scale(.1) translate3d(-2000px, 0, 0); + -webkit-transform-origin: left center; + transform-origin: left center; + } +} + +.zoomOutLeft { + -webkit-animation-name: zoomOutLeft; + animation-name: zoomOutLeft; +} + +@-webkit-keyframes zoomOutRight { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: scale(.1) translate3d(2000px, 0, 0); + transform: scale(.1) translate3d(2000px, 0, 0); + -webkit-transform-origin: right center; + transform-origin: right center; + } +} + +@keyframes zoomOutRight { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: scale(.1) translate3d(2000px, 0, 0); + transform: scale(.1) translate3d(2000px, 0, 0); + -webkit-transform-origin: right center; + transform-origin: right center; + } +} + +.zoomOutRight { + -webkit-animation-name: zoomOutRight; + animation-name: zoomOutRight; +} + +@-webkit-keyframes zoomOutUp { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomOutUp { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomOutUp { + -webkit-animation-name: zoomOutUp; + animation-name: zoomOutUp; +} + +@-webkit-keyframes slideInDown { + 0% { + -webkit-transform: translateY(-100%); + transform: translateY(-100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateY(0); + transform: translateY(0); + } +} + +@keyframes slideInDown { + 0% { + -webkit-transform: translateY(-100%); + transform: translateY(-100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateY(0); + transform: translateY(0); + } +} + +.slideInDown { + -webkit-animation-name: slideInDown; + animation-name: slideInDown; +} + +@-webkit-keyframes slideInLeft { + 0% { + -webkit-transform: translateX(-100%); + transform: translateX(-100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} + +@keyframes slideInLeft { + 0% { + -webkit-transform: translateX(-100%); + transform: translateX(-100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} + +.slideInLeft { + -webkit-animation-name: slideInLeft; + animation-name: slideInLeft; +} + +@-webkit-keyframes slideInRight { + 0% { + -webkit-transform: translateX(100%); + transform: translateX(100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} + +@keyframes slideInRight { + 0% { + -webkit-transform: translateX(100%); + transform: translateX(100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} + +.slideInRight { + -webkit-animation-name: slideInRight; + animation-name: slideInRight; +} + +@-webkit-keyframes slideInUp { + 0% { + -webkit-transform: translateY(100%); + transform: translateY(100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateY(0); + transform: translateY(0); + } +} + +@keyframes slideInUp { + 0% { + -webkit-transform: translateY(100%); + transform: translateY(100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateY(0); + transform: translateY(0); + } +} + +.slideInUp { + -webkit-animation-name: slideInUp; + animation-name: slideInUp; +} + +@-webkit-keyframes slideOutDown { + 0% { + -webkit-transform: translateY(0); + transform: translateY(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateY(100%); + transform: translateY(100%); + } +} + +@keyframes slideOutDown { + 0% { + -webkit-transform: translateY(0); + transform: translateY(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateY(100%); + transform: translateY(100%); + } +} + +.slideOutDown { + -webkit-animation-name: slideOutDown; + animation-name: slideOutDown; +} + +@-webkit-keyframes slideOutLeft { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateX(-100%); + transform: translateX(-100%); + } +} + +@keyframes slideOutLeft { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateX(-100%); + transform: translateX(-100%); + } +} + +.slideOutLeft { + -webkit-animation-name: slideOutLeft; + animation-name: slideOutLeft; +} + +@-webkit-keyframes slideOutRight { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateX(100%); + transform: translateX(100%); + } +} + +@keyframes slideOutRight { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateX(100%); + transform: translateX(100%); + } +} + +.slideOutRight { + -webkit-animation-name: slideOutRight; + animation-name: slideOutRight; +} + +@-webkit-keyframes slideOutUp { + 0% { + -webkit-transform: translateY(0); + transform: translateY(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateY(-100%); + transform: translateY(-100%); + } +} + +@keyframes slideOutUp { + 0% { + -webkit-transform: translateY(0); + transform: translateY(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateY(-100%); + transform: translateY(-100%); + } +} + +.slideOutUp { + -webkit-animation-name: slideOutUp; + animation-name: slideOutUp; +} diff --git a/git_deps/html/css/git-deps-tips.css b/git_deps/html/css/git-deps-tips.css new file mode 100644 index 0000000..909badb --- /dev/null +++ b/git_deps/html/css/git-deps-tips.css @@ -0,0 +1,79 @@ +.d3-tip { + line-height: 1; + padding: 5px; + background: rgba(247, 251, 252, 0.9); + font-family: Helvetica, arial, freesans, clean, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol'; + border-radius: 2px; + pointer-events: none; + border: 1px solid #e5e5e5; +} + +/* Creates a small triangle extender for the tooltip */ +.d3-tip:after { + box-sizing: border-box; + display: inline; + font-size: 10px; + width: 100%; + line-height: 1; + color: rgba(0, 0, 0, 0.8); + position: absolute; + pointer-events: none; +} + +/* Northward tooltips */ +.d3-tip.n:after { + content: "\25BC"; + margin: -1px 0 0 0; + top: 100%; + left: 0; + text-align: center; +} + +/* Eastward tooltips */ +.d3-tip.e:after { + content: "\25C0"; + margin: -4px 0 0 0; + top: 50%; + left: -8px; +} + +/* Southward tooltips */ +.d3-tip.s:after { + content: "\25B2"; + margin: 0 0 1px 0; + top: -8px; + left: 0; + text-align: center; +} + +/* Westward tooltips */ +.d3-tip.w:after { + content: "\25B6"; + margin: -4px 0 0 -1px; + top: 50%; + left: 100%; +} + +.d3-tip p.commit-title { + font-weight: bold; + color: #4e575b; + font-size: 15px; + margin: 0.5em 0; +} + +.d3-tip .commit-describe { + font-size: 12px; + margin: 0.5em 0; +} + +.d3-tip .commit-meta { + color: #979a9c; + font-size: 11px; +} + +.d3-tip .commit-body pre { + color: #596063; + margin: 0.5em 0; + /* padding-left: 8px; */ + /* border-left: 1px solid #e5e5e5; */ +} diff --git a/git_deps/html/css/git-deps.css b/git_deps/html/css/git-deps.css new file mode 100644 index 0000000..ea21821 --- /dev/null +++ b/git_deps/html/css/git-deps.css @@ -0,0 +1,93 @@ +body { + font-family: Helvetica, arial, freesans, clean, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol'; + width: 100vw; + height: 100vh; + margin: 0px; +} + +#page { + margin: 8px; + display: flex; /* use the flex model */ + flex-direction: column; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +} + +h1 { + margin: 0; + background: rgb(128, 150, 174); + color: white; + padding: 0.3em; +} + +#top p { + margin-right: 320px; /* Avoid overlap with noty boxes */ +} + +#svg-container { + flex: 1; + border: 1px solid #ccc; /* width has to be half of SVG_MARGIN */ +} + +rect.background { + fill: white; + cursor: all-scroll; +} + +g.node rect { + stroke: #e5e5e5; + stroke-width: 2px; + cursor: pointer; /* move is semantically better but looks the same as all-scroll */ +} + +g.node rect.explored { + fill: rgba(206, 236, 221, 0.54); +} + +g.node rect.unexplored { + fill: rgba(242, 242, 255, 0.54); +} + +g.node text { + /* fill: black; */ + fill: #295b8c; + font-size: 15px; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-weight: bold; + text-anchor: middle; + alignment-baseline: middle; + cursor: pointer; + pointer-events: none; +} + +.plus-icon use { + display: none; +} + +.plus-icon:hover use { + display: visible; +} + +.link { + fill: none; + stroke-width: 2px; + opacity: 0.4; + marker-end: url(#end-arrow); +} + +.commitish input { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; +} + +.commit-ref { + font-weight: bold; + color: #26894d; +} + +.noty_text p { + margin-top: 6px; + margin-bottom: 6px; +} diff --git a/git_deps/html/git-deps.html b/git_deps/html/git-deps.html new file mode 100644 index 0000000..6ced12b --- /dev/null +++ b/git_deps/html/git-deps.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> + +<html lang="en"> +<head> + <meta charset="utf-8" /> + <title>git commit dependency graph</title> + + <script language="javascript" type="text/javascript" src="js/bundle.js"></script> + + <link rel="stylesheet" type="text/css" href="css/animate.css" /> + <link rel="stylesheet" type="text/css" href="css/git-deps.css" /> + <link rel="stylesheet" type="text/css" href="css/git-deps-tips.css" /> +</head> + +<body> + <div id="page"> + <div id="top"> + <h1>git commit dependency graph</h1> + + <p> + Use mouse-wheel to zoom. + Drag background to pan. + Hover over a commit for more information. + Click a commit's plus icon to find dependencies of that commit. + </p> + + <form class="commitish" action="#"> + Detect dependencies for: + <input type="text" name="commitish" size="20" + value="master" autofocus /> + <button>Submit</button> + <button type="button" onclick="full_screen_click()">Full screen</button> + <button type="button" onclick="zoom_to_fit()">Zoom to fit (or double click)</button> + </form> + </div> + + <div id="svg-container" /> + </div> +</body> +</html> diff --git a/git_deps/html/js/.gitignore b/git_deps/html/js/.gitignore new file mode 100644 index 0000000..0e804e3 --- /dev/null +++ b/git_deps/html/js/.gitignore @@ -0,0 +1 @@ +bundle.js diff --git a/git_deps/html/js/fullscreen.js b/git_deps/html/js/fullscreen.js new file mode 100644 index 0000000..6d8f3d8 --- /dev/null +++ b/git_deps/html/js/fullscreen.js @@ -0,0 +1,48 @@ + +function endFullScreen(oncancel) { + if (!RunPrefixMethod(document, "FullScreen") && !RunPrefixMethod(document, "IsFullScreen")) { + oncancel(); + } +} +function fullScreen(e, oncancel) { + if (RunPrefixMethod(document, "FullScreen") || RunPrefixMethod(document, "IsFullScreen")) { + RunPrefixMethod(document, "CancelFullScreen"); + } + else { + RunPrefixMethod(e, "RequestFullScreen"); + e.setAttribute("width", screen.width); + e.setAttribute("height", screen.height); + } + if (arguments.length > 1) { + var f = function () { endFullScreen(oncancel); }; + document.addEventListener("fullscreenchange", f, false); + document.addEventListener("mozfullscreenchange", f, false); + document.addEventListener("webkitfullscreenchange", f, false); + } +} + +var pfx = ["webkit", "moz", "ms", "o", ""]; +function RunPrefixMethod(obj, method) { + + var p = 0, m, t; + while (p < pfx.length && !obj[m]) { + m = method; + if (pfx[p] == "") { + m = m.substr(0, 1).toLowerCase() + m.substr(1); + } + m = pfx[p] + m; + t = typeof obj[m]; + if (t != "undefined") { + pfx = [pfx[p]]; + return (t == "function" ? obj[m]() : obj[m]); + } + p++; + } +} + +function isFullScreen() { + var fullscreenEnabled = document.fullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled; + return fullscreenEnabled; +} + +module.exports = fullScreen; diff --git a/git_deps/html/js/git-deps-data.coffee b/git_deps/html/js/git-deps-data.coffee new file mode 100644 index 0000000..34715a3 --- /dev/null +++ b/git_deps/html/js/git-deps-data.coffee @@ -0,0 +1,108 @@ +# The list of nodes and links to feed into WebCola. +# These will be dynamically built as we retrieve them via XHR. +nodes = [] +links = [] + +# WebCola requires links to refer to nodes by index within the +# nodes array, so as nodes are dynamically added, we need to +# be able to retrieve their index efficiently in order to add +# links to/from them. This also allows us to avoid adding the +# same node twice. +node_index = {} + +# Track dependencies in a hash of hashes which maps parents to +# children to booleans. Constraints will be added to try to keep +# siblings at the same y position. For this we need to track +# siblings, which we do by mapping each parent to an array of its +# siblings in this hash. It also enables us to deduplicate links +# across multiple XHRs. +deps = {} + +# Track dependences in reverse in a hash of hashes which maps children +# to parents to booleans. This allows us to highlight parents when +# the mouse hovers over a child, and know when we can safely remove +# a commit due to its sole parent being deleted. +rdeps = {} + +# Returns 1 iff a node was added, otherwise 0. +add_node = (commit) -> + if commit.sha1 of node_index + n = node commit.sha1 + n.explored ||= commit.explored + return 0 + + nodes.push commit + node_index[commit.sha1] = nodes.length - 1 + return 1 + +# Returns 1 iff a dependency was added, otherwise 0. +add_dependency = (parent_sha1, child_sha1) -> + deps[parent_sha1] = {} unless parent_sha1 of deps + + # We've already got this link, presumably + # from a previous XHR. + return 0 if child_sha1 of deps[parent_sha1] + deps[parent_sha1][child_sha1] = true + add_link parent_sha1, child_sha1 + return 1 + +# Returns 1 iff a reverse dependency was added, otherwise 0. +add_rev_dependency = (child_sha1, parent_sha1) -> + rdeps[child_sha1] = {} unless child_sha1 of rdeps + + # We've already got this link, presumably + # from a previous XHR. + return 0 if parent_sha1 of rdeps[child_sha1] + rdeps[child_sha1][parent_sha1] = true + return 1 + +add_link = (parent_sha1, child_sha1) -> + pi = node_index[parent_sha1] + ci = node_index[child_sha1] + link = + source: pi + target: ci + value: 1 # no idea what WebCola needs this for + + links.push link + return + +# Returns true iff new data was added. +add_data = (data) -> + new_nodes = 0 + new_deps = 0 + for commit in data.commits + new_nodes += add_node(commit) + + for dep in data.dependencies + new_deps += add_dependency(dep.parent, dep.child) + add_rev_dependency(dep.child, dep.parent) + + if new_nodes > 0 or new_deps > 0 + return [ + new_nodes + new_deps + data.query + ] + + return false + +node = (sha1) -> + i = node_index[sha1] + unless i? + console.error "No index for SHA1 '#{sha1}'" + return null + return nodes[i] + +module.exports = + # Variables (N.B. if these variables are reinitialised at any + # point, the values here will become stale and require updating) + nodes: nodes + links: links + node_index: node_index + deps: deps + rdeps: rdeps + + # Functions + add: add_data + node: node diff --git a/git_deps/html/js/git-deps-graph.coffee b/git_deps/html/js/git-deps-graph.coffee new file mode 100644 index 0000000..7ad6827 --- /dev/null +++ b/git_deps/html/js/git-deps-graph.coffee @@ -0,0 +1,595 @@ +jQuery = require "jquery" +$ = jQuery +d3 = require "d3" +d3tip = require "d3-tip" +d3tip d3 + +# Hacky workaround: +# https://github.com/tgdwyer/WebCola/issues/145#issuecomment-271316856 +window.d3 = d3 + +cola = require "webcola" + +global.gdn = require "./git-deps-noty.coffee" +global.gdd = require "./git-deps-data.coffee" +global.gdl = require "./git-deps-layout.coffee" + +fullScreen = require "./fullscreen" + +SVG_MARGIN = 2 # space around <svg>, matching #svg-container border +RECT_MARGIN = 14 # space in between <rects> +PADDING = 5 # space in between <text> label and <rect> border +EDGE_ROUTING_MARGIN = 3 +PLUS_ICON_WIDTH = 14 + +svg_width = 960 +svg_height = 800 +old_svg_height = undefined +old_svg_width = undefined + +color = d3.scale.category20() + +global.d3cola = cola.d3adaptor() +d3cola + .flowLayout("y", 100) + .avoidOverlaps(true) + #.linkDistance(60) + #.symmetricDiffLinkLengths(30) + #.jaccardLinkLengths(100) + +# d3 visualization elements +container = undefined +svg = undefined +fg = undefined +nodes = undefined +paths = undefined +tip = undefined +tip_template = undefined +zoom = undefined + +options = undefined # Options will be retrieved from web server + +jQuery -> + d3.json "options", (error, data) -> + options = data + gdl.debug = options.debug + + d3.html "tip-template.html", (error, html) -> + tip_template = html + + #setup_default_form_values(); + $("form.commitish").submit (event) -> + event.preventDefault() + add_commitish $(".commitish input").val() + + init_svg() + +setup_default_form_values = -> + $("input[type=text]").each(-> + $(this).val $(this).attr("defaultValue") + $(this).css color: "grey" + ).focus(-> + if $(this).val() is $(this).attr("defaultValue") + $(this).val "" + $(this).css color: "black" + ).blur -> + if $(this).val() is "" + $(this).val $(this).attr("defaultValue") + $(this).css color: "grey" + +resize_window = -> + calculate_svg_size_from_container() + fit_svg_to_container() + redraw true + +redraw = (transition) -> + # if mouse down then we are dragging not panning + # if nodeMouseDown + # return + ((if transition then fg.transition() else fg)) + .attr "transform", + "translate(#{zoom.translate()}) scale(#{zoom.scale()})" + +graph_bounds = -> + x = Number.POSITIVE_INFINITY + X = Number.NEGATIVE_INFINITY + y = Number.POSITIVE_INFINITY + Y = Number.NEGATIVE_INFINITY + fg.selectAll(".node").each (d) -> + x = Math.min(x, d.x - d.width / 2) + y = Math.min(y, d.y - d.height / 2) + X = Math.max(X, d.x + d.width / 2) + Y = Math.max(Y, d.y + d.height / 2) + return {} = + x: x + X: X + y: y + Y: Y + +fit_svg_to_container = -> + svg.attr("width", svg_width).attr("height", svg_height) + +full_screen_cancel = -> + svg_width = old_svg_width + svg_height = old_svg_height + fit_svg_to_container() + #zoom_to_fit(); + resize_window() + +full_screen_click = -> + fullScreen container.node(), full_screen_cancel + fit_svg_to_container() + resize_window() + #zoom_to_fit(); + return false + +zoom_to_fit = -> + b = graph_bounds() + w = b.X - b.x + h = b.Y - b.y + cw = svg.attr("width") + ch = svg.attr("height") + s = Math.min(cw / w, ch / h) + tx = -b.x * s + (cw / s - w) * s / 2 + ty = -b.y * s + (ch / s - h) * s / 2 + zoom.translate([tx, ty]).scale s + redraw true + return false + +window.full_screen_click = full_screen_click +window.zoom_to_fit = zoom_to_fit + +add_commitish = (commitish) -> + tip.hide() if tip? + draw_graph commitish + +calculate_svg_size_from_container = -> + old_svg_width = svg_width + old_svg_height = svg_height + svg_width = container.node().offsetWidth - SVG_MARGIN + svg_height = container.node().offsetHeight - SVG_MARGIN + +init_svg = -> + container = d3.select("#svg-container") + calculate_svg_size_from_container() + svg = container.append("svg") + .attr("width", svg_width) + .attr("height", svg_height) + d3cola.size [svg_width, svg_height] + + d3.select(window).on "resize", resize_window + + zoom = d3.behavior.zoom() + + svg.append("rect") + .attr("class", "background") + .attr("width", "100%") + .attr("height", "100%") + .call(zoom.on("zoom", redraw)) + .on("dblclick.zoom", zoom_to_fit) + + fg = svg.append("g") + svg_defs fg + +update_cola = -> + d3cola + .nodes(gdd.nodes) + .links(gdd.links) + .constraints(gdl.constraints) + +draw_graph = (commitish) -> + d3.json "deps.json/" + commitish, (error, data) -> + if error + details = JSON.parse(error.responseText) + gdn.error details.message + return + + new_data = gdd.add(data) + + unless new_data + gdn.warn "No new commits or dependencies found!" + update_rect_explored() + return + new_data_notification new_data + focus_commitish_input() + + gdl.build_constraints() + update_cola() + + paths = fg.selectAll(".link") + .data(gdd.links, link_key) + paths.enter().append("svg:path") + .attr("class", "link") + .attr("stroke", (d) -> color(link_key(d))) + nodes = fg.selectAll(".node") + .data(gdd.nodes, (d) -> d.sha1) + global.nodes = nodes + + g_enter = nodes.enter().append("g") + .attr("class", "node") + # Questionable attempt to use dagre layout as starting positions + # https://github.com/tgdwyer/WebCola/issues/63 + nodes.each (d, i) -> + n = gdl.node d.sha1 + d.x = n.x + d.y = n.y + nodes.attr "transform", (d) -> + translate d.x, d.y + + # N.B. has to be done on the update selection, i.e. *after* the enter! + nodes.call(d3cola.drag) + + init_tip() unless tip? + # Event handlers need to be updated every time new nodes are added. + init_tip_event_handlers(nodes) + + [rects, labels] = draw_new_nodes fg, g_enter + position_nodes(rects, labels) + update_rect_explored() + +focus_commitish_input = () -> + d3.select('.commitish input').node().focus() + +# Required for object constancy: http://bost.ocks.org/mike/constancy/ ... +link_key = (link) -> + source = sha1_of_link_pointer(link.source) + target = sha1_of_link_pointer(link.target) + return source + " " + target + +# ... but even though link sources and targets are initially fed in +# as indices into the nodes array, webcola then replaces the indices +# with references to the node objects. So we have to deal with both +# cases when ensuring we are uniquely identifying each link. +sha1_of_link_pointer = (pointer) -> + return pointer.sha1 if typeof (pointer) is "object" + return gdd.nodes[pointer].sha1 + +init_tip = () -> + tip = d3.tip().attr("class", "d3-tip").html(tip_html) + global.tip = tip + fg.call tip + +# A wrapper around tip.show is required to perform multiple visual +# actions when the mouse hovers over a node; however even if the only +# action required was to show the tool tip, the wrapper would still be +# required in order to work around something which looks like a bug in +# d3 or d3-tip. tip.show is defined as: +# +# function() { +# var args = Array.prototype.slice.call(arguments) +# if(args[args.length - 1] instanceof SVGElement) target = args.pop() +# ... +# +# and there's also: +# +# function getScreenBBox() { +# var targetel = target || d3.event.target; +# ... +# +# which I'm guessing normally uses d3.event.target. However for some +# reason when using tip.show as the dragend handler, d3.event.target +# points to a function rather than the expected DOM element, which +# appears to be exactly the same problem described here: +# +# http://stackoverflow.com/questions/12934731/d3-event-targets +# +# However I tried rects.call ... instead of nodes.call as suggested in +# that SO article, but it resulted in the callback not being triggered +# at all. By *always* providing the exact SVGElement the tip is +# supposed to target, the desired behaviour is obtained. If +# node_mouseover is only used in tip_dragend_handler then the target +# gets memoised, and a normal hover-based tip.show shows the target +# last shown by a drag, rather than the node being hovered over. +# Weird, and annoying. +node_mouseover = (d, i) -> + tip.show d, i, nodes[0][i] + highlight_nodes d3.select(nodes[0][i]), false + highlight_parents(d, i, true) + highlight_children(d, i, true) + +node_mouseout = (d, i) -> + tip.hide d, i, nodes[0][i] + highlight_nodes d3.select(nodes[0][i]), false + highlight_parents(d, i, false) + highlight_children(d, i, false) + +highlight_parents = (d, i, highlight) -> + sha1 = gdd.nodes[i].sha1 + parents = nodes.filter (d, i) -> + d.sha1 of (gdd.rdeps[sha1] || {}) + highlight_nodes parents, highlight, 'rgb(74, 200, 148)' + +highlight_children = (d, i, highlight) -> + sha1 = gdd.nodes[i].sha1 + children = nodes.filter (d, i) -> + d.sha1 of (gdd.deps[sha1] || {}) + highlight_nodes children, highlight, 'rgb(128, 197, 247)' + +highlight_nodes = (selection, highlight, colour='#c0c0c0') -> + selection.selectAll('rect') + .transition() + .ease('cubic-out') + .duration(200) + .style('stroke', if highlight then colour else '#e5e5e5') + .style('stroke-width', if highlight then '4px' else '2px') + +tip_dragend_handler = (d, i, elt) -> + focus_commitish_input() + node_mouseover d, i + +init_tip_event_handlers = (selection) -> + # We have to reuse the same drag object, otherwise only one + # of the event handlers will work. + drag = d3cola.drag() + hide_tip_on_drag = drag.on("drag", tip.hide) + on_dragend = drag.on("dragend", tip_dragend_handler) + selection.call hide_tip_on_drag + selection.call on_dragend + +draw_new_nodes = (fg, g_enter) -> + rects = g_enter.append('rect') + .attr('rx', 5) + .attr('ry', 5) + .on('dblclick', (d) -> launch_viewer d) + + labels = g_enter.append('text').text((d) -> + d.name + ).each((d) -> + b = @getBBox() + + # Calculate width/height of rectangle from text bounding box. + d.rect_width = b.width + 2 * PADDING + d.rect_height = b.height + 2 * PADDING + + # Now set the node width/height as used by cola for + # positioning. This has to include the margin + # outside the rectangle. + d.width = d.rect_width + 2 * RECT_MARGIN + d.height = d.rect_height + 2 * RECT_MARGIN + ) + + return [rects, labels] + +explore_node = (d) -> + if d.explored + gdn.warn "Commit #{d.name} already explored" + else + add_commitish d.sha1 + +launch_viewer = (d) -> + window.location.assign "gitfile://#{options.repo_path}##{d.sha1}" + +new_data_notification = (new_data) -> + new_nodes = new_data[0] + new_deps = new_data[1] + query = new_data[2] + notification = + if query.revspec == query.tip_sha1 + "Analysed dependencies of #{query.revspec}" + else if query.revisions.length == 1 + "<span class=\"commit-ref\">#{query.revspec}</span> + resolved as #{query.tip_abbrev}" + else + "<span class=\"commit-ref\">#{query.revspec}</span> + expanded; tip is #{query.tip_abbrev}" + notification += "<p>#{new_nodes} new commit" + notification += "s" unless new_nodes == 1 + notification += "; #{new_deps} new " + + (if new_deps == 1 then "dependency" else "dependencies") + notification += "</p>" + + gdn.success notification + +svg_defs = () -> + # define arrow markers for graph links + defs = svg.insert("svg:defs") + + defs.append("svg:marker") + .attr("id", "end-arrow") + .attr("viewBox", "0 -5 10 10") + .attr("refX", 6) + .attr("markerWidth", 6) + .attr("markerHeight", 6) + .attr("orient", "auto") + .append("svg:path") + .attr("d", "M0,-5L10,0L0,5") + .attr("fill", "#000") + + plus_icon = defs.append("svg:symbol") + .attr("id", "plus-icon") + .attr("viewBox", "-51 -51 102 102") # allow for stroke-width 1 + # border + plus_icon.append("svg:rect") + .attr("width", 100) + .attr("height", 100) + .attr("fill", "#295b8c") + .attr("stroke", "rgb(106, 136, 200)") + .attr("x", -50) + .attr("y", -50) + .attr("rx", 20) + .attr("ry", 20) + # plus sign + plus_icon.append("svg:path") + .attr("d", "M-30,0 H30 M0,-30 V30") + .attr("stroke", "white") + .attr("stroke-width", 10) + .attr("stroke-linecap", "round") + + # Uncomment to see a large version: + # fg.append("use") + # .attr("class", "plus-icon") + # .attr("xlink:href", "#plus-icon") + # .attr("width", "200") + # .attr("height", "200") + # .attr("x", 400) + # .attr("y", 200) + +position_nodes = (rects, labels) -> + rects + .attr("width", (d, i) -> d.rect_width) + .attr("height", (d, i) -> d.rect_height) + .on("mouseover", node_mouseover) + .on("mouseout", node_mouseout) + + # Centre labels + labels + .attr("x", (d) -> d.rect_width / 2) + .attr("y", (d) -> d.rect_height / 2) + .on("mouseover", node_mouseover) + .on("mouseout", node_mouseout) + + d3cola.start 10, 20, 20 + d3cola.on "tick", tick_handler + + # d3cola.on "end", routeEdges + + # turn on overlap avoidance after first convergence + # d3cola.on("end", () -> + # unless d3cola.avoidOverlaps + # gdd.nodes.forEach((v) -> + # v.width = v.height = 10 + # d3cola.avoidOverlaps true + # d3cola.start + +update_rect_explored = () -> + d3.selectAll(".node rect").attr "class", (d) -> + if d.explored then "explored" else "unexplored" + nodes.each (d) -> + existing_icon = d3.select(this).select("use.plus-icon") + if d.explored + existing_icon.remove() + else if existing_icon.empty() + add_plus_icon this + +add_plus_icon = (node_element) -> + n = d3.select(node_element) + rw = node_element.__data__.rect_width + rh = node_element.__data__.rect_height + + icon = n.insert('use') + .attr('class', 'plus-icon') + .attr('xlink:href', '#plus-icon') + .attr('x', rw/2) + .attr('y', rh - PLUS_ICON_WIDTH/2) + .attr('width', 0) + .attr('height', 0) + icon + .on('mouseover', (d, i) -> icon_ease_in icon, rw) + .on('mouseout', (d, i) -> icon_ease_out icon, rw) + .on('click', (d) -> explore_node d) + + n + .on('mouseover', (d, i) -> icon_ease_in icon, rw) + .on('mouseout', (d, i) -> icon_ease_out icon, rw) + +icon_ease_in = (icon, rw) -> + icon.transition() + .ease('cubic-out') + .duration(200) + .attr('width', PLUS_ICON_WIDTH) + .attr('height', PLUS_ICON_WIDTH) + .attr('x', rw/2 - PLUS_ICON_WIDTH/2) + +icon_ease_out = (icon, rw) -> + icon.transition() + .attr(rw/2 - PLUS_ICON_WIDTH/2) + .ease('cubic-out') + .duration(200) + .attr('width', 0) + .attr('height', 0) + .attr('x', rw/2) + +tip_html = (d) -> + fragment = $(tip_template).clone() + top = fragment.find("#fragment") + title = top.find("p.commit-title") + title.text d.title + + if d.refs + title.append " <span />" + refs = title.children().first() + refs.addClass("commit-describe commit-ref") + .text(d.refs.join(" ")) + + top.find("span.commit-author").text(d.author_name) + date = new Date(d.author_time * 1000) + top.find("time.commit-time") + .attr("datetime", date.toISOString()) + .text(date) + pre = top.find(".commit-body pre").text(d.body) + + if options.debug + # deps = gdd.deps[d.sha1] + # if deps + # sha1s = [gdd.node(sha1).name for name, bool of deps] + # top.append("<br />Dependencies: " + sha1s.join(", ")); + index = gdd.node_index[d.sha1] + debug = "<br />node index: " + index + dagre_node = gdl.graph.node(d.sha1) + debug += "<br />dagre: (#{dagre_node.x}, #{dagre_node.y})" + top.append debug + + # Javascript *sucks*. There's no way to get the outerHTML of a + # document fragment, so you have to wrap the whole thing in a + # single parent and then look that up via children[0]. + return fragment[0].children[0].outerHTML + +translate = (x, y) -> + "translate(#{x},#{y})" + +tick_handler = -> + nodes.each (d) -> + # cola sets the bounds property which is a Rectangle + # representing the space which other nodes should not + # overlap. The innerBounds property seems to tell + # cola the Rectangle which is the visible part of the + # node, minus any blank margin. + d.innerBounds = d.bounds.inflate(-RECT_MARGIN) + + nodes.attr "transform", (d) -> + translate d.innerBounds.x, d.innerBounds.y + + paths.each (d) -> + @parentNode.insertBefore this, this if isIE() + + paths.attr "d", (d) -> + # Undocumented: https://github.com/tgdwyer/WebCola/issues/52 + route = cola.makeEdgeBetween \ + d.source.innerBounds, + d.target.innerBounds, + # This value is related to but not equal to the + # distance of arrow tip from object it points at: + 5 + + lineData = [ + {x: route.sourceIntersection.x, y: route.sourceIntersection.y}, + {x: route.arrowStart.x, y: route.arrowStart.y} + ] + return lineFunction lineData + +lineFunction = d3.svg.line() + .x((d) -> d.x) + .y((d) -> d.y) + .interpolate("linear") + +routeEdges = -> + d3cola.prepareEdgeRouting EDGE_ROUTING_MARGIN + paths.attr "d", (d) -> + lineFunction d3cola.routeEdge(d) + # show visibility graph + # (g) -> + # if d.source.id == 10 and d.target.id === 11 + # g.E.forEach (e) => + # vis.append("line").attr("x1", e.source.p.x).attr("y1", e.source.p.y) + # .attr("x2", e.target.p.x).attr("y2", e.target.p.y) + # .attr("stroke", "green") + + if isIE() + paths.each (d) -> + @parentNode.insertBefore this, this + +isIE = -> + (navigator.appName is "Microsoft Internet Explorer") or + ((navigator.appName is "Netscape") and + ((new RegExp "Trident/.*rv:([0-9]{1,}[.0-9]{0,})") + .exec(navigator.userAgent)?)) diff --git a/git_deps/html/js/git-deps-layout.coffee b/git_deps/html/js/git-deps-layout.coffee new file mode 100644 index 0000000..8b8cd05 --- /dev/null +++ b/git_deps/html/js/git-deps-layout.coffee @@ -0,0 +1,253 @@ +DEBUG = false + +MIN_ROW_GAP = 60 +MIN_NODE_X_GAP = 100 # presumably includes the node width +MAX_NODE_X_GAP = 300 +MAX_NODE_Y_GAP = 80 + +dagre = require "dagre" + +gdd = require "./git-deps-data.coffee" + +# The list of constraints to feed into WebCola. +constraints = [] + +# Group nodes by row, as assigned by the y coordinates returned from +# dagre's layout(). This will map a y coordinate onto all nodes +# within that row. +row_groups = {} + +debug = (msg) -> + if exports.debug + console.log msg + +dagre_layout = -> + g = new dagre.graphlib.Graph() + exports.graph = g + + # Set an object for the graph label + g.setGraph {} + + # Default to assigning a new object as a label for each new edge. + g.setDefaultEdgeLabel -> {} + + for node in gdd.nodes + g.setNode node.sha1, + label: node.name + width: node.rect_width or 70 + height: node.rect_height or 30 + + for parent_sha1, children of gdd.deps + for child_sha1, bool of children + g.setEdge parent_sha1, child_sha1 + + dagre.layout g + return g + +dagre_row_groups = -> + g = dagre_layout() + row_groups = {} + exports.row_groups = row_groups + for sha1 in g.nodes() + x = g.node(sha1).x + y = g.node(sha1).y + row_groups[y] = [] unless y of row_groups + row_groups[y].push + sha1: sha1 + x: x + + for y, nodes of row_groups + nodes.sort (n) -> -n.x + + return row_groups + +build_constraints = -> + row_groups = dagre_row_groups() + debug "build_constraints" + for y, row_nodes of row_groups + debug y + debug row_nodes + + constraints.length = 0 # FIXME: only rebuild constraints which changed + + # We want alignment constraints between all nodes which dagre + # assigned the same y value. + #row_alignment_constraints(row_groups) + + # We need separation constraints ensuring that the left-to-right + # ordering within each row assigned by dagre is preserved. + for y, row_nodes of row_groups + # No point having an alignment group with only one node in. + continue if row_nodes.length <= 1 + + # Multiple constraints per row. + debug "ordering for row y=#{y}" + row_node_ordering_constraints(row_nodes) + debug_constraints() + + # We need separation constraints ensuring that the top-to-bottom + # ordering assigned by dagre is preserved. Since all nodes within + # a single row are already constrained to the same y coordinate + # from above, one would have hoped it would be enough to only have + # separation between a single node in adjacent rows: + # + # row_ordering_constraints(row_groups) + + # However, due to https://github.com/tgdwyer/WebCola/issues/61 + # there is more flexibility for y-coordinates within a row than we + # want, so instead we order rows using dependencies. + dependency_ordering_constraints() + +debug_constraints = (cs = constraints) -> + for c in cs + debug c + return + +row_alignment_constraints = (row_groups) -> + row_alignment_constraint(row_nodes) \ + for y, row_nodes of row_groups when row_nodes.length > 1 + +row_alignment_constraint = (row_nodes) -> + debug 'row_alignment_constraint' + # A standard alignment constraint (one per row) is too strict + # because it doesn't give cola enough "wiggle room": + # + # constraint = + # axis: "y" + # type: "alignment" + # offsets: [] + # + # for node in row_nodes + # constraint.offsets.push + # node: gdd.node_index[node.sha1], + # offset: 0 + # + # constraints.push constraint + # + # So instead we use vertical min/max separation constraints: + i = 0 + while i < row_nodes.length - 1 + left = row_nodes[i] + right = row_nodes[i+1] + mm = max_unordered_separation_constraints \ + 'y', MAX_NODE_Y_GAP, + gdd.node_index[left.sha1], + gdd.node_index[right.sha1] + exports.constraints = constraints = constraints.concat mm + i++ + debug_constraints() + return + +row_node_ordering_constraints = (row_nodes) -> + debug 'row_node_ordering_constraints' + i = 0 + while i < row_nodes.length - 1 + left = row_nodes[i] + right = row_nodes[i+1] + left_i = gdd.node_index[left.sha1] + right_i = gdd.node_index[right.sha1] + debug " #{left_i} < #{right_i} (#{left.x} < #{right.x})" + # mm = min_max_ordered_separation_constraints \ + # 'x', MIN_NODE_X_GAP, MAX_NODE_X_GAP, left_i, right_i + min = min_separation_constraint \ + 'x', MIN_NODE_X_GAP, left_i, right_i + exports.constraints = constraints = constraints.concat min + i++ + return + +row_ordering_constraints = (row_groups) -> + debug 'row_ordering_constraints' + row_y_coords = Object.keys(row_groups).sort() + + i = 0 + while i < row_y_coords.length - 1 + upper_y = row_y_coords[i] + lower_y = row_y_coords[i + 1] + upper_node = row_groups[upper_y][0] + lower_node = row_groups[lower_y][0] + constraints.push \ + min_separation_constraint \ + 'y', MIN_ROW_GAP, + gdd.node_index[upper_node.sha1], + gdd.node_index[lower_node.sha1] + + i++ + debug_constraints() + return + +dependency_ordering_constraints = () -> + debug 'dependency_ordering_constraints' + + for parent_sha1, children of gdd.deps + child_sha1s = Object.keys(children).sort (sha1) -> node(sha1).x + dependency_ordering_constraint(parent_sha1, child_sha1s[0]) + len = child_sha1s.length + if len > 1 + dependency_ordering_constraint(parent_sha1, child_sha1s[len-1]) + if len > 2 + middle = Math.floor(len / 2) + dependency_ordering_constraint(parent_sha1, child_sha1s[middle]) + + debug_constraints() + return + +dependency_ordering_constraint = (parent_sha1, child_sha1) -> + constraints.push \ + min_separation_constraint \ + 'y', MIN_ROW_GAP, + gdd.node_index[parent_sha1], + gdd.node_index[child_sha1] + +################################################################## +# helpers + +# Uses approach explained here: +# https://github.com/tgdwyer/WebCola/issues/62#issuecomment-69571870 +min_max_ordered_separation_constraints = (axis, min, max, left, right) -> + return [ + min_separation_constraint(axis, min, left, right), + max_separation_constraint(axis, max, left, right) + ] + +# https://github.com/tgdwyer/WebCola/issues/66 +max_unordered_separation_constraints = (axis, max, left, right) -> + return [ + max_separation_constraint(axis, max, left, right), + max_separation_constraint(axis, max, right, left) + ] + +min_separation_constraint = (axis, gap, left, right) -> + {} = + axis: axis + gap: gap + left: left + right: right + +# We use a negative gap and reverse the inequality, in order to +# achieve a maximum rather than minimum separation gap. However this +# does not prevent the nodes from overlapping or even swapping order. +# For that you also need a min_separation_constraint, but it's more +# convenient to use min_max_ordered_separation_constraints. See +# https://github.com/tgdwyer/WebCola/issues/62#issuecomment-69571870 +# for more details. +max_separation_constraint = (axis, gap, left, right) -> + {} = + axis: axis + gap: -gap + left: right + right: left + +node = (sha1) -> + exports.graph.node sha1 + +module.exports = exports = + # Variables have to be exported every time they're assigned, + # since assignment creates a new object and associated reference + + # Functions + build_constraints: build_constraints + debug_constraints: debug_constraints + node: node + + # Variables + debug: DEBUG diff --git a/git_deps/html/js/git-deps-noty.coffee b/git_deps/html/js/git-deps-noty.coffee new file mode 100644 index 0000000..cec2b08 --- /dev/null +++ b/git_deps/html/js/git-deps-noty.coffee @@ -0,0 +1,32 @@ +noty = require "noty" + +# Different noty types: +# alert, success, error, warning, information, confirmation +noty_error = (text) -> notyfication "error", text +noty_warn = (text) -> notyfication "warning", text +noty_success = (text) -> notyfication "success", text +noty_info = (text) -> notyfication "information", text +noty_debug = (text) -> notyfication "information", text + +# "notyfication" - haha, did you see what I did there? +notyfication = (type, text) -> + noty( + text: text + type: type + layout: "topRight" + theme: "relax" + maxVisible: 15 + timeout: 30000 # ms + animation: + open: "animated bounceInUp" # Animate.css class names + close: "animated bounceOutUp" # Animate.css class names + easing: "swing" # unavailable - no need + speed: 500 # unavailable - no need + ) + +module.exports = + error: noty_error + warn: noty_warn + success: noty_success + info: noty_info + debug: noty_debug diff --git a/git_deps/html/package.json b/git_deps/html/package.json new file mode 100644 index 0000000..d4dfcd7 --- /dev/null +++ b/git_deps/html/package.json @@ -0,0 +1,54 @@ +{ + "name": "git-deps", + "version": "0.1.0", + "authors": [ + "Adam Spiers" + ], + "description": "tool for performing automatic analysis of dependencies between git commits", + "main": "git-deps", + "keywords": [ + "git", + "dependency", + "analysis", + "scm", + "graphing", + "visualization" + ], + "license": "GPL-2.0", + "homepage": "https://github.com/aspiers/git-deps", + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "d3-tip": "~0.6.6", + "d3": "~3.5.3", + "webcola": "aspiers/WebCola#git-deps-master", + "jquery": "~2.1.3", + "noty": "~v2.4.1", + "browserify": "*", + "coffeeify" : "~1.0.0", + "dagre": "~0.7.1" + }, + "devDependencies": { + "watchify": "*" + }, + "directories": { + "test": "test" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/aspiers/git-deps" + }, + "author": "Adam Spiers", + "bugs": { + "url": "https://github.com/aspiers/git-deps/issues" + } +} diff --git a/git_deps/html/test.json b/git_deps/html/test.json new file mode 100644 index 0000000..3d73289 --- /dev/null +++ b/git_deps/html/test.json @@ -0,0 +1,442 @@ +{ + "commits": [ + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": 0, + "author_time": 1420486941, + "body": "This creates the JSON which will eventually be consumed by\nthe Javascript visualizer.\n", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420487137, + "describe": "tags/test-0", + "name": "2b6d591", + "separator": "\n", + "sha": "2b6d5915f6433b9eb1685751b82cfbebcbb37981", + "title": "add JSON listener" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384447153, + "body": "Automatic git commit dependency inference tool.\n\nOriginally committed to:\n\n https://github.com/aspiers/git-config/blob/master/bin/git-deps\n\nand then split off into this repository via git filter-branch\nand other hackery, preserving history.\n", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420476874, + "describe": "", + "name": "b196757", + "separator": "\n", + "sha": "b1967573e81a8100a4cc778936de0ba0a8a8f5cb", + "title": "first prototype of git-deps" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384471712, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477027, + "describe": "", + "name": "3a1dd42", + "separator": "\n", + "sha": "3a1dd42fd6114a634ba7cf037ce61e2aee76db73", + "title": "add logging and recursion" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384563102, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477027, + "describe": "", + "name": "3374b84", + "separator": "\n", + "sha": "3374b8419a45d91d3c0631be11c8cf893b272217", + "title": "add listener classes" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": 0, + "author_time": 1420483579, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420483579, + "describe": "", + "name": "ff82dda", + "separator": "\n", + "sha": "ff82dda196947650bd497301e61b282753193564", + "title": "fix a bunch of PEP8 issues" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384579026, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477028, + "describe": "", + "name": "8d44254", + "separator": "\n", + "sha": "8d442544a20b706b996d66ab390a16fd97b48d6d", + "title": "use new-style classes" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384452615, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477027, + "describe": "", + "name": "b144bfd", + "separator": "\n", + "sha": "b144bfd5feb327ef7ce0c26bbfb6f4da573abfe5", + "title": "refactor into new DependencyDetector class" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": 0, + "author_time": 1420482533, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420482533, + "describe": "", + "name": "e406002", + "separator": "\n", + "sha": "e40600230d1c3059485437bd4d5690d61c9edb2f", + "title": "don't show \"[False]\" default for boolean options" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384650040, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477028, + "describe": "", + "name": "4f27a1e", + "separator": "\n", + "sha": "4f27a1ee2b5fd63a58311a20e2aed0a24eda8da2", + "title": "add --exclude-commits" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384656388, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477028, + "describe": "", + "name": "824f84c", + "separator": "\n", + "sha": "824f84cd594254d0c87f330b855153fc5ffe5ad3", + "title": "add installation instructions" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384579202, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477028, + "describe": "", + "name": "6240939", + "separator": "\n", + "sha": "62409395e260ad01f9ae7b84869f5516ef80c7aa", + "title": "output dependencies as soon as they're found" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384654780, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477028, + "describe": "", + "name": "5071249", + "separator": "\n", + "sha": "5071249715e82dcf3c1db12eec28c1232aba2142", + "title": "avoid adding entries to TODO queue multiple times" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384566591, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477027, + "describe": "", + "name": "6e86e8b", + "separator": "\n", + "sha": "6e86e8b7f648bd6a3a6d3216aa5899414b65cbed", + "title": "don't crash on commits which only add files" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384563081, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477027, + "describe": "", + "name": "acc24a4", + "separator": "\n", + "sha": "acc24a404d82061bbc6db5afb146d83bf131830b", + "title": "add --context-lines" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384567428, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477028, + "describe": "", + "name": "5ec5ccb", + "separator": "\n", + "sha": "5ec5ccbdff508014c61ae9d18f3366a15c0f2689", + "title": "add first line of commits to debug" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384563342, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477027, + "describe": "", + "name": "f2cddb4", + "separator": "\n", + "sha": "f2cddb4aa00de4ddff2cdca251758e25e95e04ad", + "title": "tweaks to improve debugging" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384651799, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477028, + "describe": "", + "name": "1b66efa", + "separator": "\n", + "sha": "1b66efa173a19a8b4c0c47274a1b9cdd8b9912af", + "title": "improve help text" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384567528, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477028, + "describe": "", + "name": "80c247f", + "separator": "\n", + "sha": "80c247fd21a1e7f476d1c8ba289498e216eff3dc", + "title": "--help: put short options first" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384567489, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477028, + "describe": "", + "name": "2ebcb2b", + "separator": "\n", + "sha": "2ebcb2b6081e32e9a463519525bd432287b24520", + "title": "improve --help for --context-lines" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384636660, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477028, + "describe": "", + "name": "f7bf058", + "separator": "\n", + "sha": "f7bf058439fd7499aad7a10418a9f516e6949fbc", + "title": "allow multiple dependents on ARGV, and fix usage string" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384612401, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477028, + "describe": "", + "name": "2a05400", + "separator": "\n", + "sha": "2a05400e232e14f0d4c1cbfb548a0871ea57bd44", + "title": "ignore KeyboardInterrupt" + }, + { + "author_mail": "git@adamspiers.org", + "author_name": "Adam Spiers", + "author_offset": -300, + "author_time": 1384652004, + "body": "", + "committer_mail": "git@adamspiers.org", + "committer_name": "Adam Spiers", + "committer_offset": 0, + "committer_time": 1420477028, + "describe": "", + "name": "4364944", + "separator": "\n", + "sha": "43649442f49876ad22051b085a9258f39bbcd5c6", + "title": "fix error message" + } + ], + "dependencies": [ + { + "child": "b1967573e81a8100a4cc778936de0ba0a8a8f5cb", + "parent": "2b6d5915f6433b9eb1685751b82cfbebcbb37981" + }, + { + "child": "3a1dd42fd6114a634ba7cf037ce61e2aee76db73", + "parent": "2b6d5915f6433b9eb1685751b82cfbebcbb37981" + }, + { + "child": "3374b8419a45d91d3c0631be11c8cf893b272217", + "parent": "2b6d5915f6433b9eb1685751b82cfbebcbb37981" + }, + { + "child": "ff82dda196947650bd497301e61b282753193564", + "parent": "2b6d5915f6433b9eb1685751b82cfbebcbb37981" + }, + { + "child": "8d442544a20b706b996d66ab390a16fd97b48d6d", + "parent": "2b6d5915f6433b9eb1685751b82cfbebcbb37981" + }, + { + "child": "b144bfd5feb327ef7ce0c26bbfb6f4da573abfe5", + "parent": "2b6d5915f6433b9eb1685751b82cfbebcbb37981" + }, + { + "child": "e40600230d1c3059485437bd4d5690d61c9edb2f", + "parent": "2b6d5915f6433b9eb1685751b82cfbebcbb37981" + }, + { + "child": "4f27a1ee2b5fd63a58311a20e2aed0a24eda8da2", + "parent": "2b6d5915f6433b9eb1685751b82cfbebcbb37981" + }, + { + "child": "824f84cd594254d0c87f330b855153fc5ffe5ad3", + "parent": "ff82dda196947650bd497301e61b282753193564" + }, + { + "child": "62409395e260ad01f9ae7b84869f5516ef80c7aa", + "parent": "ff82dda196947650bd497301e61b282753193564" + }, + { + "child": "5071249715e82dcf3c1db12eec28c1232aba2142", + "parent": "ff82dda196947650bd497301e61b282753193564" + }, + { + "child": "6e86e8b7f648bd6a3a6d3216aa5899414b65cbed", + "parent": "ff82dda196947650bd497301e61b282753193564" + }, + { + "child": "acc24a404d82061bbc6db5afb146d83bf131830b", + "parent": "ff82dda196947650bd497301e61b282753193564" + }, + { + "child": "5ec5ccbdff508014c61ae9d18f3366a15c0f2689", + "parent": "ff82dda196947650bd497301e61b282753193564" + }, + { + "child": "f2cddb4aa00de4ddff2cdca251758e25e95e04ad", + "parent": "ff82dda196947650bd497301e61b282753193564" + }, + { + "child": "1b66efa173a19a8b4c0c47274a1b9cdd8b9912af", + "parent": "ff82dda196947650bd497301e61b282753193564" + }, + { + "child": "80c247fd21a1e7f476d1c8ba289498e216eff3dc", + "parent": "ff82dda196947650bd497301e61b282753193564" + }, + { + "child": "2ebcb2b6081e32e9a463519525bd432287b24520", + "parent": "ff82dda196947650bd497301e61b282753193564" + }, + { + "child": "f7bf058439fd7499aad7a10418a9f516e6949fbc", + "parent": "ff82dda196947650bd497301e61b282753193564" + }, + { + "child": "2a05400e232e14f0d4c1cbfb548a0871ea57bd44", + "parent": "ff82dda196947650bd497301e61b282753193564" + }, + { + "child": "43649442f49876ad22051b085a9258f39bbcd5c6", + "parent": "5071249715e82dcf3c1db12eec28c1232aba2142" + } + ] +} diff --git a/git_deps/html/tip-template.html b/git_deps/html/tip-template.html new file mode 100644 index 0000000..1362574 --- /dev/null +++ b/git_deps/html/tip-template.html @@ -0,0 +1,11 @@ +<div id="fragment"> + <p class="commit-title" /> + <div class="commit-meta"> + <span class="commit-author"></span> + authored on + <time class="commit-time" is="relative-time" /> + </div> + <div class="commit-body"> + <pre /> + </div> +</div> |