I've included the CSS for you to be able to use a new tool called Fokus. With Fokus enabled through User:Your username/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 username/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();
})();
czechout ☎ ✍ 12:26: Tue 13 Nov 2012
Update[]
This feature has now been phased out. If you happen to dig around the archives and find this message, know that it no longer will work on this site under any circumstances.
czechout ☎ ✍ 16:40: Mon 03 Dec 2012