msssk avatar msssk committed 3b2164b

init

Comments (0)

Files changed (18)

+<html>
+<script>
+chrome.extension.onRequest.addListener( function( request, sender, sendResponse ) {
+	var options;
+
+	if (request.reqtype === 'options') {
+		options = localStorage['options'];
+
+		if( !options ) {
+			options = {};
+		}
+		else {
+			options = JSON.parse( options );
+		}
+
+		sendResponse( { options: options } );
+	}
+});
+</script>
+</html>
+/*global $, jQuery */
+
+/**
+ * @namespace
+ */
+var ClikkitExtension = {};
+
+
+/**
+ * Brighten or darken a color to produce a suitable highlight color
+ * If any of the RGB values provided are greater than 128, the color will be darkened, otherwise brightened
+ * @param rgbString:  a string of the format "rgb(0, 0, 0)"
+ * @returns a highlight color of the format "rgb(28, 28, 28)"
+ */
+ClikkitExtension.highlight = function( rgbString ) {
+	if( rgbString === 'rgba(0, 0, 0, 0)' ) {
+		return( 'rgb(244, 244, 244)' );
+	}
+	
+	var startpos,
+		endpos,
+		red,
+		green,
+		blue,
+		increment;
+
+	startpos = rgbString.indexOf( '(' );
+	endpos = rgbString.indexOf( ',', startpos );
+	red = rgbString.substring( startpos + 1, endpos );
+	red = parseInt( red, 10 );
+	
+	startpos = endpos + 1;
+	endpos = rgbString.indexOf( ',', startpos );
+	green = rgbString.substring( startpos, endpos );
+	green = parseInt( green, 10 );
+	
+	startpos = endpos + 1;
+	endpos = rgbString.indexOf( ',', startpos );
+	if( endpos === -1 ) {
+		endpos = rgbString.indexOf( ')', startpos );
+	}
+	blue = rgbString.substring( startpos, endpos );
+	blue = parseInt( blue, 10 );
+	
+	increment = 11;
+	
+	if( red > 128 || green > 128 || blue > 128 ) {
+		red -= increment;
+		green -= increment;
+		blue -= increment;
+		
+		red = red < 0 ? 0 : red;
+		green = green < 0 ? 0 : green;
+		blue = blue < 0 ? 0 : blue;
+	}
+	else {
+		red += increment;
+		green += increment;
+		blue += increment;
+		
+		red = red > 255 ? 255 : red;
+		green = green > 255 ? 255 : green;
+		blue = blue > 255 ? 255 : blue;
+	}
+	
+	return( 'rgb(' + red + ', ' + green + ', ' + blue + ')' );
+};
+
+
+ClikkitExtension.checkURL = function() {
+	var frameHREF = this.href;
+
+	if( frameHREF.indexOf( 'youtube.com' ) !== -1 || frameHREF.indexOf( 'youtu.be' ) !== -1 ) {
+		frameHREF = ClikkitExtension.getYoutubeEmbedURL( frameHREF );
+		if( frameHREF ) {
+			return { href: frameHREF };
+		}
+	}
+};
+
+
+ClikkitExtension.getYoutubeEmbedURL = function( originalHREF ) {
+	var href = originalHREF,
+		startIndex,
+		endIndex,
+		videoID = '',
+		timeAnchor = '',
+		embedURL = '';
+
+	try {
+		startIndex = href.lastIndexOf( '#' );
+		if( startIndex !== -1 ) {
+			timeAnchor = href.substring( startIndex );
+			href = href.substring( 0, startIndex );
+		}
+
+		if( href.indexOf( 'youtube.com' ) !== -1 ) {
+			startIndex = href.indexOf( '?v=' );
+
+			if( startIndex === -1 ) {
+				startIndex = href.indexOf( '/v/' );
+				
+				if( startIndex === -1 ) {
+					startIndex = href.indexOf( '&v=' );
+				}
+				else {
+					startIndex += 3;
+					endIndex = href.indexOf( '?', startIndex );
+					
+					if( endIndex === -1 ) {
+						videoID = href.substring( startIndex );
+					}
+					else {
+						videoID = href.substring( startIndex, endIndex );
+					}
+				}
+			}
+
+			// we didn't get the videoID yet, but we got the startIndex of either '?v=' or '&v='
+			if( videoID === '' && startIndex !== -1 ) {
+				startIndex += 3;
+				endIndex = href.indexOf( '&', startIndex );
+				
+				if( endIndex === -1 ) {
+					videoID = href.substring( startIndex );
+				}
+				else {
+					videoID = href.substring( startIndex, endIndex );
+				}
+			}
+		}
+		else if( href.indexOf( 'youtu.be' ) !== -1 )
+		{
+			startIndex = href.lastIndexOf( '/' );
+			
+			if( startIndex !== -1 ) {
+				startIndex += 1;
+				endIndex = href.indexOf( '?' );
+				
+				if( endIndex === -1 ) {
+					videoID = href.substring( startIndex );
+				}
+				else {
+					videoID = href.substring( startIndex, endIndex );
+				}
+			}
+		}
+
+		if( videoID.length !== 0 ) {
+			embedURL = 'http://www.youtube.com/embed/' + videoID + timeAnchor;
+		}
+	}
+	catch( e )
+	{
+		if( typeof console !== 'undefined' && typeof console.log === 'function' ) {
+			console.log( '(getYoutubeEmbedURL) ERROR:  ' + e.name + ':  ' + e.message );
+		}
+	}
+	finally
+	{
+		return embedURL;
+	}
+};
+
+
+ClikkitExtension.stompitMessage = function() {
+	var msg = 'You are about to leave this page and close clikkit.\n',
+		siteAddress,
+		slashIndex;
+
+	try {
+		siteAddress = $.clikkit.lastLink;
+		if( siteAddress ) {
+			slashIndex = siteAddress.indexOf( '/', 8 );
+			if( slashIndex > 0 ) {
+				siteAddress = siteAddress.substring( 0, slashIndex );
+			}
+
+			msg += 'If you did not intend to, then the page at:\n';
+			msg += siteAddress;
+			msg += '\nis probably trying to break out of the iframe.\n';
+			msg += "You're probably better off staying here.";
+		}
+		else {
+			msg = '';
+		}
+	}
+	catch( e ) {
+		if( typeof console !== 'undefined' && typeof console.log === 'function' ) {
+			console.log( '(stompitMessage) ERROR:  ' + e.name + ':  ' + e.message );
+		}
+	}
+
+	return msg;
+};
+/*global chrome, ClikkitExtension, loadClikkit:true */
+
+chrome.extension.sendRequest( {reqtype: 'options'}, loadClikkit );
+
+function loadClikkit( response ) {
+	var options = response.options;
+	if( options.noFrameDomains ) {
+		options.noFrameDomains = options.noFrameDomains.split( '\n' );
+	}
+
+	if( ClikkitExtension.$clikkitLinks && ClikkitExtension.$clikkitLinks.length ) {
+		ClikkitExtension.$clikkitLinks.clikkit( options );
+
+		ClikkitExtension.$clikkitLinks.bind( 'clikkitAddLink', ClikkitExtension.checkURL );
+
+		if( ClikkitExtension.highlightSourceLink ) {
+			ClikkitExtension.$clikkitLinks.bind( 'clikkitShowLink', ClikkitExtension.highlightSourceLink );
+		}
+
+		if( ClikkitExtension.unhighlightSourceLink ) {
+			ClikkitExtension.$clikkitLinks.bind( 'clikkitCloseLink', ClikkitExtension.unhighlightSourceLink );
+		}
+	}
+	else {
+		console.log( 'clikkit:  no links' );
+	}
+
+	if( ClikkitExtension.$otherLinks ) {
+		ClikkitExtension.$otherLinks.stompit( ClikkitExtension.stompitMessage );
+	}
+}
+/**
+ * @fileOverview <p>clikkit is a jQuery plug-in that provides framed browsing of page links.</p>
+ * <p>An onclick handler is set for all main links on the page that returns false to override the default action.
+ * When a link is clicked, the URL is analyzed - special cases are handled to open frame-unfriendly sites
+ * in a new window, and images sized to fit the frame (with onclick toggling of size between fit and original).</p>
+ *
+ * <p>Link queue - each time a link is clicked, it is enqueued.  An iframe is immediately created so the
+ * browser will start loading the content, but it is positioned out of the viewport. When the current
+ * link being displayed is closed, its iframe is destroyed and the next link in the queue is displayed.
+ * To display a new link, its iframe is appended to the contentPane and positioned so that it is visible.<br>
+ * The most recently enqueued link is available as $.clikkit.lastLink</p>
+ *
+ * <p>Images are handled specially - as with normal links, an iframe is created with the image URL as the source.
+ * This is necessary so that the browser will consider the link visited (which it won't do if we just load the
+ * image directly to an <img> element). When it is time to display an image, instead of making its iframe visible,
+ * we make the ImageFrame div visible and set the image 'src' attribute to the image's URL. (For some reason
+ * loading an image in an iframe disables the browser's built-in fit-to-window image resizing.)</p>
+ *
+ * <p>If the hoverIntent plug-in () is available, it will be used - when you hover over a clikkit link, the clikkit
+ * content pane will shrink and fade, increasing the visibility of the underlying page.  If the plug-in is not
+ * available, jQuery's hover event will be used.</p>
+ * @author Mangala Sadhu Sangeet Singh Khalsa
+ * @version 1.0
+ */
+
+/*global jQuery, init, clikkitHandler, shrink, expand, close, logError, TRACE, imgOnLoad, imgOnClick, imgSetSize, addLink, closeLink, closeImageFrame, setState */
+
+ 
+( function($) {
+
+var $container,
+	$contentPane,
+	links = [],
+	noFrameDomains = [ 'flickr.com', 'facebook.com', 'facebook.net', 'fbcdn.net', 'twitter.com' ];
+
+/**
+ * @private
+ */
+$.clikkit = {
+	isInitialized: false,
+	settings: {
+		DEBUG: false
+	}
+};
+
+
+/**
+ * @name clikkit
+ * @function
+ * @description Register the clikkit event handler for the 'click' event for the specified links.<br>
+ * clikkit can register event handlers to all elements in the jQuery object it is invoked on at the
+ * time of invocation, or it can use jQuery.delegate() to register a single event handler to a containing element.
+ * @example Apply clikkit to a collection of links:
+ * $( 'a' ).clikkit();
+ * @example Apply clikkit using delegate to links in the #main element:
+ * $( '#main' ).clikkit( 'a' );
+ * @param {String} [selector] jQuery selector for element(s) to apply clikkit to <br>
+ * If this parameter is supplied, clikkit will use jQuery.delegate().  The event handler will be registered with the
+ * jQuery object that clikkit is invoked on, and applied to the elements within it that are matched by 'selector'. <br>
+ * clikkit is designed for use with HTMLLinkElements, but should work with any element with href and text properties
+ * @param {Object} userConfig clikkit configuration
+ * @param {Number|String} [userConfig.expandedWidth=75%] The width of the clikkit pane
+ * @param {Number|String} [userConfig.shrunkenWidth=50%] The width of the clikkit pane when contracted
+ * @param {Number} [userConfig.shrunkenOpacity=0.6] The opacity of the clikkit pane when contracted - a value between 0 and 1
+ * @param {Number|String} [userConfig.closedWidth=2] The width of the clikkit pane when closed
+ * @param {Number} [userConfig.closedOpacity=0] The opacity of the clikkit pane when closed
+ * @param {Number} [userConfig.closeBarColor=hsl(345, 64%, 35%)] The background color of the Close bar
+ * @param {Number} [userConfig.zIndex=100] The z-index of the clikkit pane
+ * @param {Boolean} [userConfig.enableHotkeys=true] If true, hotkeys will be enabled: <br>
+ *     Escape or numpad minus:  close the currently displayed link <br>
+ *     Left arrow or numpad plus:  expand the clikkit pane (if it's currently contracted)
+ */
+$.fn.clikkit = function( selector, userConfig ) {
+	if ( !this.length ) {
+		return this;
+	}
+
+	var config = {
+		expandedWidth: '75%',
+		shrunkenWidth: '50%',
+		shrunkenOpacity: 0.6,
+		closedWidth: 2,
+		closedOpacity: 0,
+		closeBarColor: 'hsl(345, 64%, 35%)',
+		zIndex: 100,
+		enableHotkeys: true
+	};
+
+	try {
+		if( $.isPlainObject( selector ) ) {
+			userConfig = selector;
+			selector = '';
+		}
+
+		if( userConfig ) {
+			$.extend( config, userConfig );
+		}
+
+		$.clikkit.settings = config;
+
+		if( selector ) {
+			this.delegate( selector, 'click', clikkitHandler );
+
+			if( $.fn.hoverIntent ) {
+				this.hoverIntent({
+					delegateSelector: selector,
+					over: shrink,
+					out: $.noop
+				});
+			}
+			else {
+				this.delegate( selector, 'mouseover', shrink );
+			}
+		}
+		else {
+			this.click( clikkitHandler );
+
+			if( $.fn.hoverIntent ) {
+				this.hoverIntent( shrink, $.noop );
+			}
+			else {
+				this.mouseover( shrink );
+			}
+		}
+	}
+	catch( e ) {
+		logError( e, 'clikkit' );
+	}
+
+	return this;
+};
+
+
+function clikkitHandler( clickEvent ) {
+	// allow normal action for middle-click and ctrl+click
+	// shift+click is a special clikkit modifier - disable clikkit just for that click
+	if( clickEvent.button === 1 || clickEvent.ctrlKey || clickEvent.shiftKey ) {
+		return true;
+	}
+
+	clickEvent.preventDefault();
+	addLink( this );
+	return false;
+}
+
+
+function keyupHandler( keyEvent ) {
+	switch( keyEvent.which ) {
+		case 27: // escape
+		case 109: // numpad minus (-)
+			closeLink( keyEvent );
+			break;
+
+		case 37: // left arrow
+		case 107: // numpad plus (+)
+			if( $.clikkit.state === 'shrink' ) {
+				expand();
+			}
+			break;
+	}
+}
+
+
+function init() {
+	if( $.clikkit.isInitialized ) {
+TRACE( 'WARNING:  init() called, already initialized.' );
+		return;
+	}
+
+	var $row,
+		$borderCell,
+		$border,
+		$edgeCell,
+		$closeText;
+
+	if( $.clikkit.settings.enableHotkeys ) {
+		$(window).keyup( keyupHandler );
+	}
+
+	if( $.clikkit.settings.noFrameDomains && $.clikkit.settings.noFrameDomains.length > 0 ) {
+		noFrameDomains = noFrameDomains.concat( $.clikkit.settings.noFrameDomains );
+	}
+
+	// translucent left-border challenges:
+	// 1. div-inside-div:  inner div inherits translucence of outer div
+	// 2. div-next-to-div:  alignment is hell, pixels are all wrong!
+	// 3. table - it works!
+	$container = $( '<table id="clikkit"></table>' );
+	$container.css({
+		borderCollapse: 'collapse',
+		borderSpacing: 0,
+		opacity: $.clikkit.settings.closedOpacity,
+		position: 'fixed',
+		width: $.clikkit.settings.closedWidth,
+		height: $(window).height(),
+		top: 0,
+		right: 0,
+		zIndex: $.clikkit.settings.zIndex
+	});
+
+	$row = $( '<tr></tr>' );
+	$row.css( {
+		margin: 0,
+		padding: 0
+	} );
+	$container.append( $row );
+
+	$borderCell = $( '<td id="clikkitBorder"></td>' );
+	$borderCell.css( {
+		border: 'none',
+		padding: 0,
+		opacity: 0.42,
+		boxShadow: '-3px 0 5px #424242'
+	} );
+	$row.append( $borderCell );
+
+	$border = $( '<div></div>' );
+	$border.css( {
+		borderLeft: '1px solid hsl(0, 0%, 96%)',
+		borderRight: '1px solid hsl(0, 0%, 96%)',
+		margin: 0,
+		// border color - blueish #2F48D1
+		// border color - greyish #5B687A
+		background: '#5B687A',
+		width: '4px',
+		height: '100%'
+	} );
+	$borderCell.append( $border );
+
+	$edgeCell = $( '<td id="clikkitEdge"></td>' );
+	$edgeCell.css( {
+		opacity: 0.88,
+		fontFamily: 'verdana, arial, helvetica, sans-serif',
+		fontSize: 'small',
+		fontStyle: 'normal',
+		fontWeight: 'bold',
+		letterSpacing: '4px',
+		color: 'white',
+		background: $.clikkit.settings.closeBarColor,
+		cursor: 'pointer'
+	} );
+	$edgeCell.click( closeLink );
+	$row.append( $edgeCell );
+
+	$closeText = $( '<p id="clikkitCloseText">CLOSE</p>' );
+	$closeText.css( {
+		writingMode: 'tb-rl',
+		WebkitTransform: 'rotate(270deg)',
+		MozTransform: 'rotate(270deg)',
+		width: '14px'
+	} );
+	$edgeCell.append( $closeText );
+
+	$contentPane = $( '<td id="clikkitContent"></td>' );
+	$contentPane.css( {
+		width: '100%',
+		height: '100%',
+		background: '#E3E3E3',
+		verticalAlign: 'top'
+	} );
+	$contentPane.mouseenter( function() {
+		if( $.clikkit.state === 'shrink' ) {
+			expand();
+		}
+	} );
+	$row.append( $contentPane );
+
+	// put a dummy value in the div so that when it is first rendered, it has its normal height
+	$contentPane.URL = $( '<div>Tj</div>' );
+	$contentPane.URL.css( {
+		font: 'small verdana, arial, helvetica, sans-serif',
+		color: 'black',
+		backgroundColor: '#F5F5F5',
+		padding: '2px 2px 2px 3px',
+		border: '1px solid #B3B3B3',
+		borderRadius: '4px',
+		margin: '2px 2px 1px 2px'
+	} );
+	$contentPane.append( $contentPane.URL );
+
+	$contentPane.FrameCount = $( '<div>0</div>' );
+	$contentPane.FrameCount.css( {
+		display: 'block',
+		"float": 'left',
+		marginTop: '1px',
+		marginRight: '4px',
+		marginLeft: '2px',
+		fontSize: '10pt',
+		fontFamily: 'arial, helvetica, sans-serif',
+		fontWeight: 'bold',
+		textAlign: 'center',
+		border: '1px solid #B3B3B3',
+		width: '18px',
+		paddingTop: '1px',
+		backgroundImage: (function() {
+			if( $.browser.webkit || $.browser.safari ) {
+				return '-webkit-gradient(linear,left bottom,left top,color-stop(0.05, rgb(227,227,227)),color-stop(0.41, rgb(245,245,245)))';
+			}
+			else if( $.browser.mozilla ) {
+				return '-moz-linear-gradient(center bottom,rgb(227,227,227) 5%,rgb(245,245,245) 41%)';
+			}
+			else if( $.browser.opera ) {
+				return '-o-linear-gradient(center bottom,rgb(227,227,227) 5%,rgb(245,245,245) 41%)';
+			}
+			else {
+				return 'none';
+			}
+		}())
+	} );
+	$contentPane.append( $contentPane.FrameCount );
+
+	$contentPane.Title = $( '<div>Tj</div>' );
+	$contentPane.Title.css( {
+		fontFamily: 'verdana, arial, helvetica, sans-serif',
+		fontSize: 'medium',
+		color: '#222222',
+		padding: '2px 2px 2px 4px',
+		marginBottom: '1px'
+	} );
+	$contentPane.append( $contentPane.Title );
+
+	$contentPane.ImageFrame = $( '<div id="clikkitImagePane"></div>' );
+	$contentPane.ImageFrame.css( {
+		overflow: 'auto',
+		width: '100%',
+		height: 600,
+		borderTop: '1px solid #8B8B8B'
+	} );
+	$contentPane.ImageFrame.Image = $( new Image() );
+	$contentPane.ImageFrame.Image.click( imgOnClick );
+	$contentPane.ImageFrame.Image.load( imgOnLoad );
+	$contentPane.ImageFrame.append( $contentPane.ImageFrame.Image );
+	$contentPane.append( $contentPane.ImageFrame );
+
+	$(document.body).append( $container );
+	$contentPane.ImageFrame.hide();
+TRACE( 'container.height: ' + $container.height() + '; c.outer: ' + $container.outerHeight() + '; win: ' + $(window).height() );
+
+	$.clikkit.isInitialized = true;
+	$.clikkit.state = 'close';
+}
+
+
+/**
+ * @param {Object} link The clikkit link {domNode: <HTMLLinkElement>, $iframe: <$HTMLIFrameElement>} to display
+ */
+function showLink( link ) {
+	var eventResult,
+		frameHREF,
+		linkText,
+		dotIndex,
+		hrefEnd,
+		$linkNode = $(link.domNode),
+		showLinkEvent;
+
+	/**
+	 * @name clikkitShowLink
+	 * @event
+	 * @description Fires on an HTMLLinkElement before clikkit displays the link <br>
+	 * If an event handler returns a falsy value other than undefined, the link will not be displayed.
+	 * @example
+	 * $( 'a' ).bind( 'clikkitShowLink', myFunction )
+	 */
+	showLinkEvent = $.Event( 'clikkitShowLink' );
+	$linkNode.trigger( showLinkEvent );
+	eventResult = showLinkEvent.result;
+	if( showLinkEvent.isDefaultPrevented() || (!eventResult && eventResult !== undefined) ) {
+		return false;
+	}
+
+	frameHREF = $linkNode.data( 'clikkithref' );
+	linkText = $linkNode.data( 'clikkittext' );
+
+	$contentPane.URL.html( '<a href="' + frameHREF + '" target="_blank">' + frameHREF + '</a>' );
+	$contentPane.FrameCount.text( links.length );
+	$contentPane.Title.text( linkText );
+	$contentPane.Frame = link.$iframe;
+
+	dotIndex = frameHREF.lastIndexOf( '.' );
+	if( dotIndex !== -1 ) {
+		hrefEnd = frameHREF.substring( dotIndex + 1 ).toLowerCase();
+		if( hrefEnd === 'jpeg' || hrefEnd === 'jpg' || hrefEnd === 'png' || hrefEnd === 'gif' ) {
+			$contentPane.ImageFrame.height( $contentPane.Frame.innerHeight() - 1 ); // -1 to allow for 1px border at top
+			if( $contentPane.ImageFrame.outerHeight() > $contentPane.Frame.height() ) {
+				$contentPane.ImageFrame.height( $contentPane.ImageFrame.height() - ($contentPane.ImageFrame.outerHeight() - $contentPane.Frame.height()) );
+			}
+			$contentPane.ImageFrame.Image.attr( 'src', frameHREF );
+			$contentPane.ImageFrame.isActive = true;
+			$contentPane.ImageFrame.show();
+
+			// if the image is loaded and ready, set the size
+			if( $contentPane.ImageFrame.Image[0].complete && $contentPane.ImageFrame.Image[0].width > 0 ) {
+				if( $.clikkit.state === 'expand' ) {
+					imgSetSize.apply( $contentPane.ImageFrame.Image[0] );
+				}
+				else {
+					$container.one( 'clikkitexpand', function() { imgSetSize.apply( $contentPane.ImageFrame.Image[0] ); } );
+				}
+			}
+
+			// exit the function so we don't make the normal iframe visible
+			return;
+		}
+	}
+
+	$contentPane.append( link.$iframe );
+	$contentPane.Frame.css( 'position', 'relative' );
+	$contentPane.Frame.css( 'left', 0 );
+}
+
+
+function closeLink( event ) {
+	try {
+		var currentLink,
+			eventResult,
+			$linkNode,
+			closeLinkEvent;
+
+		currentLink = links.shift();
+		$linkNode = $( currentLink.domNode );
+		$linkNode.removeData( 'clikkithref' );
+		$linkNode.removeData( 'clikkittext' );
+
+		/**
+		 * @name clikkitCloseLink
+		 * @event
+		 * @description Fires on an HTMLLinkElement before clikkit closes the link <br>
+		 * If an event handler returns a falsy value other than undefined, the link will not be closed.
+		 * @example
+		 * $( 'a' ).bind( 'clikkitCloseLink', myFunction )
+		 */
+		closeLinkEvent = $.Event( 'clikkitCloseLink' );
+		$linkNode.trigger( closeLinkEvent );
+		eventResult = closeLinkEvent.result;
+		if( closeLinkEvent.isDefaultPrevented() || (!eventResult && eventResult !== undefined) ) {
+			return false;
+		}
+
+		$contentPane.URL.html( 'Tj' );
+		$contentPane.Title.html( 'Tj' );
+
+		if( $contentPane.ImageFrame.isActive ) {
+			closeImageFrame();
+		}
+
+		$contentPane.Frame.remove();
+	}
+	catch( e ) {
+		logError( e, 'closeLink' );
+	}
+
+	if( links.length > 0 ) {
+		showLink( links[0] );
+	}
+	else {
+		if( event && event.shiftKey ) {
+			closeImageFrame();
+		}
+
+		links = [];
+		close();
+		$.clikkit.lastLink = '';
+	}
+}
+
+
+/**
+ * @param {HTMLLinkElement} link The link to enqueue
+ */
+function addLink( link ) {
+	try {
+		var $link = $(link),
+			eventResult,
+			// if the frame height calculations are off, a correction value may be needed
+			frameHeightCorrection = 0,
+			frameCountHeight,
+			titleHeight,
+			frameHeight,
+			frameHREF,
+			addLinkEvent,
+			i;
+
+		/**
+		 * @name clikkitAddLink
+		 * @event
+		 * @description Fires on an HTMLLinkElement before clikkit enqueues the link. <br>
+		 * If an event handler returns a falsy value other than undefined, the link will not be enqueued. <br>
+		 * If the last event handler returns a link-like object (contains 'href' and 'text' properties),
+		 * that link will be enqueued instead of the original. This can be useful, for example, to change a
+		 * normal Youtube link to its embed equivalent.
+		 * @example
+		 * $( 'a' ).bind( 'clikkitAddLink', myFunction )
+		 */
+		addLinkEvent = $.Event( 'clikkitAddLink' );
+		$link.trigger( addLinkEvent );
+		eventResult = addLinkEvent.result;
+		if( addLinkEvent.isDefaultPrevented() || (!eventResult && eventResult !== undefined) ) {
+			return false;
+		}
+
+		frameHREF = link.href;
+		$link.data( 'clikkithref', link.href );
+		$link.data( 'clikkittext', link.text );
+
+		if( eventResult ) {
+			if( eventResult.href ) {
+				$link.data( 'clikkithref', eventResult.href );
+				frameHREF = eventResult.href;
+			}
+
+			if( eventResult.text ) {
+				$link.data( 'clikkittext', eventResult.text );
+			}
+		}
+
+		if( !$.clikkit.isInitialized ) {
+			init();
+		}
+
+		// handle sites that break out of frames - open them in a new window
+		for( i = 0; i < noFrameDomains.length; i++ ) {
+			if( frameHREF.indexOf( noFrameDomains[i] ) !== -1 ) {
+				window.open( frameHREF );
+				return false;
+			}
+		}
+
+		frameCountHeight = $contentPane.FrameCount.outerHeight();
+		titleHeight = $contentPane.Title.outerHeight();
+		if( frameCountHeight > titleHeight ) {
+			titleHeight = frameCountHeight;
+		}
+		frameHeight = $contentPane.innerHeight() - $contentPane.URL.outerHeight() - titleHeight + frameHeightCorrection;
+TRACE( 'frameHeight = ' + frameHeight + '[' + $contentPane.innerHeight() + ' - ' + $contentPane.URL.outerHeight() + ' - ' + titleHeight + ' + ' + frameHeightCorrection + ']' );
+
+		// store the originally clicked link's href value, even if an event handler for 'clikkitAddLink' changed it
+		// since this is primarily for tracing purposes to see what the last link clicked was
+		$.clikkit.lastLink = link.href;
+
+		// enqueue the link
+		links.push( { domNode: link,
+			$iframe: $( '<iframe src="' + frameHREF + '" width="100%" height="' + frameHeight + '" frameborder="0" scrolling="auto" style="position: absolute; left: -5000px; border-top: 1px solid #8B8B8B;"></iframe>' )
+		} );
+
+		// append the iframe to the doc body
+		$(document.body).append( links[links.length - 1].$iframe );
+
+		// if nothing is already being displayed, display current link
+		if( links.length === 1 ) {
+			showLink( links[0] );
+			expand();
+		}
+		// otherwise, update link count display
+		else {
+			$contentPane.FrameCount.text( links.length );
+		}
+	}
+	catch( e )
+	{
+		logError( e, 'addLink' );
+		window.open( link.href );
+	}
+}
+
+
+function closeImageFrame() {
+	$contentPane.ImageFrame.Image.attr( 'src', '' );
+	$contentPane.ImageFrame.Image[0].width = 320;
+	$contentPane.ImageFrame.Image[0].height = 240;
+	$contentPane.ImageFrame.Image[0].retryCount = 0;
+	$contentPane.ImageFrame.isActive = false;
+	$contentPane.ImageFrame.hide();
+}
+
+
+/**
+ * @name clikkitexpanding
+ * @event
+ * @description Fires on the clikkit pane, which has 'clikkit' as its ID, when it starts expanding <br>
+ * This is a non-bubbling event.
+ * @example
+ * $( '#clikkit' ).bind( 'clikkitexpanding', myFunction )
+ */
+
+/**
+ * @name clikkitexpand
+ * @event
+ * @description Fires on the clikkit pane, which has 'clikkit' as its ID, when it finishes expanding <br>
+ * This is a non-bubbling event.
+ * @example
+ * $( '#clikkit' ).bind( 'clikkitexpand', myFunction )
+ */
+
+/**
+ * Expand the clikkit pane and make it visible
+ */
+function expand() {
+	// only expand if current state is not 'expand'
+	if( $.clikkit.state !== 'expand' ) {
+		setState( 'expanding' );
+		$container.animate( { width: $.clikkit.settings.expandedWidth, opacity: 1 }, 200, function() {
+			setState( 'expand' );
+		});
+	}
+}
+
+
+/**
+ * @name clikkitshrinking
+ * @event
+ * @description Fires on the clikkit pane, which has 'clikkit' as its ID, when it starts shrinking <br>
+ * This is a non-bubbling event.
+ * @example
+ * $( '#clikkit' ).bind( 'clikkitshrinking', myFunction )
+ */
+
+/**
+ * @name clikkitshrink
+ * @event
+ * @description Fires on the clikkit pane, which has 'clikkit' as its ID, when it finishes shrinking <br>
+ * This is a non-bubbling event.
+ * @example
+ * $( '#clikkit' ).bind( 'clikkitshrink', myFunction )
+ */
+
+/**
+ * Shrink the clikkit pane and reduce its visibility
+ */
+function shrink() {
+	// only shrink if current state is 'expand'
+	if( $.clikkit.state === 'expand' ) {
+		setState( 'shrinking' );
+		$container.animate( { width: $.clikkit.settings.shrunkenWidth, opacity: $.clikkit.settings.shrunkenOpacity }, 275, function() {
+			setState( 'shrink' );
+		});
+	}
+}
+
+
+/**
+ * @name clikkitclose
+ * @event
+ * @description Fires on the clikkit pane, which has 'clikkit' as its ID, when it is closed <br>
+ * This is a non-bubbling event.
+ * @example
+ * $( '#clikkit' ).bind( 'clikkitclose', myFunction )
+ */
+
+/**
+ * Close and hide the clikkit pane
+ */
+function close() {
+	$container.width( $.clikkit.settings.closedWidth );
+	$container.css( 'opacity', $.clikkit.settings.closedOpacity );
+	setState( 'close' );
+}
+
+
+function setState( state ) {
+TRACE( 'state:  ' + state );
+	$.clikkit.state = state;
+	$container.triggerHandler( 'clikkit' + state );
+}
+
+
+function imgOnClick() {
+	if( this.width === this.naturalWidth ) {
+		this.width = this.fitWidth ;
+		this.height = this.fitHeight;
+	}
+	else {
+		this.width = this.naturalWidth;
+		this.height = this.naturalHeight;
+	}
+}
+
+
+function imgOnLoad() {
+	// seems like sometimes the onLoad fires before the image is really ready?
+	// if we can't get the dimensions, wait a bit and try again, if we still can't get them, open image in new window
+	if( this.width === 0 || this.height === 0 ) {
+		this.retryCount = this.retryCount === undefined ? 1 : this.retryCount + 1;
+		if( this.retryCount === 10 ) {
+			// can't get image dimensions, open in new window
+			window.open( this.src );
+			this.src = '';
+			this.width = 320;
+			this.height = 240;
+			this.retryCount = 0;
+			return;
+		}
+
+		setTimeout( imgOnLoad.apply( this ), 250 );
+		return;
+	}
+
+	if( this.naturalWidth === undefined ) {
+		this.naturalWidth = this.width;
+	}
+	
+	if( this.naturalHeight === undefined ) {
+		this.naturalHeight = this.height;
+	}
+TRACE( 'img loaded:  ' + this.src + '; w=' + this.width + '; h=' + this.height );
+
+	// showLink() sets '$contentPane.ImageFrame.isActive=true', then calls imgSetSize if the image is ready
+	// If isActive is already true during onLoad, that means the image frame was activated before the image
+	// finished loading and imgSetSize was not called, so call it now
+	if( $contentPane.ImageFrame.isActive && $contentPane.ImageFrame.Image[0] === this ) {
+TRACE( 'imgOnLoad->imgSetSize' );
+		if( $.clikkit.state === 'expand' ) {
+			imgSetSize.apply( this );
+		}
+		else {
+			var image = this;
+			$container.one( 'clikkitexpand', function() { imgSetSize.apply( image ); } );
+		}
+	}
+}
+
+
+function imgSetSize() {
+	var frameWidth,
+		frameHeight,
+		ratio;
+
+	frameWidth = $contentPane.ImageFrame.innerWidth();
+	this.fitWidth = frameWidth;
+
+	ratio = this.fitWidth / this.naturalWidth;
+	this.fitHeight = parseInt( this.naturalHeight * ratio, 10 );
+
+	frameHeight = $contentPane.ImageFrame.innerHeight();
+	if( this.fitHeight > frameHeight ) {
+		ratio = frameHeight / this.naturalHeight;
+		this.fitWidth = parseInt( this.naturalWidth * ratio, 10 );
+		this.fitHeight = frameHeight;
+	}
+
+	// HACK:  sanity checks
+	if( this.fitWidth < 10 ) {
+		this.fitWidth = 320;
+	}
+	if( this.fitHeight < 10 ) {
+		this.fitHeight = 240;
+	}
+TRACE( 'imgSetSize:  calculated fitWidth = ' + this.fitWidth + '; fitHeight = ' + this.fitHeight );
+
+	// if the width or height is larger than the frame, then resize to fit
+	if( this.naturalWidth > frameWidth || this.naturalHeight > frameHeight ) {
+		// but NOT if the width is less than the frame and the height is more than 1.5*width (don't shrink down long vertical images, like long rage comics)
+		if( this.naturalWidth < frameWidth && this.naturalHeight > (1.5 * this.naturalWidth) ) {
+			// we have to explicitly set the image to the size of the newly loaded image because it is re-using
+			// the old image element, but just setting a new value for 'src'
+			this.width = this.naturalWidth;
+			this.height = this.naturalHeight;
+		}
+		else {
+			this.width = this.fitWidth;
+			this.height = this.fitHeight;
+		}
+TRACE( 'imgSetSize:  w:' + this.naturalWidth + '->' + this.fitWidth + '; h:' + this.naturalHeight + '->' + this.fitHeight );
+	}
+	else {
+		// we have to explicitly set the image to the size of the newly loaded image because it is re-using
+		// the old image element, but just setting a new value for 'src'
+		this.width = this.naturalWidth;
+		this.height = this.naturalHeight;
+	}
+}
+
+
+function TRACE( msg ) {
+	if( $.clikkit.settings.DEBUG && console ) {
+		console.log( '(clikkit)' + msg );
+	}
+}
+
+
+function logError( e, source ) {
+	var msg;
+
+	if( source ) {
+		source = ':' + source;
+	} else {
+		source = '';
+	}
+
+	msg = 'ERROR(clikkit' + source + '):  ' + e.name + ':  ' + e.message;
+
+	if( console ) {
+		console.log( msg );
+	} else {
+		alert( msg );
+	}
+}
+
+}( jQuery ));

lib/hoverIntent.js

+/**
+* hoverIntent is similar to jQuery's built-in "hover" function except that
+* instead of firing the onMouseOver event immediately, hoverIntent checks
+* to see if the user's mouse has slowed down (beneath the sensitivity
+* threshold) before firing the onMouseOver event.
+* 
+* hoverIntent r6 // 2011.02.26 // jQuery 1.5.1+
+* <http://cherne.net/brian/resources/jquery.hoverIntent.html>
+* patched: 2011.06.27 Mangala SSS Khalsa
+* added jQuery.delegate support
+* 
+* hoverIntent is currently available for use in all personal or commercial 
+* projects under both MIT and GPL licenses. This means that you can choose 
+* the license that best suits your project, and use it accordingly.
+* 
+* // basic usage (just like .hover) receives onMouseOver and onMouseOut functions
+* $("ul li").hoverIntent( showNav , hideNav );
+* 
+* // advanced usage receives configuration object only
+* $("ul li").hoverIntent({
+*	sensitivity: 7, // number = sensitivity threshold (must be 1 or higher)
+*	interval: 100,   // number = milliseconds of polling interval
+*	over: showNav,  // function = onMouseOver callback (required)
+*	timeout: 0,   // number = milliseconds delay before onMouseOut function call
+*	out: hideNav    // function = onMouseOut callback (required)
+* });
+* 
+* @param  f  onMouseOver function || An object with configuration options
+* @param  g  onMouseOut function  || Nothing (use configuration options object)
+* @author    Brian Cherne brian(at)cherne(dot)net
+*/
+(function($) {
+	$.fn.hoverIntent = function(f,g) {
+		// default configuration options
+		var cfg = {
+			sensitivity: 7,
+			interval: 100,
+			timeout: 0
+		};
+		// override configuration options with user supplied object
+		cfg = $.extend(cfg, g ? { over: f, out: g } : f );
+
+		// instantiate variables
+		// cX, cY = current X and Y position of mouse, updated by mousemove event
+		// pX, pY = previous X and Y position of mouse, set by mouseover and polling interval
+		var cX, cY, pX, pY;
+
+		// A private function for getting mouse position
+		var track = function(ev) {
+			cX = ev.pageX;
+			cY = ev.pageY;
+		};
+
+		// A private function for comparing current and previous mouse position
+		var compare = function(ev,ob) {
+			ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
+			// compare mouse positions to see if they've crossed the threshold
+			if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) {
+				$(ob).unbind("mousemove",track);
+				// set hoverIntent state to true (so mouseOut can be called)
+				ob.hoverIntent_s = 1;
+				return cfg.over.apply(ob,[ev]);
+			} else {
+				// set previous coordinates for next time
+				pX = cX; pY = cY;
+				// use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
+				ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval );
+			}
+		};
+
+		// A private function for delaying the mouseOut function
+		var delay = function(ev,ob) {
+			ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
+			ob.hoverIntent_s = 0;
+			return cfg.out.apply(ob,[ev]);
+		};
+
+		// A private function for handling mouse 'hovering'
+		var handleHover = function(e) {
+			// copy objects to be passed into t (required for event object to be passed in IE)
+			var ev = jQuery.extend({},e);
+			var ob = this;
+
+			// cancel hoverIntent timer if it exists
+			if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }
+
+			// if e.type == "mouseenter"
+			if (e.type == "mouseenter") {
+				// set "previous" X and Y position based on initial entry point
+				pX = ev.pageX; pY = ev.pageY;
+				// update "current" X and Y position based on mousemove
+				$(ob).bind("mousemove",track);
+				// start polling interval (self-calling timeout) to compare mouse coordinates over time
+				if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}
+
+			// else e.type == "mouseleave"
+			} else {
+				// unbind expensive mousemove event
+				$(ob).unbind("mousemove",track);
+				// if hoverIntent state is true, then call the mouseOut function after the specified delay
+				if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
+			}
+		};
+
+		// bind the function to the two event listeners
+		if( cfg.delegateSelector ) {
+			$( this ).delegate( cfg.delegateSelector, 'mouseenter', handleHover );
+			$( this ).delegate( cfg.delegateSelector, 'mouseleave', handleHover );
+			return this;
+		}
+		else {
+			return this.bind('mouseenter',handleHover).bind('mouseleave',handleHover);
+		}
+	};
+})(jQuery);
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.