diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ac52de0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# rake standalone +standalone/flying-focus.js + +# rake safari +FlyingFocus.safariextension/flying-focus.css +FlyingFocus.safariextension/flying-focus.js diff --git a/FlyingFocus.safariextension/Icon-64.png b/FlyingFocus.safariextension/Icon-64.png new file mode 100644 index 0000000..5bdca83 Binary files /dev/null and b/FlyingFocus.safariextension/Icon-64.png differ diff --git a/FlyingFocus.safariextension/Info.plist b/FlyingFocus.safariextension/Info.plist new file mode 100644 index 0000000..4c8dbd6 --- /dev/null +++ b/FlyingFocus.safariextension/Info.plist @@ -0,0 +1,54 @@ + + + + + Author + Nikita Vasilyev + Builder Version + 9537.66 + CFBundleDisplayName + Flying Focus + CFBundleIdentifier + com.n12v.flying-focus + CFBundleInfoDictionaryVersion + 6.0 + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1.0 + Chrome + + Content + + Scripts + + End + + flying-focus.js + + + Stylesheets + + flying-focus.css + + + Description + Smooth focus transition + ExtensionInfoDictionaryVersion + 1.0 + Permissions + + Website Access + + Include Secure Pages + + Level + All + + + Update Manifest URL + http://n12v.com/focus-transition/update.plist + Website + http://n12v.com/focus-transition/ + + diff --git a/MIT-LICENSE.txt b/MIT-LICENSE.txt new file mode 100644 index 0000000..bc57aac --- /dev/null +++ b/MIT-LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) Nikita Vasilyev + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..84d7e96 --- /dev/null +++ b/README.markdown @@ -0,0 +1,22 @@ +# [Focus Transition](http://n12v.com/focus-transition/) + +![Flying Focus icon](http://nv.github.io/flying-focus/chrome/icon_128.png) + +Flying Focus is a UI concept. + +# How to build + +## A single-file library + +Create a flying-focus.js that can be included to any web page. +It includes all necessary CSS and has no external dependencies. + + rake standalone + +## Safari extension + + rake safari + +## Chrome extension + +No build step required. Just load it as an unpacked extension from `chrome/`. diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..287e626 --- /dev/null +++ b/Rakefile @@ -0,0 +1,28 @@ +task :default => [:safari, :standalone] + +desc 'Build standalone file' +task :standalone => ['chrome/flying-focus.js', 'chrome/flying-focus.css'] do + require 'jspp' + File.open('standalone/flying-focus.js', 'w') { |file| + text = JSPP('standalone/flying-focus.jspp.js') + file.write(text) + } + puts 'standalone/flying-focus.js' +end + + +desc 'Build Safari extension to ./FlyingFocus.safariextension/' +task :safari => ['chrome/flying-focus.js', 'chrome/flying-focus.css'] do + cp_r ['chrome/flying-focus.js', 'chrome/flying-focus.css'], 'FlyingFocus.safariextension' + puts 'FlyingFocus.safariextension' +end + +#FIXME +#task :firefox do +# +#end + +#FIXME +#task :ie do +# +#end diff --git a/chrome/flying-focus.css b/chrome/flying-focus.css new file mode 100644 index 0000000..4cf379b --- /dev/null +++ b/chrome/flying-focus.css @@ -0,0 +1,28 @@ +#flying-focus { + position: absolute; + margin: 0; + background: transparent; + -webkit-transition-property: left, top, width, height, opacity; + transition-property: left, top, width, height, opacity; + -webkit-transition-timing-function: cubic-bezier(0, 0.2, 0, 1); + transition-timing-function: cubic-bezier(0, 0.2, 0, 1); + visibility: hidden; + pointer-events: none; + box-shadow: 0 0 2px 3px #78aeda, 0 0 2px #78aeda inset; border-radius: 2px; +} +#flying-focus.flying-focus_visible { + visibility: visible; + z-index: 9999; +} +.flying-focus_target { + outline: none !important; /* Doens't work in Firefox :( */ +} + +/* Replace it with @supports rule when browsers catch up */ +@media screen and (-webkit-min-device-pixel-ratio: 0) { + #flying-focus { + box-shadow: none; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -3px; + } +} diff --git a/chrome/flying-focus.js b/chrome/flying-focus.js new file mode 100644 index 0000000..0bea586 --- /dev/null +++ b/chrome/flying-focus.js @@ -0,0 +1,70 @@ +var flyingFocus = document.createElement('flying-focus'); // use uniq element name to decrease the chances of a conflict with website styles +flyingFocus.id = 'flying-focus'; +document.body.appendChild(flyingFocus); + +var DURATION = 100; +flyingFocus.style.transitionDuration = flyingFocus.style.WebkitTransitionDuration = DURATION / 1000 + 's'; + +function offsetOf(elem) { + var rect = elem.getBoundingClientRect(); + var docElem = document.documentElement; + var win = document.defaultView; + var body = document.body; + + var clientTop = docElem.clientTop || body.clientTop || 0, + clientLeft = docElem.clientLeft || body.clientLeft || 0, + scrollTop = win.pageYOffset || docElem.scrollTop || body.scrollTop, + scrollLeft = win.pageXOffset || docElem.scrollLeft || body.scrollLeft, + top = rect.top + scrollTop - clientTop, + left = rect.left + scrollLeft - clientLeft; + + return {top: top, left: left}; +} + +var movingId = 0; + +var prevFocused; + +document.documentElement.addEventListener('focus', function(event) { + var target = event.target; + if (target.id === 'flying-focus') { + return; + } + var offset = offsetOf(target); + flyingFocus.style.left = offset.left + 'px'; + flyingFocus.style.top = offset.top + 'px'; + flyingFocus.style.width = target.offsetWidth + 'px'; + flyingFocus.style.height = target.offsetHeight + 'px'; + + // Would be nice to use: + // + // flyingFocus.style['outline-offset'] = getComputedStyle(target, null)['outline-offset'] + // + // but it always '0px' in WebKit and Blink for some reason :( + + if (prevFocused) { + target.classList.add('flying-focus_target'); + show(); + if (movingId) { + clearTimeout(movingId); + } + movingId = setTimeout(function() { + target.classList.remove('flying-focus_target'); + hide(); + }, DURATION); + } + prevFocused = target; +}, true); + +document.documentElement.addEventListener('blur', function() { + hide(); +}, true); + + +function hide() { + flyingFocus.classList.remove('flying-focus_visible'); +} + +function show() { + flyingFocus.classList.add('flying-focus_visible'); +} diff --git a/chrome/icon_128.png b/chrome/icon_128.png new file mode 100644 index 0000000..41d1177 Binary files /dev/null and b/chrome/icon_128.png differ diff --git a/chrome/icon_48.png b/chrome/icon_48.png new file mode 100644 index 0000000..34bdc72 Binary files /dev/null and b/chrome/icon_48.png differ diff --git a/chrome/manifest.json b/chrome/manifest.json new file mode 100644 index 0000000..9daa421 --- /dev/null +++ b/chrome/manifest.json @@ -0,0 +1,17 @@ +{ + "manifest_version": 2, + "name": "Flying Focus", + "version": "1.0", + "description": "Smooth focus transition", + "content_scripts": [ + { + "js": ["flying-focus.js"], + "css": ["flying-focus.css"], + "matches": [ + "" + ] + } + ], + "author": "Nikita Vasilyev", + "homepage_url": "http://n12v.com/focus-transition/" +} diff --git a/standalone/flying-focus.jspp.js b/standalone/flying-focus.jspp.js new file mode 100644 index 0000000..b652ca8 --- /dev/null +++ b/standalone/flying-focus.jspp.js @@ -0,0 +1,11 @@ +(function() { + +if (document.getElementById('flying-focus')) return; + +/*> ../chrome/flying-focus.js */ + +var style = document.createElement('style'); +style.textContent = "/*> ../chrome/flying-focus.css */"; +document.body.appendChild(style); + +})();