Webkit phantom float bug

I recently found a strange webkit bug when I used a negative top margin to position a floated element.

I float an element to the left and gives the outer wrapper element a top margin of ex. 100px. Then I give the inner child wrapper a negative margin-top of 100px to push the floated element down. In most modern browsers this works, but in Chrome and Safari we can see a hole where the floated element would have been without the negative margins. The floated element further down looks fine.

[example] (test with a webkit browser)

The “fix” is a strange one. I insert an empty floated div before the inner child wrapper and the phantom float disappears.

[example]

Posted in CSS | Leave a comment

Silverlight css3 transform bug

Strange bug with Silverlight.

I have created a slideshow using the css transform property and translateX to move between the content items. If Silverlight is initialised without windowless = true, it disappears.

To show Silverlight after applying a css transform I had to use:

<param name="windowless" value="true" />

Hope this helps if anyone encounters the same bug.

UPDATE: Got the same problem with disqus. Applied position relative to disqus container and the problem is “fixed”

Posted in CSS | Leave a comment

MNO Mobil – Vi girer opp

Nå blir det full fart på mobil og brett, og vi trenger folk!
Vi er på jakt etter 4 personer:

  • Direktør for mobile platformer
  • Produsent for mobile platformer
  • Klientutviklere mobile platformer (2 personer)

Vil du bli med på den mest spennende satsningen fra Media Norge Digital, så sleng inn en søknad nå.

Lurer du på hvordan det er å jobbe her eller vil du vite litt om hvilke teknologier vi driver med, ta kontakt med meg på twitter (@andrefoynb).

Andre spørsmål om stillingen kan tas til konserndirektør i Media Norge: Stig A. Waagbø

Her er stillingstekstene:



Posted in Uncategorized | Leave a comment

HackDay januar 2011 ferdig!

Etter å ha sovet godt ut etter 24 timers koding og en fantastisk bra gjennomført presentasjon, legger vi nå ut litt bilder og info.

Arrangementet kan oppsumeres slik: mange spennende prosjekter, trøtte folk, gaming, Cola/Burn og bordtennis.
Og til slutt sms-avstemning for beste prosjekt.
Her er listen over prosjekter:

Og vinneren ble: “Full fart til Hovden”. Et prosjekt laget av Atle Brandt (http://twitter.com/atleb), medieutvikler fra FVN.no. Som han skriver på sin twitter status i løpet av natten: Google Maps / Google Elevation API med charts / Street view/ html5 Video / Geometry library Et stilig prosjekt som viser en sykkeltur til hovden på video med sanntidsoppdatering på kart, høydeforskjeller i terrenget samt Google Street View av turen. Stilig laget, og viser muligheter for de åpne api’er som finnes der ute, spesiellt fra Google.
Her er et skjermbilde fra Atle sitt prosjekt:

Atle vant en middag ute for 2 personer.

Andre plassen ble tatt av Torill Kjøsnes (http://twitter.com/torillkj) , utvikler i Media Norge Digital, med prosjektet “lokasjonsstyring”. Torill forklarer prosjektet sitt på denne måten:

Applikasjonen er bygget rundt html5 geolocation, og henter inn data fra ulike tjenester, twitter, gowalla, flickr basert på nåverende lokasjon. Målet var også å vise hvordan man kan integrere nyheter og hendelser i et slikt system dersom disse er tagget med geodata.

Takk til arrangements komiteen (Erik og Kim-Robin) for en kjempe event!

Noen flere bilder fra opplegget:

Posted in Hackday | Leave a comment

Hackday for Media Norge

Torsdag 27.januar er det tid for nok en hackday. Vi kommer til å poste resultatet på bloggen.

Gode innspill på hva vi kan lage tas imot med takk!

Dette blir 24 timer med ekte nerding, koding, pizza, xbox, red-bull og veldig sosialt. Så langt er vi 17 påmeldte MNO ansatte. Vi har ett auditorium slik at vi kan spille xbox på storskjerm, samt et sort møterom hvor alle deltagere sitter, hvor det i tillegg er en en annen xbox. I løpet av natten regner vi med Fifa turnering og 4 mot 4 i Halo i nettverk.

Forrige runde var en kjempe suksess, og vi gleder oss til å kjøre igang første sesjon i 2011

Posted in Hackday | Tagged | Leave a comment

Hackday mai 2010

Litt info om hackday i mai 2010. Bildet er tatt fra Aftenpostens internblad. Årets arrangement vil bli mer digital, og info kommer til å komme på denne bloggen.

Fra Aftenpostens intern magasin

Posted in Uncategorized | Tagged | Leave a comment

Vi trenger flere designere/javahuer/app devs/streaming folk

Uansett hva du kaller deg, vi trenger tech folk og designere. Har du lyst til å jobbe med avis i alle kanaler (web/mobil etc) for Media Norge, sjekk ut hva slags folk vi trenger og meld din interesse ved å sende mail til webutvikler (att) medianorge (dott) no

Posted in Uncategorized | Leave a comment

Sandbox jQuery

First try to sandbox $ (jQuery) to a specific element:

HTML:

<div id = "test">
  <h2>TEST</h2>
</div>
<h2>TEST 2</h2>

Javascript:

function Module() {
}

function Sub () {
	console.log($('h2'));

};

Sub.prototype = new Module();

Sub();

console.log($('h2'));

function $() {
	var args = Array.prototype.slice.call(arguments);
	var callerFunction = arguments.callee.caller;
	if (callerFunction && callerFunction.prototype.constructor == Module) {
		return window.jQuery(args[0], '#test');
	}
	return window.jQuery.apply(null,args);
}

Result: Doesn’t work with chaining.

Second try

if (typeof Object.create !== 'function') {
      Object.create = function (o) {
        function F() {}
        F.prototype = o;
        return new F();
      };
    }

var Module = function () {
	var pub = {};
	pub.test = "true";
	return pub;
	}()

var Sub = function () {
	var pub = {};
	pub.test2=function () {
		$('h2').html("test");
		};
	return pub;
	}()

var instance = function () {
	this.$ = function (selector) {
		return window.jQuery(selector, '#test');
		};
	var pub = window.jQuery.extend(Object.create(Sub), Module);
	return pub;
}()

$(document).ready(function () {
	instance.test2();
});

Success!

Posted in Javascript, jQuery | Leave a comment

Real cross browser box-shadow

If you want more css functionality, ccs3Pie gives you all the functionality you need. If you only need box-shadows – read on. This method is easier than css3Pie, since it doesn’t use VML.

Internet Explorer versions below version 9 doesn’t have the css3 box-shadow property. You can use the dropshadow filter, but this filter creates a solid dropshadow. It is not really what we want. To create real drop shadow for ie8, ie7 and ie6 we apply the Blur filter.

I have used this technique to create box-shadows on Aftenposten for over a year, but only as markup hidden in conditional comments. With this html-component the box-shadow just works.


Just add the file dropshadow.htc using IE behaviours and Internet Explorer will display propert dropshadows.

[Example]

<!--[if lt IE 9]>
<style type= "text/css">
	#dropshadow {
		behavior:url(dropshadow.htc);
	}
</style>
<![endif]-->
<style = "text/css">
	#dropshadow {
		box-shadow:5px 5px 10px #ccc;
	}
</style>

Here is the code for dropshadow.htc

<public:attach event="oncontentready" onevent="oncontentready()" />

<script type="text/javascript">
	function oncontentready() {
		var dropShadow=window.document.createElement('div');
		dropShadow.setAttribute('class','dropShadow');
		var innerWrapper = window.document.createElement('div');

		element.style.position = (element.currentStyle['position'] == 'static') ? 'relative' : element.currentStyle['position'];
		innerWrapper.style.position = "relative";
		innerWrapper.style.zIndex = "2";
		innerWrapper.style.backgroundColor = "#ffffff";

		var html = element.innerHTML;
		element.innerHTML = '';
		innerWrapper.innerHTML = html;
		element.appendChild(innerWrapper);
		element.appendChild(dropShadow);

		var style = this.currentStyle['box-shadow'].split(' ');
		var shadowX = style[0].replace('px','');
		var shadowY = style[1].replace('px','');
		var shadowWidth = style[2].replace('px','');
		var shadowColor = style[3];
		dropShadow.style.position='absolute';
		dropShadow.style.left=(shadowX-shadowWidth)+'px';
		dropShadow.style.top=(shadowY-shadowWidth)+'px';
		dropShadow.style.zIndex=1;
		dropShadow.style.width='100%';
		dropShadow.style.height='100%';
		dropShadow.style.background=shadowColor;

		var sProperties = "pixelradius="+shadowWidth+", makeShadow=true, ShadowOpacity=0.5";
		dropShadow.style.filter = "progid:DXImageTransform.Microsoft.Blur("+sProperties+")";

	}
</script>
Posted in CSS, IE Behaviours, Javascript | Leave a comment

Creating a jQuery plugin to wrap text around images

Do you want to wrap text around the edges of an image, not constrained to the square shape? This tutorial show how it is done.

Example | Download plugin

A “long” time ago, Eric Meyer introduced the concept of ragged float. In 2006 Rob Swan gave us Sliced and Diced Sandbags, an automated way of creating ragged floats using a server side script. With the arrival of the canvas element we can to this on the client. (After creating this plugin I found the jQSlickWrap plugin that does the same thing as my plugin).


There  is no way to read the pixel out of an img-element. However, by using the canvas method getImageData we can collect an array of all the pixels. This is an one dimensional array consisting of the red, green, blue and alpha channels . Each pixel is represented by four entries in the array. One for each channel: [r,g,b,aplha, r,g,b,aplha...]. By iterating through the array, we can find where the image is transparent and where we have visible pixels. We can also do this with color, but this is not yet implemented in this plugin.
The plugin won’t work in browsers that don’t support the canvas element.

First the code:

/**************************************************
*	cutOut
*	Author: Tor Brekke Skjøtskift
*	Version: 0.1
*
**************************************************/
(function($) {
	$.fn.cutout = function(settings) {
     	var config = {
			padding:'5px'
			}, canvas = document.createElement("canvas"), padding="";

		if (!canvas.getContext("2d")) return false;

		var ctx = canvas.getContext("2d");

		if (settings) $.extend(config, settings);
		this.each(function() {
			var $this = $(this);
			var imgEl = $this.get(0);
			//Find relevant styles
			var lineHeight = function (lh) {
				if (lh.indexOf('px') !== -1) {
					return parseInt(lh.replace('px',''),10);
				} else {
					var copy=$this.clone();
				        result = 0;
					$(copy).css({
						padding:'0'
					})
					.html('x')
					.insertBefore($this);
			            	result = copy.innerHeight()/10;
			         	copy.remove();
					return parseInt(result,10);
				}
			}($this.parent().css('line-height'));
			var direction;
			if ($this.css('float') == "left") {
				direction = 'left';
				padding='padding-right:'+config.padding;
			} else if ($this.css('float') == "right") {
				direction = 'right';
				padding='padding-left:'+config.padding;
			} else {
				direction = false;
			}
			if (direction) {
				//set height and width of canvas to same as image
				canvas.width = imgEl.width;
				canvas.height = imgEl.height;
				//draw image on canvas
				ctx.drawImage(imgEl, 0, 0);
				//get pixel data
				var oImageData = ctx.getImageData(0, 0,imgEl.width,imgEl.height );
				 //cache data for performance
				var data = oImageData.data;
				var ragged = [], i = 0, el, count = 0;
				//iterate image height in steps of parent line-heigh
				for (var h = 0; h <= oImageData.height; h=h+lineHeight) {
					for (var h1 = h; h1 < h+lineHeight; h1++) {
						if (direction=='left') {
							for (var w = oImageData.width; w>=0; w--) {
								var i = (h1*oImageData.width + w-1)*4 + 3;
								if (data[i] > 250) {
									ragged[count] = w;
									break;
								} // end if
							} // end for
						} else {
							for (var w = 0; w<=oImageData.width; w++) {
								var i = (h1*oImageData.width + w)*4 + 3;
								if (data[i] > 250) {
									ragged[count] = oImageData.width-w;
									break;
								} // end if
							} // end for
						}
					} // end for
					count++;
				} // end for
				//Create the ragged float
				while(count--) {
					el = document.createElement('div');
					el.setAttribute('style', 'float:'+direction+';clear:'+direction+';background:url('+$this.attr('src')+') no-repeat '+direction+' -'+count*lineHeight+'px;z-index:10;height:'+lineHeight+'px;'+padding+';width:'+ragged[count]+'px');
					$this.after(el);
				} // end while
				$this.remove();
			} else {
				if (window.console) {
					console.log('You need to apply float:left or float:right to the cutout image');
				}
			} // endif
		}); // end each
		return this;
	};
})(jQuery);

Lets have a look at the code, step by step.

var config = {
	padding:'5px'
	},canvas = document.createElement("canvas"), padding="";

	if (!canvas.getContext("2d")) return false;

	var ctx = canvas.getContext("2d");

	if (settings) $.extend(config, settings);

First we create the default settings. For this plugin I have only one setting – padding. We need some padding between the edges of the image and the text to create the nessecary air. I also create one canvas element. Then we test if the browser supports the getContext method to find out if the browser supports canvas. We then create the context.

The canvas element is only used to inspect the pixels in the image, so we don’t need more than one.

this.each(function() {
	var $this = $(this);
	var imgEl = $this.get(0);

For performance purposes I cache a reference to $(this) and the html element itself. “get(0)” gets the original DOM element from the jQuery object.

//Find relevant styles
var lineHeight = function (lh) {
	if (lh.indexOf('px') !== -1) {
		return parseInt(lh.replace('px',''),10);
	} else {
		var copy=$this.clone()
		      result = 0;
			$(copy).css({
				padding:'0'
			})
			.html('x')
			.insertBefore($this);

            	result = copy.innerHeight()/10;
         	copy.remove();
		return parseInt(result,10);
	}

}($this.parent().css('line-height'));
var direction;
if ($this.css('float') == "left") {
	direction = 'left';
	padding='padding-right:'+config.padding;
} else if ($this.css('float') == "right") {
	direction = 'right';
	padding='padding-left:'+config.padding;
} else {
	direction = false;
}

Now we inspect both the parent element of the image and the image itself to find the nessecary styles needed for presentation. First we find the line-height of the parent element. (UPDATE: Added test for lineheight other than px). If you have a look at Eric Meyers original ragged float, he sets the height of the div-elements to 15px. This is not a good way to create ragged floats. In order to get the correct flow of the text, the height of the div should be the same as the line-height of the body text. The next thing we do is to check if the image is floated and if it is floated to the left or right. If the image is not floated, there is no reason to create a text wrap.

if (direction) {
	//set height and width of canvas to same as image
	canvas.width = imgEl.width;
	canvas.height = imgEl.height;

	//draw image on canvas
	ctx.drawImage(imgEl, 0, 0);
	//get pixel data
	var oImageData = ctx.getImageData(0, 0,imgEl.width,imgEl.height );

	 //cache data for performance
	var data = oImageData.data;

If we have a float, we proceed to setting the width of the canvas to the width of the img-element. Using the drawImage method, we place the image at the canvas at the top left position. We can now use getImageData method to retrive an array of pixels. This is stored in the data property of the imageDataObject

Important: Due to security limitations, the getImageData method will not work locally or with external images

Lastly we create a variabel to hold the data for performance.

var ragged = [], i = 0, el, count = 0;
//iterate image height in steps of parent line-heigh
for (var h = 0; h <= oImageData.height; h=h+lineHeight) {

First we create the variables. The array “ragged” will hold the width of the edge of the image.

The for loop loops through the y-axis of the image in steps of the parent line-height. We do this because we are only interested in one width value for each div-element in the ragged float.

for (var h1 = h; h1 < h+lineHeight; h1++) {

We create another loop to go through the pixels in between the steps in the outer loop. We need this value to find the correct index in the imageDataObject.

if (direction=='left') {
	for (var w = oImageData.width; w>=0; w--) {
		var i = (h1*oImageData.width + w-1)*4 + 3;
		if (data[i] > 250) {
			ragged[count] = w;
			break;
		} // end if
	} // end for
} else {
	for (var w = 0; w<=oImageData.width; w++) {
		var i = (h1*oImageData.width + w)*4 + 3;
		if (data[i] > 250) {
			ragged[count] = oImageData.width-w;
			break;
		} // end if
	} // end for
}

First we check the direction of the float. If the image is floated left, we need to start examine the pixels from the right and vice versa. The direction of wich we are examining the pixels are decided in the for-loop. Notice the difference.

The next line is a bit tricky to understand (var i = (h1*oImageData.width + w-1)*4 + 3;). This is where we find the index of the channel of the pixel. As I briefly explained earlier the imageDataObject consists of a one dimensional array with all the color channels. One pixel is represented by four entries in the array. We multiply the h1 variabel, which represents the y-axis of the image with the pixel width of the image. Then we add the w-variabel which represents the x-axis we are currently on, to get the number of pixels we have looped through. Then we need to multiply this by four, because each pixel is represented by four entries in the array, one for each channel. The w-variable is subtracted by one when we examine from the right, so we can inspect the current pixel. We then add 3 to get the aplha channel of the pixel we would like to examine. Remeber the index always start at 0, so the index number three is the fourth entry.


We now check the value of the alpha channel. Complete transparency is 255, but I have set this at 250 just to be safe. If the test goes through and we have a pixel with an alpha channel lower than 250 we add the width to our array and break the loop. We have found the edge.

while(count--) {
	el = document.createElement('div');
	el.setAttribute('style', 'float:'+direction+';clear:'+direction+';background:url('+$this.attr('src')+') no-repeat '+direction+' -'+count*lineHeight+'px;z-index:10;height:'+lineHeight+'px;padding-'+direction+':'+config.padding+';width:'+ragged[count]+'px');
	$this.after(el);
} // end while
$this.remove();

Now the time has come to actually create the ragged float. This is done by creating div elements with the height of the parents line-height. The width is retrieved form our array. We also nedd to add a clear-property to push the div’s to a new line. Here we also add padding and the direction variable to insure corerrect rendering.

We then inserts the div elements after our image element and finally removes the image element from the DOM.

And we are done.

Posted in jQuery | 2 Comments