Forum:Cool optional tool ready for your use

From Tardis Wiki, the free Doctor Who reference
ForumsArchive indexPanopticon archives → Cool optional tool ready for your use
This thread has been archived.
Please create a new thread on the new forums if you want to talk about this topic some more.
Please DO NOT add to this discussion.


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<staff />    12:26: Tue 13 Nov 2012

Update[[edit source]]

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<staff />    16:40: Mon 03 Dec 2012