|
|
Line 6: |
Line 6: |
|
| |
|
| <pre> | | <pre> |
|
| |
|
| |
| /*! | | /*! |
| * Fokus 0.3 | | * Fokus 0.3 |
Line 61: |
Line 59: |
| } | | } |
|
| |
|
| /**
| |
| * Is this browser capable of running Fokus?
| |
| */
| |
| function capable() {
| |
|
| |
| return !!(
| |
| 'addEventListener' in document &&
| |
| 'pointerEvents' in document.body.style
| |
| );
| |
|
| |
| }
| |
|
| |
| /**
| |
| * Redraws an animates the overlay.
| |
| */
| |
| function redraw() {
| |
|
| |
| // Cache the response of this for re-use below
| |
| var _hasSelection = hasSelection();
| |
|
| |
| // Reset to a solid (less opacity) overlay fill
| |
| overlayContext.clearRect( 0, 0, overlay.width, overlay.height );
| |
| overlayContext.fillStyle = 'rgba( 0, 0, 0, '+ overlayAlpha +' )';
| |
| overlayContext.fillRect( 0, 0, overlay.width, overlay.height );
| |
|
| |
| if( _hasSelection ) {
| |
| if( overlayAlpha < 0.1 ) {
| |
| // Clear the selection instantly
| |
| clearedRegion = selectedRegion;
| |
| }
| |
| else {
| |
| // Ease the cleared region towards the current selection
| |
| clearedRegion.left += ( selectedRegion.left - clearedRegion.left ) * 0.15;
| |
| clearedRegion.top += ( selectedRegion.top - clearedRegion.top ) * 0.15;
| |
| clearedRegion.right += ( selectedRegion.right - clearedRegion.right ) * 0.15;
| |
| clearedRegion.bottom += ( selectedRegion.bottom - clearedRegion.bottom ) * 0.15;
| |
| }
| |
| }
| |
|
| |
| // Cut out the cleared region
| |
| overlayContext.clearRect(
| |
| clearedRegion.left - window.scrollX - PADDING,
| |
| clearedRegion.top - window.scrollY - PADDING,
| |
| ( clearedRegion.right - clearedRegion.left ) + ( PADDING * 2 ),
| |
| ( clearedRegion.bottom - clearedRegion.top ) + ( PADDING * 2 )
| |
| );
| |
|
| |
| // Fade in if there's a valid selection...
| |
| if( _hasSelection ) {
| |
| overlayAlpha += ( OPACITY - overlayAlpha ) * 0.08;
| |
| }
| |
| // ... otherwise fade out
| |
| else {
| |
| overlayAlpha = Math.max( ( overlayAlpha * 0.85 ) - 0.02, 0 );
| |
| }
| |
|
| |
| // Continue so long as there is content selected or we are fading out
| |
| if( _hasSelection || overlayAlpha > 0 ) {
| |
| // Append the overlay if it isn't already in the DOM
| |
| if( !overlay.parentNode ) document.body.appendChild( overlay );
| |
|
| |
| // Stage a new animation frame
| |
| cancelAnimationFrame( redrawAnimation );
| |
| redrawAnimation = requestAnimationFrame( redraw );
| |
| }
| |
| else {
| |
| document.body.removeChild( overlay );
| |
| }
| |
|
| |
| }
| |
|
| |
| /**
| |
| * Steps through all selected nodes and updates current region
| |
| * (bounds of selection).
| |
| */
| |
| function updateSelection() {
| |
|
| |
| // Default to negative space
| |
| selectedRegion = { left: Number.MAX_VALUE, top: Number.MAX_VALUE, right: 0, bottom: 0 };
| |
|
| |
| var nodes = getSelectedNodes();
| |
|
| |
| for( var i = 0, len = nodes.length; i < len; i++ ) {
| |
| var node = nodes[i];
| |
|
| |
| // Select parents of text nodes that have contents
| |
| if( node.nodeName === '#text' && node.nodeValue.trim() ) {
| |
| node = node.parentNode;
| |
| }
| |
|
| |
| // Fetch the screen coordinates for this element
| |
| var position = getScreenPosition( node );
| |
|
| |
| var x = position.x,
| |
| y = position.y,
| |
| w = node.offsetWidth,
| |
| h = node.offsetHeight;
| |
|
| |
| if( node && typeof x === 'number' && typeof w === 'number' && !node.nodeName.match( /^br$/gi ) && ( w > 0 || h > 0 ) ) {
| |
| selectedRegion.left = Math.min( selectedRegion.left, x );
| |
| selectedRegion.top = Math.min( selectedRegion.top, y );
| |
| selectedRegion.right = Math.max( selectedRegion.right, x + w );
| |
| selectedRegion.bottom = Math.max( selectedRegion.bottom, y + h );
| |
| }
| |
| }
| |
|
| |
| if( hasSelection() ) {
| |
| redraw();
| |
| }
| |
|
| |
| }
| |
|
| |
| /**
| |
| * Checks if a region is currently selected.
| |
| */
| |
| function hasSelection() {
| |
|
| |
| return selectedRegion.left < selectedRegion.right && selectedRegion.top < selectedRegion.bottom;
| |
|
| |
| }
| |
|
| |
| function onMouseDown( event ) {
| |
|
| |
| window.addEventListener( 'mousemove', onMouseMove, false );
| |
| window.addEventListener( 'mouseup', onMouseUp, false );
| |
|
| |
| updateSelection();
| |
|
| |
| }
| |
|
| |
| function onMouseMove( event ) {
| |
|
| |
| updateSelection();
| |
|
| |
| }
| |
|
| |
| function onMouseUp( event ) {
| |
|
| |
| window.removeEventListener( 'mousemove', onMouseMove, false );
| |
| window.removeEventListener( 'mouseup', onMouseUp, false );
| |
|
| |
| setTimeout( updateSelection, 1 );
| |
|
| |
| }
| |
|
| |
| function onKeyUp( event ) {
| |
|
| |
| updateSelection();
| |
|
| |
| }
| |
|
| |
| function onWindowResize( event ) {
| |
|
| |
| overlay.width = window.innerWidth;
| |
| overlay.height = window.innerHeight;
| |
|
| |
| }
| |
|
| |
| /**
| |
| * Helper methods for getting selected nodes, source:
| |
| * http://stackoverflow.com/questions/7781963/js-get-array-of-all-selected-nodes-in-contenteditable-div
| |
| */
| |
| function getSelectedNodes() {
| |
|
| |
| if (window.getSelection) {
| |
| var sel = window.getSelection();
| |
| if (!sel.isCollapsed) {
| |
| return getRangeSelectedNodes(sel.getRangeAt(0));
| |
| }
| |
| }
| |
| return [];
| |
|
| |
| }
| |
| function getRangeSelectedNodes( range ) {
| |
|
| |
| var node = range.startContainer;
| |
| var endNode = range.endContainer;
| |
|
| |
| // Special case for a range that is contained within a single node
| |
| if (node == endNode) {
| |
| if( node.nodeName === '#text' ) {
| |
| return [node.parentNode];
| |
| }
| |
| return [node];
| |
| }
| |
|
| |
| // Iterate nodes until we hit the end container
| |
| var rangeNodes = [];
| |
| while (node && node != endNode) {
| |
| rangeNodes.push( node = nextNode(node) );
| |
| }
| |
|
| |
| // Add partially selected nodes at the start of the range
| |
| node = range.startContainer;
| |
| while (node && node != range.commonAncestorContainer) {
| |
| rangeNodes.unshift(node);
| |
| node = node.parentNode;
| |
| }
| |
|
| |
| return rangeNodes;
| |
|
| |
| }
| |
| function nextNode(node) {
| |
|
| |
| if (node.hasChildNodes()) {
| |
| return node.firstChild;
| |
| } else {
| |
| while (node && !node.nextSibling) {
| |
| node = node.parentNode;
| |
| }
| |
| if (!node) {
| |
| return null;
| |
| }
| |
| return node.nextSibling;
| |
| }
| |
|
| |
| }
| |
|
| |
| /**
| |
| * Gets the x/y screen position of the target node, source:
| |
| * http://www.quirksmode.org/js/findpos.html
| |
| */
| |
| function getScreenPosition( node ) {
| |
| var x = 0,
| |
| y = 0;
| |
|
| |
| if ( node.offsetParent ) {
| |
| do {
| |
| x += node.offsetLeft;
| |
| y += node.offsetTop;
| |
| } while ( node = node.offsetParent );
| |
| }
| |
|
| |
| return { x: x, y: y };
| |
| }
| |
|
| |
| /**
| |
| * rAF polyfill.
| |
| */
| |
| (function() {
| |
| var lastTime = 0;
| |
| var vendors = ['ms', 'moz', 'webkit', 'o'];
| |
| for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
| |
| window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
| |
| window.cancelAnimationFrame =
| |
| window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
| |
| }
| |
|
| |
| if (!window.requestAnimationFrame)
| |
| window.requestAnimationFrame = function(callback, element) {
| |
| var currTime = new Date().getTime();
| |
| var timeToCall = Math.max(0, 16 - (currTime - lastTime));
| |
| var id = window.setTimeout(function() { callback(currTime + timeToCall); },
| |
| timeToCall);
| |
| lastTime = currTime + timeToCall;
| |
| return id;
| |
| };
| |
|
| |
| if (!window.cancelAnimationFrame)
| |
| window.cancelAnimationFrame = function(id) {
| |
| clearTimeout(id);
| |
| };
| |
| }());
| |
|
| |
| initialize();
| |
|
| |
| })();
| |
| </pre> | | </pre> |
| {{user:CzechOut/Sig}}{{User:CzechOut/TimeFormat}} 12:26: Tue 13 Nov 2012</span> | | {{user:CzechOut/Sig}}{{User:CzechOut/TimeFormat}} 12:26: Tue 13 Nov 2012</span> |
Spoilers are strongly policed here.
If this thread's title doesn't specify it's spoilery, don't bring any up.
I've included the CSS for you to be able to use a new tool called Fokus. With Fokus enabled through User:Your name/wikia.js, you'll be able to double click on a bit of text, and it will be highlighted for easier reading. I've not made it the default condition, because some people might not want the feature.
If you'd like to try it, just cut and paste the entirety of the following into User:Your name/wikia.js:
/*!
* Fokus 0.3
* http://lab.hakim.se/fokus
*/
(function(){
// Padding around the selection
var PADDING = 5;
// Opacity of the overlay
var OPACITY = 0.60;
// The opaque overlay canvas
var overlay,
overlayContext,
overlayAlpha = 0,
// Reference to the redraw animation so it can be cancelled
redrawAnimation,
// Currently selected region
selectedRegion = { left: 0, top: 0, right: 0, bottom: 0 },
// Currently cleared region
clearedRegion = { left: 0, top: 0, right: 0, bottom: 0 };
// choo choo!
function initialize() {
// Only initialize if the client is capable
if( capable() ) {
overlay = document.createElement( 'canvas' );
overlayContext = overlay.getContext( '2d' );
// Place the canvas on top of
overlay.style.position = 'fixed';
overlay.style.left = 0;
overlay.style.top = 0;
overlay.style.zIndex = 2147483647;
overlay.style.pointerEvents = 'none';
window.addEventListener( 'mousedown', onMouseDown, false );
window.addEventListener( 'keyup', onKeyUp, false );
window.addEventListener( 'resize', onWindowResize, false );
// Trigger an initial resize
onWindowResize();
}
}
czechout<staff /> ☎ ✍ 12:26: Tue 13 Nov 2012