• 0

Javascript problem


Question

My menu (lavalamp) is supposed to keep the marker on the topic you're under.

If you click About the Author, it opens that page but the marker should stay

under About the Author. Instead, it's going back to Home.

If you click your browser's back button, the marker is over the area it's

supposed to be... but you're not on that topic anymore.

Anyone else using Lavalamp and having this issue?

http://www.richardmccord.com

/**
 * jquery.LavaLamp v1.3.4b2 - light up your menu with fluid, jQuery powered animations.
 *
 * Requires jQuery v1.2.3 or better from http://jquery.com
 * Tested on jQuery 1.4, 1.3.2 and 1.2.6
 *
 * http://nixboxdesigns.com/projects/jquery-lavalamp/
 *
 * Copyright (c) 2008, 2009, 2010 Jolyon Terwilliger, jolyon@nixbox.com
 * Source code Copyright (c) 2008, 2009, 2010
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 * credits to Guillermo Rauch and Ganeshji Marwaha (gmarwaha.com) for previous editions
 *
 * Version: 1.0 - adapted for jQuery 1.2.x series
 * Version: 1.1 - added linum parameter
 * Version: 1.2 - modified to support vertical resizing of elements
 * Version: 1.3 - enhanced automatic <li> item hi-lighting - will attempt to
 *					lock onto li > a element with href closest to selected
 *					window.location
 *			 	- click always returns 'true' by default, for standard link follow through.
 *
 * Version: 1.3.1 - verified for use with jQuery 1.3 - should still work with 1.2.x series
 *				- changed linum parameter to startItem for clarity
 *				- improved slide-in accuracy for .back elements with borders
 *				- changed .current class to .selectedLava for clarity and improved
 *					support
 *				- appended 'Lava' to all internal class names to avoid conflicts
 *				- fixed bug applying selectedLava class to elements with matching
 *					location.hash
 *				- now supports jquery.compat plugin for cross-library support
 *				- performance improvements
 *				- added new options:
 *				autoReturn: true - if set to false, hover will not return to last selected
 *									item upon list mouseout.
 *				returnDelay: 0 - if set, will delay auto-return feature specified # of
 *									milliseconds.
 *				setOnClick: true - if set to false, hover will return to default element
 *									regardless of click event.
 *				homeLeft: 0, homeTop: 0 - if either set to non zero value, absolute
 *									positioned li element with class .homeLava is 
 *									prepended to list for homing feature.
 *				homeWidth: 0, homeHeight: 0 - if set, are used for creation of li.homeLava
 *									element.
 *				returnHome: false - if set along with homeLeft or homeTop, lavalamp hover
 *									will always return to li.home after click.
 *
 * Version: 1.3.2 - fixed: stray $ references inside the plugin to work with
 *					jQuery.noConflict() properly - thanks Colin.
 *
 * Version: 1.3.3 - fixed: added closure with null passed argument for move() command in
 * 					returnDelay to fix errors some were seeing - thanks to Michel and 
 *					Richard for noticing this.
 *
 *					fixed: changed mouseover/out events to mouseenter/leave to fix jerky
 *					animation problem when using excessive margins instead of padding. 
 *					Thanks to Thomas for the solution and Chris for demonstrating the problem.
 *					* requires jQuery 1.3 or better
 *
 *					enhanced: added 'noLava' class detection to prevent LavaLamp effect
 *					application to LI elements with this class. This feature allows you to
 *					create submenus - for details, see examples at
 *					http://nixboxdesigns.com/demos/jquery-lavalamp-demos.html
 *
 *					enhanced: modified to better automatically find default location for 
 *					relative links. Thanks to Harold for testing and finding this bug.
 *
 * Version: 1.3.4 - major overhaul on practically everything:
 *					enhanced: added target and autoResize options - see below.
 *					enhanced: better automatic default item selection and URI resolution,
 *					better support for returnHome and returnDelay, refined internal variable
 *					usage and test to be as lean as possible
 *					fixed: backLava hover element now exactly covers the destination LI dimensions.
 *					fixed: changed use of mouseleave/mouseenter to bind events so will work with
 *							jQuery 1.2.2 onward.
 *					enhanced: behaves more like a plugin should and now automatically adds proper
 * 							position CSS tags to the backLava element and parent container
 *							if absent. 
 *
 * Examples and usage:
 *
 * The HTML markup used to build the menu can be as simple as...
 *
 * 	<ul class="lavaLamp">
 * 	<li><a href="#">Phone Home</a></li>
 * 	<li><a href="#">Make Contact</a></li>
 * 	<li><a href="#">Board Ship</a></li>
 * 	<li><a href="#">Fly to Venus</a></li>
 * 	</ul>
 *
 * Additional Styles must be added to make the LavaLamp perform properly, to wit:
 *
 * <style>
 * ul.lavaLamp {
 * padding:5px; // use some minimal padding to account for sloppy mouse movements
 * }
 * ul.lavaLamp li.backLava {
 * z-index:3; // must be less than z-index of A tags within the LI elements
 * }
 * ul.lavaLamp li a {
 * display:block; // helps with positioning the link within the LI element
 * z-index:10; 	// or must be higher than li.backLava z-index
 * }
 * </style>
 *
 * Once you have included the basic styles above, you will need to include 
 * the jQuery library, easing plugin (optional) and the this LavaLamp plugin.
 *
 * jQuery Easing Library 1.3 available here: http://plugins.jquery.com/project/Easing
 * 
 * Example LavaLamp initializing statement:
 * $(function() { $("ul.lavaLamp").lavaLamp({ fx: "easeOutBack", speed: 700}) });
 * finds all UL elements in the document with the class of 'lavaLamp' and attaches the 
 * LavaLamp plugin using an easing library fx of OutBack and an animate speed of 
 * 700 milliseconds or 7/10ths of a second.
 *
 *
 * List of Parameters
 *
 * @param target - default: 'li' 
 * valid selector for target elements to receive hover effect
 *
 * Example:
 * jQuery("div#article").lavaLamp({ target:'p' });
 * assigns all p elements under div#article to receive lavaLamp hover events
 *
 * @param fx - default: 'swing'
 * selects the easing formula for the animation - requires the jQuery Easing library 
 * to be loaded for additional effects
 * 
 * Example:
 * jQuery("ul.lavaLamp").lavaLamp({ fx: "easeOutElastic" });
 * animates the backLava element using the OutElastic formula
 * 
 * @param speed - default: 500
 * sets animation speed in milliseconds
 * 
 * Example:
 * jQuery("ul.lavaLamp").lavaLamp({ speed: 1000 });
 * sets the animation speed to one second.
 * 
 * @param click - default: function() { return true; }
 * Callback to be executed when the menu item is clicked. The 'event' object and source target
 * DOM element will be passed in as arguments so you can use them in your function.
 * 
 * Example:
 * jQuery("ul.lavaLamp").lavaLamp({ click: function(event, menuItem) {
 *		alert(event+el);
 *		return false;
 * } });
 *
 * causes the browser to display an alert message of the variables passed and 
 * return false aborts any other click events on child items, including not 
 * following any links contained within the target
 *
 * @param startItem - default: 'no'
 * specifies the number target element as default, starting with 0 for the first element
 * Used to manually set the default lavaLamp hi-light on load.
 *
 * Example:
 * jQuery("ul.lavaLamp").lavaLamp({ startItem: 2 });
 * selects the third element in the list as default location for backLava
 *
 * @param autoReturn - default: true
 * defines whether the backLava hover should return to the last selectedLava element
 * upon mouseleave.
 *
 * Example:
 * jQuery("ul.lavaLamp").lavaLamp({ autoReturn: false });
 * turns off the autoReturn feature - backLava element will stay on the last element
 * that you hovered over.
 *
 * @param returnDelay - default: 0
 * how many milliseconds to wait before returning the backLava element to the last
 * selected element. Only works if autoReturn is set to true (default setting)
 *
 * Example:
 * jQuery("ul.lavaLamp").lavaLamp({ returnDelay: 1000 });
 * waits one second after mouseleave event before returning to the last selected
 * element.
 *
 * @param setOnClick - default: true
 * defines whether a clicked element should receive the selectLava class and become the
 * most recently selected element
 *
 * Example:
 * jQuery("ul.lavaLamp").lavaLamp({ setOnClick:false });
 * disables selecting of elements once clicked - after you leave the parent list element
 * the backLava will return to the original default element the page was loaded with.
 *
 * @param homeTop - default: 0
 * @param homeLeft - default: 0
 * @param homeHeight - default: 0
 * @param homeWidth - default: 0
 * allows you to define an independent 'home' element where the backLava defaults to or can
 * be sent to. This can be used to define a unique starting and/or resting place for the 
 * backLava upon leaving the parent element.
 *
 * Example:
 * jQuery("ul.lavaLamp").lavaLamp({ homeTop:-100, homeLeft:0, homeHeight:20, homeWidth:600 });
 * creates a home element 100 pixels above the parent container with a height of 20px and
 * width of 600px. If the parent element has CSS of overflow:hidden, this can provide
 * an interesting fly-in effect
 *
 * @param returnHome - default:false
 * adjusts behavior of the backLava element when the mouse leaves the parent container. 
 * the default behavior of 'false' causes the backLava element to stay on the active menu 
 * items after it is first triggered. this feature respects the returnDelay parameter, if set.
 * this feature overrides the autoReturn parameter.
 *
 * Example:
 * jQuery("ul.lavaLamp").lavaLamp({ returnHome:true });
 * causes the backLava element to always return to the homeLava position after 
 * mouse leaves the parent container. this can be manually triggered by running 
 * the command jQuery("ul.lavaLamp").mouseover();
 *
 * @param autoResize - default:false
 * triggers the selectedLava mouseenter event when the window is resized 
 * setting autoResize to true causes the backLava element to reposition and change dimensions
 * if the resizing the screen changes the shape of the lavaLamp. Best used with the
 * target option. Default is false for efficiency as this feature is new and seldom used.
 *
 * Example:
 * jQuery('div#articles').lavaLamp({target:'p',autoSize:true});
 * causes the backLava element to resize and reposition to the p.selectedLava position 
 * and dimensions when the window resizes.
 *
 */

//console.log();
(function(jQuery) {
jQuery.fn.lavaLamp = function(o) {
	o = jQuery.extend({
				target: 'li', 
				fx: 'swing',
				speed: 500, 
				click: function(){return true}, 
				startItem: '',
				autoReturn: true,
				returnDelay: 0,
				setOnClick: true,
				homeTop:0,
				homeLeft:0,
				homeWidth:0,
				homeHeight:0,
				returnHome:false,
				autoResize:false
				}, 
			o || {});

	// parseInt for easy mathing
	function getInt(arg) {
		var myint = parseInt(arg);
		return (isNaN(myint)? 0: myint);
	}

	if (o.autoResize)
		jQuery(window).resize(function(){
			jQuery(o.target+'.selectedLava').trigger('mouseenter');
		});

	return this.each(function() {
		// ensures parent UL or OL element has some positioning
		if (jQuery(this).css('position')=='static')
			jQuery(this).css('position','relative');

		// create homeLava element if origin dimensions set
		if (o.homeTop || o.homeLeft) { 
			var $home = jQuery('<'+o.target+' class="homeLava"></'+o.target+'>').css({ left:o.homeLeft, top:o.homeTop, width:o.homeWidth, height:o.homeHeight, position:'absolute' });
			jQuery(this).prepend($home);
		}

		var path = location.pathname + location.search + location.hash, $selected, $back, $lt = jQuery(o.target+'[class!=noLava]', this), delayTimer, bx=0, by=0;

		// start $selected default with CSS class 'selectedLava'
		$selected = jQuery(o.target+'.selectedLava', this);

		// override $selected if startItem is set
		if (o.startItem != '')
			$selected = $lt.eq(o.startItem);

		// default to $home element
		if ((o.homeTop || o.homeLeft) && $selected.length<1)
			$selected = $home;

		// loop through all the target element a href tags and
		// the longest href to match the location path is deemed the most 
		// accurate and selected as default
		if ($selected.length<1) {
			var pathmatch_len=0, $pathel;

			$lt.each(function(){ 
				var thishref = $('a:first',this).attr('href');
				//console.log(thishref+' size:'+thishref.length);
				if (path.indexOf(thishref)>-1 && thishref.length > pathmatch_len )
				{
					$pathel = $(this);
					pathmatch_len = thishref.length;
				}

			});
			if (pathmatch_len>0) {
				//console.log('foundch:'+$('a:first',$pathel).attr('href'));
				$selected = $pathel;
			}
			//else 
				//console.log('noch!');
		}

		// if still no matches, default to the first element
		if ( $selected.length<1 )
			$selected = $lt.eq(0);

		// make sure we only have one element as $selected and apply selectedLava class
		$selected = jQuery($selected.eq(0).addClass('selectedLava

		// add mouseover event for every sub element
		$lt.bind('mouseenterunction() {
			//console.log('mouseenter			// help backLava behave if returnDelay is set
			if(delayTimer) {clearTimeout(delayTimer);delayTimer=null;}
			move(jQuery(this));
		}).click(function(e) {
			if (o.setOnClick) {
				$selected.removeClass('selectedLava');
				$selected = jQuery(this).addClass('selectedLava');
			}
			return o.click.apply(this, [e, this]);
		});

		// creates and adds to the container a backLava element with absolute positioning
		$back = jQuery('<li class="backLava"><div class="leftLava"></div><div class="bottomLava"></div><div class="cornerLava"></div></li>').css('position','absolute').prependTo(this);

		// setting css height and width actually sets the innerHeight and innerWidth, so
		// compute border and padding differences on styled backLava element to fit them in also.
		bx = getInt($back.css('borderLeftWidth'))+getInt($back.css('borderRightWidth'))+getInt($back.css('paddingLeft'))+getInt($back.css('paddingRightback.cssback.css('borderTopWidth'))+getInt($back.css('borderBottomWidth'))+getInt($back.css('paddingTop'))+getInt($back.css('paddingBottomposition for the lavalamp hover element: .back
		if (o.homeTop || o.homeLeft)
			$back.css({ left:o.homeLeft, top:o.homeTop, width:o.homeWidth, height:o.homeHeight });
		else
		{
			$back.css({ left: $selected.position().left, top: $selected.position().top, width: $selected.outerWidth()-bx, height: $selected.outerHeight()-by }); 
		}

		// after we leave the container element, move back to default/last clicked element
		jQuery(this).bind('mouseleave', function() {
			//console.log('mouseleave			var $returnEl = null;
			if (o.returnHome)
				$returnEl = $home;
			else if (!o.autoReturn)
				return true;

			if (o.returnDelay) {
				if(delayTimer) clearTimeout(delayTimer);
				delayTimer = setTimeout(function(){move($returnEl);},o.returnDelay);
			}
			else {
				move($returnEl);
			}
			return true;
		});

		function move($el) {
			if (!$el) $el = $selected;

			$back.stop()
			.animate({
				left: $el.position().left,
				top: $el.position().top,
				width: $el.outerWidth()-bx,
				height: $el.outerHeight()-by
			}, o.speed, o.fx);
		};
	});

};
})(jQuery);

Link to comment
https://www.neowin.net/forum/topic/934566-javascript-problem/
Share on other sites

6 answers to this question

Recommended Posts

  • 0
  On 03/09/2010 at 13:40, McCordRm said:

Ack, nothing?

I'm not a JavaScript developer, but I would bet that your issue is the initialization of your start location.

I bet you need something like this to start up at the About the Author tab/option:

jQuery("ul.lavaLamp").lavaLamp({ startItem: 3 });

Assuming the name is accurate.

  • 0

I can't see your PHP but it doesn't look like a start position is being pushed through to the javascript code.

This is a Wordpress blog right?

If it isn't a Wordpress blog, swap out (is_page('pagename')) for ($_GET['page'] == "pagename") or whatever code snippet you're using to determine what page is loaded.

You will need to set up something like this:

<?php
if(is_home()) {$lavastart = 1;} else
if(is_page('Designs')) {$lavastart = 2;} else
if(is_page('Opinions')) {$lavastart = 3;} else
if(is_page('Biography')) {$lavastart = 4;} else 
if(is_page('Credits')) {$lavastart = 5;} else
{$lavastart = 1;}
?>

And then in your javascript:

(this assumes that the syntax above is correct - startItem: 3)

$(function() {
   $('ul#menu').lavaLamp({ startItem: <?php echo $lavastart; ?>});
}); 

If you want to set up multiple pages to show the indicator on the same spot (ie. Designs & My Game show Designs highlighted) you will need to set up some ors in your ifs, like so:

...
if(is_page('Designs') or is_page('My Game') or is_page('OS')) {$lavastart = 2;}
...

Disclaimer: This is completely untested and I only just woke up. It's entirely possible there may be syntax errors or missing brackets. Sorry :)

  • 0

This isn't a Wordpress site. The menu structure is:

<div id="menu-box">
<ul id="menu">
&lt;li&gt;&lt;a href="http://www.richardmccord.com/">Home</a></li>;li&gt;&lt;a href="http://www.richardmccord.com/Designs.php">Designs</a></li>;li&gt;&lt;a href="http://www.richardmccord.com/Opinions.php">Opinions</a></li>;li&gt;&lt;a href="http://www.richardmccord.com/index.php?page=Biography">Aboutauthor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.richardmccord.com/index.php?page=Credits">Credits</a></li>;/ul&gt;
&lt;/div&gt;

The Javascript shown in my first post applied the Lavalamp to everything between the UL Menu tag.

  • 0

Alright. So how did it go? Was there some part of my post you didn't understand?

As I mentioned, simply replace the wordpress functions with whatever you're using to determine which page is loaded. Based on your markup above, I'm guessing it's $_GET['page']?

Actually now that I think about it a switch statement would probably be a better method of doing it.

&lt;?php
switch ($_GET['page']) {
    case "designs": $lavastart = 2; break;
    case "opinions": $lavastart = 3; break;
   ..etc...
    default: $lavastart = 1;
}
?&gt;

Then replace your jquery script with this:

$(function() {
   $('ul#menu').lavaLamp({ startItem: &lt;?php echo $lavastart; ?&gt;});
}); 

Yeah. That should work.

This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Posts

    • Microsoft 365 Word gets SharePoint eSignature, now you can ditch third-party signing tools by Paul Hill Microsoft has just announced that it will be rolling out an extremely convenient feature for Microsoft 365 customers who use Word throughout this year. The Redmond giant said that you’ll now be able to use SharePoint’s native eSignature service directly in Microsoft Word. The new feature allows customers to request electronic signatures without converting the documents to a PDF or leaving the Word interface, significantly speeding up workflows. Microsoft’s integration of eSignatures also allows you to create eSignature templates which will speed up document approvals, eliminate physical signing steps, and help with compliance and security in the Microsoft 365 environment. This change has the potential to significantly improve the quality-of-life for those in work finding themselves adding lots of signatures to documents as they will no longer have to export PDFs from Word and apply the signature outside of Word. It’s also key to point out that this feature is integrated natively and is not an extension. The move is quite clever from Microsoft, if businesses were using third-party tools to sign their documents, they would no longer need to use these as it’s easier to do it in Word. Not only does it reduce reliance on other tools, it also makes Microsoft’s products more competitive against other office suites such as Google Workspace. Streamlined, secure, and compliant The new eSignature feature is tightly integrated into Word. It lets you insert signature fields seamlessly into documents and request other people’s signatures, all while remaining in Word. The eSignature feature can be accessed in Word by going to the Insert ribbon. When you send a signature request to someone from Word, the recipient will get an automatically generated PDF copy of the Word document to sign. The signed PDF will then be kept in the same SharePoint location as the original Word file. To ensure end-to-end security and compliance, the document never leaves the Microsoft 365 trust boundary. For anyone with a repetitive signing process, this integration allows you to turn Word documents into eSignature templates so they can be reused. Another feature that Microsoft has built in is audit trail and notifications. Both the senders and signers will get email notifications throughout the entire signing process. Additionally, you can view the activity history (audit trail) in the signed PDF to check who signed it and when. Finally, Microsoft said that administrators will be able to control how the feature is used in Word throughout the organization. They can decide to enable it for specific users via an Office group policy or limit it to particular SharePoint sites. The company said that SharePoint eSignature also lets admins log activities in the Purview Audit log. A key security measure included by Microsoft, which was mentioned above, was the Microsoft 365 trust boundary. By keeping documents in this boundary, Microsoft ensures that all organizations can use this feature without worry. The inclusion of automatic PDF creation is all a huge benefit to users as it will cut out the step of manual PDF creation. While creating a PDF isn’t complicated, it can be time consuming. The eSignature feature looks like a win-win-win for organizations that rely on digital signatures. Not only does it speed things along and remain secure, but it’s also packed with features like tracking, making it really useful and comprehensive. When and how your organization gets it SharePoint eSignature has started rolling out to Word on the M365 Beta and Current Channels in the United States, Canada, the United Kingdom, Europe, and Australia-Pacific. This phase of the rollout is expected to be completed by early July. People in the rest of the world will also be gaining this time-saving feature but it will not reach everyone right away, though Microsoft promises to reach everybody by the end of the year. To use the feature, it will need to be enabled by administrators. If you’re an admin who needs to enable this, just go to the M365 Admin Center and enable SharePoint eSignature, ensuring the Word checkbox is selected. Once the service is enabled, apply the “Allow the use of SharePoint eSignature for Microsoft Word” policy. The policy can be enabled via Intune, Group Policy manager, or the Cloud Policy service for Microsoft 365 Assuming the admins have given permission to use the feature, users will be able to access SharePoint eSignatures on Word Desktop using the Microsoft 365 Current Channel or Beta Channel. The main caveats include that the rollout is phased, so you might not get it right away, and it requires IT admins to enable the feature - in which case, it may never get enabled at all. Overall, this feature stands to benefit users who sign documents a lot as it can save huge amounts of time cumulatively. It’s also good for Microsoft who increase organizations’ dependence on Word.
    • It's always good to have an option to secure your stuff to another medium. I did that with DVD/CD collection, and run my own media server now. It's more convenient that way and no need for separate players anymore.
    • Google Search AI Mode gets support for data visualization and custom charts by Aditya Tiwari Google announced it is rolling out support for data visualizations and graphs for finance-related queries in Google Search's AI Mode. Introduced last month at the Google I/O 2025 keynote, the feature lets you analyze complex datasets and create custom charts simply using natural language prompts. The updated AI Mode lets you compare and analyze information over a specific period, Google explained. It generates interactive graphs and provides a comprehensive explanation for your questions. AI Mode utilizes Gemini's multimodal capabilities and multi-step reasoning approach to comprehend the question's intent while accessing historical and real-time information relevant to the question. For instance, instead of manually researching individual companies and their stock prices, you can use AI Mode to compare the stock performance of different companies for a specific year. Once the graph is generated, you can choose the desired time period using the mouse cursor and ask follow-up questions based on the data presented. These new data visualizations for finance queries are available to users who have enabled the AI Mode experiment in Labs. AI Mode was introduced earlier this year as an experimental feature in the US. The feature is an upgraded version of AI Overviews, and Google closely worked with AI power users through the initial development process. It uses the “query fan-out” technique to perform multiple related searches across subtopics and different data sources, then combines them to come up with a comprehensive response. Google updated AI Mode last month to use a custom version of the latest Gemini 2.5 model. It added several new features, including Deep Search, live capabilities, agentic capabilities of Project Mariner, a new shopping experience, and the ability to add personal context by linking Google apps. The search giant is planning to turn AI Mode into its bread and butter. It has begun testing ads for the feature, which will appear below and be integrated into AI Mode responses where relevant.
    • Guys, you should find another way to promote your deals... It's the third article in the last months that promote this deal for an upgrade from 10. Considering that upgrade from 10 to 11 is free it's a total non-sense.
    • Store should be a shrine of useful applications, vetted and verified. Easily sorted by publisher. Windows should start with not much installed and have things as options in the store. Not the wild west mess that it is. You could delete 95%+ of the crap on there and no one would notice. They need to add a better UI to the updates, it's awful right now.
  • Recent Achievements

    • Week One Done
      luxoxfurniture earned a badge
      Week One Done
    • First Post
      Uranus_enjoyer earned a badge
      First Post
    • Week One Done
      Uranus_enjoyer earned a badge
      Week One Done
    • Week One Done
      jfam earned a badge
      Week One Done
    • First Post
      survivor303 earned a badge
      First Post
  • Popular Contributors

    1. 1
      +primortal
      432
    2. 2
      +FloatingFatMan
      239
    3. 3
      snowy owl
      213
    4. 4
      ATLien_0
      211
    5. 5
      Xenon
      157
  • Tell a friend

    Love Neowin? Tell a friend!