More actions
Index → Tech notes → Cool optional tool ready for your use
Spoilers are strongly policed here.
If this thread's title doesn't specify it's spoilery, don't bring any up.
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:<insert name here>/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:<insert name here>/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(); } } /** * 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(); })();