<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Hinnerup Net &#187; JavaScript</title>
	<atom:link href="http://www.hinnerup.net/permanent/category/programming/javascript/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.hinnerup.net</link>
	<description></description>
	<lastBuildDate>Thu, 19 Jan 2012 12:35:57 +0000</lastBuildDate>
	<language>da</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Autocomplete med Ajax på adresse med jQuery og OIO</title>
		<link>http://www.hinnerup.net/permanent/2011/06/23/autocomplete-med-ajax-paa-adresse-med-jquery-og-oio/</link>
		<comments>http://www.hinnerup.net/permanent/2011/06/23/autocomplete-med-ajax-paa-adresse-med-jquery-og-oio/#comments</comments>
		<pubDate>Thu, 23 Jun 2011 12:04:52 +0000</pubDate>
		<dc:creator>Tobias Hinnerup</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[Programmering]]></category>
		<category><![CDATA[Ajax]]></category>
		<category><![CDATA[JSONP]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[Widget]]></category>

		<guid isPermaLink="false">http://www.hinnerup.net/?p=1470</guid>
		<description><![CDATA[Indtastning af adresseoplysninger er notorisk noget bøvlet: Der er flere indbyrdes forbundne felter og erfaringsvis opstår der meget hurtigt mangel på sammenhæng &#8211; hvilket fører til manuelle processer for opfølgning og fejlretning, for ikke at glemme muligheden for tabte forsendelser. Heldigvis har den offentlige digitalisering i danmark resulteret i gratis &#038; ubetinget adgang til services, [...]]]></description>
			<content:encoded><![CDATA[<p>Indtastning af adresseoplysninger er notorisk noget bøvlet: Der er flere indbyrdes forbundne felter og erfaringsvis opstår der meget hurtigt mangel på sammenhæng &#8211; hvilket fører til manuelle processer for opfølgning og fejlretning, for ikke at glemme muligheden for tabte forsendelser.</p>
<p>Heldigvis har den offentlige digitalisering i danmark resulteret i gratis &#038; ubetinget adgang til services, der kan sammensættes til en gevaldig hjælp i forhold til at ramme rigtigt: Med et par kombinationer af jQuery, Ajax, JSONP og autocomplete er det muligt ud fra et delvist vejnavn at få postnummer og bynavn givet, med samt en liste over gyldige vejnumre.</p>
<p>Herunder et eksempel på et sæt adressefelter der hjælper hvor hjælpes kan. Prøv eventuelt først at taste vejnavn og lade autocomplete klare &#8220;resten&#8221; &#8211; og derefter i postnummerfeltet at skrive et nyt/andet postnummer og derefter konstatér, at autocomplete i vejnavne-feltet nu kun giver navne i det pågældende postnummer.</p>
<style>  .wp-fuckup p, .wp-fuckup br  { display: none; } .ui-autocomplete-loading { background: white url('/2011/06/oiorest/ui-anim_basic_16x16.gif') right center no-repeat; }	#streetname { width: 25em; } </style>
<div class="wp-fuckup">
<link rel="stylesheet" href="http://code.jquery.com/ui/1.8.13/themes/base/jquery-ui.css">
<script src="http://code.jquery.com/jquery-latest.js"></script><br />
<script src="http://code.jquery.com/ui/1.8.13/jquery-ui.min.js"></script><br />
<script src="/2011/06/oiorest/jquery.ui.plugin_autocompleteAddress.js"></script><br />
<script>
	$().ready(function() {
		jQuery("#streetname").autocompleteAddress();
	});
</script></p>
<div class="ui-widget">
  <label for="streetname" >Vejnavn:</label></p>
<input type="text" name="streetname" id="streetname" />
  <label for="streetnumber">Nr.:</label></p>
<input type="text" name="streetnumber" id="streetnumber" size="4" />
</div>
<div class="ui-widget">
  <label for="zipcode">Postnummer: </label></p>
<input type="text" name="zipcode" id="zipcode" size="4"  />
<input type="text" name="city" id="city" size="45" disabled="disabled" /></div>
</div>
<p>Givet, at de rette inkludes er gjort, kan den fulde funktionalitet af ovenstående implementeres med et kald ala</p>
<pre class="prettyprint">
$().ready(function() {
  $(&#34;#streetname&#34;).autocompleteAddress();
});
</pre>
<p>Ovenstående under forudsætning af at der er defineret input felter i HTML&#8217;en som herunder.</p>
<pre class="prettyprint">
&lt;div class=&#34;ui-widget&#34;&gt;
	&lt;label for=&#34;streetname&#34;&gt;Vejnavn: &lt;&#47;label&gt;
	&lt;input type=&#34;text&#34; name=&#34;streetname&#34; id=&#34;streetname&#34;&#47;&gt;
	&lt;label for=&#34;streetnumber&#34;&gt;Nr.: &lt;&#47;label&gt;
	&lt;input type=&#34;text&#34; name=&#34;streetnumber&#34; id=&#34;streetnumber&#34; size=&#34;4&#34; &#47;&gt;
&lt;&#47;div&gt;
&lt;div class=&#34;ui-widget&#34;&gt;
	&lt;label for=&#34;zipcode&#34;&gt;Postnummer: &lt;&#47;label&gt;
	&lt;input type=&#34;text&#34; name=&#34;zipcode&#34; id=&#34;zipcode&#34; size=&#34;4&#34;  &#47;&gt;
	&lt;input type=&#34;text&#34; name=&#34;city&#34; id=&#34;city&#34; size=&#34;45&#34; disabled=&#34;disabled&#34; &#47;&gt;
&lt;&#47;div&gt;</pre>
<p>Såfremt det ønskes at benytte andre feltnavne/id&#8217;er en de her angivne, kan de angives eksplicit:</p>
<pre class="prettyprint">
  $(&#34;#streetname&#34;).autocompleteAddress({
    streetNumber: &#34;#streetnumber&#34;,
    zipCode : &#34;#zipcode&#34;,
    city: &#34;#city&#34;
});
</pre>
<p>Såfremt en eller flere af adresse-felterne ikke kan findes (eller angives med null i argumentet) udelades autocomplete-funktionaliteten for dét. Særligt er tilfældet, hvor det eksempelvis kun ønskes at benytte plugin&#8217;et til at lave autocomplete på postnummer/bynavn: Her skal blot sørges for, at instantiere fra et vilkårligt element der IKKE er et input felt (eksempelvis &#8220;body&#8221;).</p>
<p>Værd at bemærke er også følgende options:</p>
<p><b>matchAnywhere</b> Default: false<br />
Afgør hvorvidt det indtastede kan matche et vilkårligt sted i vejnavnet (true), eller kun i begyndelsen (false).</p>
<p><b>maxSuggestions</b> Default: 12<br />
Angiver hvor mange valgmuligheder der maksimalt vises. </p>
<p>De nødvendige inkludes er &#8220;de gængse&#8221; for brug af jQuery og tilhørende UI, med tilføjelse af en lille smule bogholderi (style) og den til formålet udviklede widget: <a href="/2011/06/oiorest/jquery.ui.plugin_autocompleteAddress.js">autocompleteAddress.js</a>.</p>
<pre class="prettyprint">
&lt;link rel=&#34;stylesheet&#34; href=&#34;http:&#47;&#47;code.jquery.com&#47;ui&#47;1.8.13&#47;themes&#47;base&#47;jquery-ui.css&#34;&gt;
&lt;script src=&#34;http:&#47;&#47;code.jquery.com&#47;jquery-latest.js&#34;&gt;&lt;&#47;script&gt;
&lt;script src=&#34;http:&#47;&#47;code.jquery.com&#47;ui&#47;1.8.13&#47;jquery-ui.min.js&#34;&gt;&lt;&#47;script&gt;
&lt;script src=&#34;http:&#47;&#47;www.hinnerup.net&#47;2011&#47;06&#47;oiorest&#47;jquery.ui.plugin_autocompleteAddress.js&#34;&gt;&lt;&#47;script&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;jquery.ui.plugin_autocompleteAddress.css&#34;&gt;
</pre>
<p>Bag kulisserne er der tale om, at <a href="http://jqueryui.com/demos/autocomplete/#remote-jsonp">jQuery UI&#8217;s autocomplete er kædet til JSONP opslag</a> mod &#8220;.json&#8221;-udgaverne af OIO&#8217;s REST-baserede webservices, jævnfør deres dokumentation for henholdsvis <a href="http://geo.oiorest.dk/documentation/api/vej.aspx">Vejnavne</a>, <a href="http://geo.oiorest.dk/documentation/api/adresse.aspx">Adresser </a>og <a href="http://geo.oiorest.dk/documentation/api/postnummer.aspx">Postnummer</a>.</p>
<p>Værd at bemærke er, at OIO&#8217;s implementation af vejnavne-servicen returnerer resultater på matches hvorsomhelst i navnet &#8211; og i autocomplete sammenhæng er det mest intuitive for de fleste formentlig at det er starten af vejnavnet man søger på. Såfremt matchAnywhere er false, ganges maxSuggestions med to ved opslag til Geoservicen, for at tilstræbe at &#8220;have nok&#8221;, når overflødige resultater frasorteres (idet Geoservicen kun understøtter søgninger af typen &#8220;matchAnywhere&#8221;). Det har den uheldige effekt, at idet 2 og 3 bogstavskombinationer forekommer i rigtig mange vejnavne, vil de første (mange) resultater fra OIO ofte IKKE være med matches i begyndelsen &#8211; hvorfor autocomplete ikke altid udnyttes af plugin&#8217;et. Bemærk, at denne uhensigtsmæssighed vil kunne omgås ved blot at fjerne maxantal begrænsningen på kaldet mod OIO &#8211; med performance fald som den eneste pris. </p>
<p>Situationen kan eksemplificeres ved i ovenstående at lave søgning efter eksempelvis &#8220;Bygm&#8221; og &#8220;By&#8221; henholdsvis med og uden at have angivet postnummeret &#8220;2400&#8243;.</p>
<p>Rent praktisk håndteres sagen i koden med kald af .filter og derefter .slice som illustreret herunder.</p>
<pre class="prettyprint">
var serviceUri = &#34;http:&#47;&#47;geo.oiorest.dk&#47;vejnavne.json&#34;;
var serviceArguments = {
	&#47;* Note; Custom filtering on success may reduce this *&#47;
	maxantal: matchAnywhere ? maxSuggestions : eStreetName.val().length &lt; 4 ? 20*maxSuggestions : 2*maxSuggestions ,
	vejnavn: request.term
};

if(eZipCode.length) {
	var zipCode = eZipCode.val();
	if(zipCode.length == 4) {
		serviceArguments.postnr = zipCode;
	}
}

$.ajax({
	url: serviceUri,
	dataType: &#34;jsonp&#34;,
	data: serviceArguments,
	success: function( data ) {
		if(!matchAnywhere) {
			&#47;&#47;Remove matches that are not in the beginning of navn
			data = data.filter(function (vej) {
				var pattern = new RegExp(&#34;^&#34; + eStreetName.val(), &#34;i&#34;);
				return pattern.test(vej.navn);
			});
		}

		&#47;&#47;Reduce to max number of suggestions for display
		data = data.slice(0, maxSuggestions); 

		&#47;&#47;Map OIO object to label&#47;value for jQuery autocomplete
		data = $.map(data, function( vej ) {
			return {
				label: vej.navn + &#34; (&#34; + vej.postnummer.nr + &#34;)&#34;,
				value: vej.navn
			}
		})

		response(data);
	}
});
</pre>
<p>For optimering af brugeroplevelsen benyttes kun et enkelt opslag i forhold til autocomplete af vejnummeret &#8211; resultatet caches lokalt. Dette muliggør både en mere logisk sortering end den alfanumeriske (1, 10, 11, 2a, 20 &#8230;) som OIO leverer data i. Desuden omgås derved det &#8220;problem&#8221; der ligger i at adressse-servicen hos OIO matcher eksakt på husnummer-angivelse, og dermed ikke i udgangspunktet er synderligt velegnet til den nærværende autocomplete-anvendelse.</p>
<p>Dette <a href="http://plugins.jquery.com/project/autocompleteAddress">plugin er tilføjet jQuery repository med navnet autocompleteAddress</a>. </p>
<p>Øverst på listen over kommende features står:</p>
<ul>
<li><strong>Version 1.5 (?)</strong>
<ul>
<li>Forslag modtages gerne!</li>
</ul>
</li>
<li><strong>Version 1.4 (2011-08-22)</strong>
<ul>
<li>Sortering på vejnavn/postnummer tilføjet</li>
</ul>
</li>
<li><strong>Version 1.3 (2011-07-27)</strong>
<ul>
<li>Optimeret mapning og filtrering af vejnavne &#038; numre</li>
<li>Optimeret parsning og sortering af vejnumre</li>
<li>Optimeret logik omkring maxantal (tilføjet missFactor)
<li>Tilføjet &#8220;caller id&#8221; til alle requests mod Geoservien</li>
</ul>
</li>
<li><strong>Version 1.2 (2011-07-04)</strong>
<ul>
<li>Logisk sortering af husnumre (tak til Michael Schøler)</li>
<li>Mere intelligent håndtering af opslag til OIO vedrørende maxantal ved matchAnywhere = false</li>
<li>Stylesheet opsætning udtrukket til separat fil</li>
</ul>
</li>
<li><strong>Version 1.1 (2011-06-28)</strong>
<ul>
<li>Valgfri anvendelse af de 4 adressefelter </li>
<li>Valgfri angivelse af metode for selektion af vejnavne (start|alle)</li>
<li>Valgfri angivelse af maks antal indgange i dropdown</li>
</ul>
</li>
<li><strong>Version 1.0 (2011-06-23)</strong>
<ul>
<li>Første version</li>
</ul>
</li>
</ul>
<p>Yderligere forslag og kommentarer er meget velkomne.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hinnerup.net/permanent/2011/06/23/autocomplete-med-ajax-paa-adresse-med-jquery-og-oio/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Animeret Javascript menu</title>
		<link>http://www.hinnerup.net/permanent/2011/03/23/animeret-javascript-menu/</link>
		<comments>http://www.hinnerup.net/permanent/2011/03/23/animeret-javascript-menu/#comments</comments>
		<pubDate>Wed, 23 Mar 2011 09:39:36 +0000</pubDate>
		<dc:creator>Jakob Justsen</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[Programmering]]></category>
		<category><![CDATA[Cross Browser]]></category>
		<category><![CDATA[Widget]]></category>

		<guid isPermaLink="false">http://www.hinnerup.net/?p=1148</guid>
		<description><![CDATA[Introduktion For noget tid siden fik jeg mulighed for at lege lidt mere end normalt med Javascript. Resultatet blev en animeret Javascript menu, som jeg vil beskrive i dette indlæg. Udgangspunktet for menuen kan ses i skitsen herunder: Hvert menu-punkt i menuen består af et billede med et anker omkring. Det midterste billede er det [...]]]></description>
			<content:encoded><![CDATA[<h3>Introduktion</h3>
<p>For noget tid siden fik jeg mulighed for at lege lidt mere end normalt med Javascript. Resultatet blev en animeret Javascript menu, som jeg vil beskrive i dette indlæg.</p>
<p>Udgangspunktet for menuen kan ses i skitsen herunder:</p>
<p><a href="http://www.hinnerup.net/wp-content/uploads/2011/01/BasicIdea.jpg"><img src="http://www.hinnerup.net/wp-content/uploads/2011/01/BasicIdea.jpg" alt="" title="BasicIdea" width="548"  class="alignnone size-full wp-image-1149" /></a></p>
<p>Hvert menu-punkt i menuen består af et billede med et anker omkring. Det midterste billede er det aktive billede &#8211; hvis der klikkes på det, navigeres der til linket defineret af ankeret. Klikkes der på et af de andre billeder, bliver det aktivt, og menuen roteres, så det aktive billede altid er centreret.</p>
<p>Et synligt menu-punkt er til enhver tid tilknyttet en position i menuen.</p>
<p>Menuen udvikles som en JQuery widget, idet det herved bliver nemt at anvende menuen. Den basale widget struktur kan ses herunder:</p>
<pre class="prettyprint">
(function($) {
    options: {
        nVisibleItems: 5,
        visibilityFactor: 0.5,
        nFrames: 60,
        speed: 200,
	smoothTransition: true
    },

    _create: function() {
        ...
    },
    ...
})(jQuery);
</pre>
<p>I ovenstående stump kode erklæres først en anonym funktion, der tager én parameter $.Funktionen kaldes herefter med jQuery som parameter værdi. Herved undgåes evt. sammenfald med andre Javascript biblioteker, der også benytter $, som f.eks. Prototype. </p>
<p>Første skridt i den anonyme funktion er at tilføje vores widget under navnet ui.rotator til jQuery objektet. Funktionen _create fungerer som konstruktør for en jQuery widget. Den pre-fixede &#8216;_&#8217; er jQuery&#8217;s notation for en privat funktion.</p>
<p>Options objektet inderholder parametre til konstruktøren, som kan sættes når widget&#8217;en instantieres. Det er her muligt at angive antallet af synlige billeder i menuen, hvor stor en del af hvert billede der er synligt, hvor mange frames der går på en enkelt rotation, hastigheden for en rotation, samt hvorvidt der ved overgang fra inaktivt til aktivt menupunkt vises en &#8220;blød&#8221; animation. Sidstnævnte er beskrevet i detaljer sidst i artiklen.</p>
<h3>Initialisering af menu</h3>
<p>For at implementere menuen kan vi nøjes med at kigge på de synlige billeder samt de to billeder lige uden for den synlige del af menuen (markeret med stiplede linjer på figuren ovenfor). Et billedes position i menuen er en quadruple (top, left, width, zIndex), og i det følgende beskrives hvordan disse beregnes.</p>
<h4>Beregning af menuens bredde</h4>
<p>Center-positionen antages at have bredden <i>w</i>, og bredden på de resterende positioner gøres afhængige heraf. Dette klares ved at benytte en skaleringsfaktor <i>a</i> og sætte bredden af positionen <i>i</i> pladser fra midten til værdien <img src="http://www.hinnerup.net/wp-content/uploads/2011/01/scalingimagewidth.jpg" alt="" title="scalingimagewidth" width="69" height="16" class="alignnone size-full wp-image-1161" />.</p>
<p>Hvis vi antager, at <i>v</i> er synlighedsfaktoren, som angiver hvor stor en del af hver position, der skal være synlig i forhold til nærmeste position i den aktive positions retning, kan vi beregne den faktiske bredde af menu&#8217;en ud fra formlen:</p>
<p><img src="http://www.hinnerup.net/wp-content/uploads/2011/01/menuwidthstep1.jpg" alt="" title="menuwidthstep1" width="405" height="51" class="alignnone size-full wp-image-1163" /></p>
<p>Hvor <i>n</i> er antallet af positioner til venstre for midten. Summen til sidst i udtrykket er en geometrisk række og har derfor en kendt værdi, der kan udtrykkes ved:</p>
<p><img src="http://www.hinnerup.net/wp-content/uploads/2011/01/geometricsum.jpg" alt="" title="geometricsum" width="212" height="48" class="alignnone size-full wp-image-1164" /></p>
<p>Benyttes dette i ligningen fra før får vi:</p>
<p><img src="http://www.hinnerup.net/wp-content/uploads/2011/01/menuwidthstep2.jpg" alt="" title="menuwidthstep2" width="211" height="40" class="alignnone size-full wp-image-1165" /></p>
<p>Givet <i>a</i> kan vi altså beregne <i>W<sub>items</sub></i>. Målet må altså være at finde en skaleringsfaktor <i>a</i>, således at afstanden <img src="http://www.hinnerup.net/wp-content/uploads/2011/01/distance.jpg" alt="" title="distance" width="135" height="18" class="alignnone size-full wp-image-1167" /> er mindre end et givet <img src="http://www.hinnerup.net/wp-content/uploads/2011/01/epsilon.jpg" alt="" title="epsilon" width="34" height="14" class="alignnone size-full wp-image-1168" />.</p>
<p>Vi kan implementere beregningen af bredden simpelt som i nedenstående funktion:</p>
<pre class="prettyprint">
  _getEstimatedMenuWidth: function(scalingFactor) {
    var w = this.centerImageWidth,
    v = this.options.visibilityFactor,
    n = this.centerPositionIndex;

    var width = w * (1 + 2 * v * ((1 - Math.pow(scalingFactor, n)) &#47; (1 - scalingFactor) - 1));
    return width;
},
</pre>
<h4>Estimering af skaleringsfaktor</h4>
<p>For at finde en tilnærmet værdi for skaleringsfaktoren kan vi udnytte følgende:</p>
<p><a href="http://www.hinnerup.net/wp-content/uploads/2011/01/continouswidth.jpg"><img src="http://www.hinnerup.net/wp-content/uploads/2011/01/continouswidth.jpg" alt="" title="continouswidth" width="351" height="21" class="alignnone size-full wp-image-1186" /></a></p>
<p>Vi leder efter en skaleringsfaktor mellem 0 og 1. Starter vi med ½, kan vi beregne den tilnærmede bredde <i>W<sub>items</sub></i>. Hvis afstanden mellem <i>W<sub>menu</sub></i> og <i>W<sub>items</sub></i> er tilpas lille, er vi tilfredse. Baseret på ovenstående formel leder vi videre i intervallet (0;½) eller (½;1), alt efter om den er større eller mindre. Vi vælger blot en ny kandidat som skaleringsfaktor, defineret som midterpunktet i det nye interval. Herved opnåes en logaritmisk søgning, og vi kan finde en tilnærmet løsning i få skridt.</p>
<p>Søgningen implementeres rekursivt som nedenstående, hvor der benyttes en threshold på 1 pixel:</p>
<pre class="prettyprint">
  _getEstimatedScalingFactor: function(min, max) {
    var scalar = min + (max - min) &#47; 2,
    estimatedWidth = this._getEstimatedMenuWidth(scalar);

    if (this._isThresholdSatisfied(estimatedWidth)) {
      return scalar;
    }

    if (estimatedWidth &lt; this.viewWidth) {
      return this._getEstimatedScalingFactor(scalar, max);
    } 

    return this._getEstimatedScalingFactor(min, scalar);
  },
</pre>
<h4>Venstre-placering af positioner</h4>
<p>For at kunne placere positionerne korrekt i forhold til hinanden, er det nødvendigt for os at kende bredden på hver position. Da vi kun kan angive bredden på billederne i hele pixels, og da floating-point repræsentation er unøjagtig, må vi dog påregne et vist præcisionstab, når vi benytter den estimerede skaleringsfaktor til at beregne positionernes bredder. Vi skal altså håndtere, at summen af bredderne for den synlige del af hver position, ikke nødvendigvis ender på <i>W<sub>menu</sub></i>.</p>
<p>Dette kan vi gøre ved at starte med at placere den yderste position til venstre (som ikke er synlig). Dernæst placeres de resterende positioner med offset heri, dog med undtagelse af den aktive. Den aktive position placeres i midten af menuen, hvorved evt. overskydende eller manglende pixels vil blive ligeligt fordelt på begge sider af den aktive position. </p>
<p>Venstre-offset&#8217;et for positionerne kan beregnes som følger, hvor <i>n</i> er antallet af positioner til venstre for den aktive position, og <i>p</i> er det totale antal positioner:</p>
<p><img src="http://www.hinnerup.net/wp-content/uploads/2011/02/leftpos0.jpg" alt="" title="leftpos0" width="111" height="22" class="alignnone size-full wp-image-1215" /></p>
<p><img src="http://www.hinnerup.net/wp-content/uploads/2011/02/leftposcenter.jpg" alt="" title="leftposcenter" width="117" height="32" class="alignnone size-full wp-image-1214" /></p>
<p><img src="http://www.hinnerup.net/wp-content/uploads/2011/02/leftposi.jpg" alt="" title="leftposi" width="271" height="47" class="alignnone size-full wp-image-1213" /></p>
<h4>Top-placering af positioner</h4>
<p>Top-placeringen for hver position kan vi finde ved først at beregne forholdet mellem positions bredde og den aktive positions bredde. Vi kan så benytte dette forhold til at finde positionens nye højde ud fra højden på den aktive position. Halverer vi højden, har vi top-placeringen. Beregningen ses herunder:</p>
<p><img src="http://www.hinnerup.net/wp-content/uploads/2011/02/topi.jpg" alt="" title="topi" width="218" height="51" class="alignnone size-full wp-image-1220" /></p>
<h4>Beregning af z-index</h4>
<p>Vi mangler nu kun at beregne zIndex for hver position for at kunne beskrive positionerne fuldstændigt. Dette beregnes dog let ved at lade zIndex for den aktive position være <i>n</i>, mens zIndex for de resterende positioner aftager med 1 for hver plads fra den aktive position.</p>
<p>I koden er hele processen implementeret som følger:</p>
<pre class="prettyprint">
_Position: function(top, left, width, zIndex) {
  return { &#34;top&#34;: top, &#34;left&#34;: left, &#34;width&#34;: width, &#34;zIndex&#34;: zIndex };
},

_getPositions: function() {
  var p = &#91;&#93;,
  v = this.options.visibilityFactor,
  n = this.nPositions,
  m = this.centerPositionIndex,
  W = this.viewWidth,
  w = this.centerImageWidth,
  h = this.centerImageHeight,
  imageWidths = this._getImageWidths();

  p&#91;m&#93; = new this._Position(0, Math.floor((W - w) &#47; 2), w, m);
  p&#91;0&#93; = new this._Position(Math.floor((h - imageWidths&#91;m - 1&#93;*h &#47; w) &#47; 2), 0 - Math.floor(imageWidths&#91;m - 1&#93;*v), imageWidths&#91;m - 1&#93;, 0);

  for (var i = 1; i &lt; n; i++) {
    if (i == m) {
      continue;
    }

    if (i &lt; m) {
      p&#91;i&#93; = new this._Position(Math.floor((h - imageWidths&#91;m - i - 1&#93;*h &#47; w) &#47; 2), p&#91;i - 1&#93;.left + Math.floor(p&#91;i - 1&#93;.width * v), imageWidths&#91;m - i - 1&#93;, p&#91;i - 1&#93;.zIndex + 1);
    } else {
      p&#91;i&#93; = new this._Position(p&#91;n - i - 1&#93;.top, W - p&#91;n - i - 1&#93;.left - p&#91;n - i - 1&#93;.width, p&#91;n - i - 1&#93;.width, p&#91;n - i - 1&#93;.zIndex);
    }
  }

  return p;
},
</pre>
<h3>Animering</h3>
<p>Nu hvor positionernes hvile-placering er på plads i menuen, mangler vi bare at beregne positionernes placering under hver rotation, herefter kaldet frames. Antallet af frames mellem hver positions hvile-placering er angivet i options objektet. Vi beregner kun frames i venstre side af menuen, og spejler så disse til højre side. Beregningen er i princippet den samme for både top, left og width. For at komme fra position <i>i</i> til <i>i+1</i> skal vi blot dele differencen mellem top, left, og width værdierne med antallet af frames. Disse fordeles herefter ligeligt ud på frames mellem position <i>i</i> og <i>i+1</i>. I koden ser det ud som følger:</p>
<pre class="prettyprint">
_getAnimationSteps: function() {
  var positions = this._getPositions(),
                       animationSteps = &#91;&#93;;

  for (var i = 0; i &lt; this.nPositions - 1; i++) {
    animationSteps&#91;i*this.options.nFrames&#93; = positions&#91;i&#93;;
    for (var j = 1; j &lt; this.options.nFrames; j++) {
      animationSteps&#91;i*this.options.nFrames + j&#93; = new this._Position(
        this._getAnimationStep(&#34;top&#34;, positions, i, animationSteps, j),
	this._getAnimationStep(&#34;left&#34;, positions, i, animationSteps, j),
	this._getAnimationStep(&#34;width&#34;, positions, i, animationSteps, j),
	(j &#47; this.options.nFrames &lt; 0.5 ? positions&#91;i&#93;.zIndex : positions&#91;i + 1&#93;.zIndex)
      );
    }
  }

  animationSteps&#91;(this.nPositions - 1)*this.options.nFrames&#93; = positions&#91;this.nPositions - 1&#93;;
  return animationSteps;
},

_getAnimationStep: function(propertyName, positions, i, animationSteps, j) {
  return positions&#91;i&#93;&#91;propertyName&#93; + Math.floor(j*(positions&#91;i + 1&#93;&#91;propertyName&#93; - positions&#91;i&#93;&#91;propertyName&#93;) &#47; (this.options.nFrames));
},
</pre>
<h4>Afbilding af positioner til billeder</h4>
<p>For at kunne placere billederne korrekt i menuen er det nødvendigt at kunne projektere de beregnede positioner ned på en delmængde af billederne. For at kunne gøre dette benyttes et offset, der angiver hvor i listen af billeder, den første position skal placeres. Alle billeder, der ikke matches med en position, sættes til display: none. Koden hertil ses nedenfor:</p>
<pre class="prettyprint">
_drawImages: function() {
  var positionOffset = this.animationState.getCurrentPositionOffset();
  for (var i = 0; i &lt; this.images.length; i++) {
    var positionIndex = i - positionOffset;
    if (positionIndex &gt;= 0 &#038;&#038; positionIndex &lt; this.nPositions - 1) {
      var position = this.animationSteps&#91;positionIndex*this.options.nFrames + this.animationState.getCurrentFrame()&#93;;
      $(this.images&#91;i&#93;).css( &#34;width&#34;, position.width );
      $(this.anchors&#91;i&#93;).css( {top: position.top, left: position.left, zIndex: position.zIndex, display: &#34;block&#34; } );
    } else {
      $(this.anchors&#91;i&#93;).css({ display: &#34;none&#34; });
    }
  }
  this._moving();
},
</pre>
<p>Det nuværende positionoffset beregnes som det aktive billedes indeks fratrukket indekset for den midterste position. Dette lægger op til, at vi blot skal huske på indekset for det aktive billede. Men det giver nogle lidt knudrede beregninger, idet skiftet fra f.eks. position <i>i</i> frame 29 til position <i>i+1</i> frame 0 betyder, at det aktive billedes indeks skal tælles 1 ned. Dvs. når frameindekset stiger, så falder det aktive billedes indeks. Endvidere skal man angive både det aktive billede og frameindekset for at rotere menuen til en bestemt position. Det er bedre med et enkelt indeks, hvor det aktive billede og frame-indekset kan beregnes fra.</p>
<p>Vi kan definere animationsindekset <i>s</i> som <img src="http://www.hinnerup.net/wp-content/uploads/2011/02/animationstepinterval.jpg" alt="" title="animationstepinterval" width="108" height="18" class="alignnone size-full wp-image-1241" />, hvor <i>n</i> er antallet af billeder, og <i>m</i> er antallet af frames pr. rotation. Herudover kan vi definere følgende funktioner:</p>
<ul>
<li>Billedeindeks til animationsindeks: <img src="http://www.hinnerup.net/wp-content/uploads/2011/02/imagetoanimationstep.jpg" alt="" title="imagetoanimationstep" width="115" height="20" class="alignnone size-full wp-image-1238" /></li>
<li>Animationsindeks til billedeindeks: <img src="http://www.hinnerup.net/wp-content/uploads/2011/02/animationsteptoimage.jpg" alt="" title="animationsteptoimage" width="110" height="24" class="alignnone size-full wp-image-1237" /></li>
<li>Animationsindeks til frameindeks: <img src="http://www.hinnerup.net/wp-content/uploads/2011/02/animationsteptoframe.jpg" alt="" title="animationsteptoframe" width="109" height="20" class="alignnone size-full wp-image-1236" /></li>
</ul>
<p>Af ovenstående funktioner fremgår det, at for hver gang animationsindekset forøges med <i>m</i>, trækkes der 1 fra det aktive billedes indeks, hvilket er den ønskede opførsel.</p>
<p>Koden til at holde styr på indekset kan ses herunder:</p>
<pre class="prettyprint">
_AnimationState: function(nImages, nFrames, speed, currentImage, centerPositionIndex) {
  var imageToAnimationStep = function(image) {
         return (nImages - image) * nFrames;
       },
       currentAnimationStep = imageToAnimationStep(currentImage),
       lastUpdated = null;

  this.getCurrentImage = function() {
    return nImages - Math.floor(currentAnimationStep &#47; nFrames);
  }

  this.getCurrentPositionOffset = function() {
    return this.getCurrentImage() - centerPositionIndex;
  }

  this.getCurrentFrame = function() {
    return currentAnimationStep % nFrames;
  }		

  this.getAnimationStep = function() {
    return currentAnimationStep;
  }

  this.update = function(targetImage) {
    var targetAnimationStep = imageToAnimationStep(targetImage),
         direction = 0;
    if (targetAnimationStep === currentAnimationStep) {
      lastUpdated = null;
      return;
    } 

    direction = (currentAnimationStep &gt; targetAnimationStep ? -1 : 1);

    if (lastUpdated === null) {
      lastUpdated = (new Date()).getTime();
      currentAnimationStep += direction;
      return;
    }

    var now = (new Date()).getTime();
    var diff = now - lastUpdated;
    lastUpdated = now;

    var offset = Math.floor(diff &#47; this.getFrameSpeed());

    currentAnimationStep += direction * offset;

    if ((direction &lt; 0 &#038;&#038; currentAnimationStep &gt; targetAnimationStep) ||
        (direction &gt; 0 &#038;&#038; currentAnimationStep &lt; targetAnimationStep)) {
      return;
    }

    currentAnimationStep = targetAnimationStep;
    lastUpdated = null;
  }

  this.setAnimationStep = function(animationStep) {
    currentAnimationStep = animationStep;
  }

  this.getMaxAnimationStep = function() {
    return imageToAnimationStep(0);
  }

  this.getMinAnimationStep = function() {
    return imageToAnimationStep(nImages - 1);
  }

  this.isSettled = function() {
    return this.getCurrentFrame() === 0;
  },

  this.getFrameSpeed = function() {
    return speed &#47; nFrames;
  }
},
</pre>
<p>Update funktionen i AnimationState objektet tester på, om det er nødvendigt at springe frames over under animeringen. Browsere er ikke altid lige præcise i deres timer funktioner, og de udfører placeringen af billederne i hvert skridt med variabel hastighed. For at undgå en hakkende og sløv rotation af menuen sørger Update funktionen for altid at vise den frame, der burde være den aktive frame jvf. tiden siden sidste opdatering.</p>
<p>Med alt det ovenstående på plads, kan vi nu forholdsvist nemt starte en animation, der aktiverer et vilkårligt billede i menuen. Funktionen udføres af nedenstående kode.</p>
<pre class="prettyprint">
centerImage: function(targetImage) {
  targetImage = parseInt(targetImage);

  if (!this.animationState.isSettled()) {
    return;
  }

  var self = this;
  this.animationInterval = setInterval(
                                    function() {
				      self.animationState.update(targetImage);
				      self._drawImages();
				      if ((targetImage === self.animationState.getCurrentImage()) &#038;&#038; self.animationState.isSettled()) {
				        clearInterval(self.animationInterval);
					self.animationInterval = null;
					self._itemCentered();
				      }
				    }, Math.ceil(this.animationState.getFrameSpeed())
  );
},
</pre>
<h4>Glidende overgang mellem aktiv og inaktiv position</h4>
<p>Den sidste ting, der mangler, er nu at lave en pæn overgang mellem de aktive billede og dets to naboer. Indtil videre vil billederne glide igennem hinanden, når det aktive billede bliver mindre, og det kommende aktive billede bliver større. Ved at ændre på animationen for den aktives billedes rotation, sådan at de to billeder flugter hinanden, når de er lige store, får vi et langt pænere skift. Dette gøres ved at tilføje en ny option <i>smoothTransition</i> til options objektet, samt nedenstående kode:</p>
<pre class="prettyprint">
_smoothTransition: function() {
  var leftPosition = (this.centerPositionIndex - 1) * this.options.nFrames,
       centerPosition = this.centerPositionIndex * this.options.nFrames,
       centerFrame = Math.floor(this.options.nFrames &#47; 2),
       p = this.animationSteps,
       overlap = p&#91;centerPosition + centerFrame&#93;.width - (p&#91;centerPosition + centerFrame&#93;.left - p&#91;leftPosition + centerFrame&#93;.left),
       var centerFrameLeftTarget = p&#91;leftPosition + centerFrame&#93;.left - overlap &#47; 2,
       var firstOffset = (centerFrameLeftTarget - p&#91;leftPosition&#93;.left) &#47; centerFrame,
       var secondOffset = (p&#91;leftPosition + this.options.nFrames - 1&#93;.left - centerFrameLeftTarget) &#47; (this.options.nFrames - centerFrame - 1);

  for (var i = 1; i &lt; this.options.nFrames; i++) {
    if (i &lt;= centerFrame) {
      p&#91;leftPosition + i&#93;.left = Math.floor(p&#91;leftPosition&#93;.left + i*firstOffset);
    } else {
      p&#91;leftPosition + i&#93;.left = Math.floor(p&#91;leftPosition + centerFrame&#93;.left + (i - centerFrame)*secondOffset);
    }
    p&#91;centerPosition + this.options.nFrames - i&#93;.left = this.viewWidth - p&#91;leftPosition + i&#93;.left - p&#91;leftPosition + i&#93;.width;
  }
},
</pre>
<h3>Den færdige menu</h3>
<p>Vores widget eksponerer to public events <i>moving</i> og <i>itemCentered</i>, der aktiveres efter <i>_drawImage</i>, og <i>centerImage</i> funktionerne er blevet kørt. Udover <i>centerImage</i> funktionen, som er public, tilbydes også <i>setAnimationStep</i>, med hvilken man direkte kan sætte animationsindekset, samt <i>value</i> som returnerer det nuværende animationsindeks. Disse events og funktioner kan benyttes til f.eks. at tilknytte en slider til menuen.</p>
<p>Et eksempel på brug kan downloades <a href="http://www.hinnerup.net/2011/02/jsmenu/JavascriptAnimatedMenu.rar">her</a>. Scriptet kan downloades <a href="http://www.hinnerup.net/2011/02/jsmenu/lib/ui.rotator.js">her</a>. </p>
<p>Den endelige menu kan ses herunder:</p>
<p><script type="text/javascript" src="http://www.hinnerup.net/2011/02/jsmenu/lib/jquery-1.4.4.min.js"></script><br />
<script type="text/javascript" src="http://www.hinnerup.net/2011/02/jsmenu/lib/jquery-ui-1.8.8.custom.min.js"></script><br />
<script type="text/javascript" src="http://www.hinnerup.net/2011/02/jsmenu/lib/ui.rotator.js"></script><br />
<script type="text/javascript">
  $(function() {
    $('#MenuContainer').rotator({
      centerImageWidth: 288,
      centerImageHeight: 197,
      nVisibleItems: 5
    })
  });
</script></p>
<style>
  #MenuContainer a img {
    border: none;
  }
</style>
<div id="MenuContainer" style="width: 548px; height: 197px; border: 1px solid black;">
  <a href="http://www.flexsus.dk"><img src="http://www.hinnerup.net/2011/02/jsmenu/images/flexsus.jpg" /></a><br />
  <a href="http://www.infolink.dk"><img src="http://www.hinnerup.net/2011/02/jsmenu/images/infolink.jpg" /></a><br />
  <a href="http://www.homeworkers.dk"><img src="http://www.hinnerup.net/2011/02/jsmenu/images/homeworkers.jpg" /></a><br />
  <a href="http://www.bornefonden.dk"><img src="http://www.hinnerup.net/2011/02/jsmenu/images/boernefonden.jpg" /></a><br />
  <a href="http://www.uni-c.dk"><img src="http://www.hinnerup.net/2011/02/jsmenu/images/uni-c.jpg" /></a><br />
  <a href="http://www.legouniverse.com"><img src="http://www.hinnerup.net/2011/02/jsmenu/images/lego.jpg" /></a><br />
  <a href="http://www.vejdirektoratet.dk"><img src="http://www.hinnerup.net/2011/02/jsmenu/images/vejdirektoratet.jpg" /></a><br />
  <a href="http://www.bluebusiness.dk"><img src="http://www.hinnerup.net/2011/02/jsmenu/images/blueBusiness.jpg" /></a><br />
  <a href="http://www.sonlinc.dk"><img src="http://www.hinnerup.net/2011/02/jsmenu/images/sonlinc.jpg" /></a><br />
  <a href="http://www.givblod.dk"><img src="http://www.hinnerup.net/2011/02/jsmenu/images/givblod.jpg" /></a>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.hinnerup.net/permanent/2011/03/23/animeret-javascript-menu/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Click tracking</title>
		<link>http://www.hinnerup.net/permanent/2010/01/22/click-tracking/</link>
		<comments>http://www.hinnerup.net/permanent/2010/01/22/click-tracking/#comments</comments>
		<pubDate>Fri, 22 Jan 2010 12:37:31 +0000</pubDate>
		<dc:creator>Lars Støttrup</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Programmering]]></category>
		<category><![CDATA[Click tracking]]></category>
		<category><![CDATA[Trafic analysis]]></category>

		<guid isPermaLink="false">http://www.hinnerup.net/?p=828</guid>
		<description><![CDATA[Vi har installeret CrazyEgg click tracking på hinnerup.net. CrazyEgg er et værktøj til at visualisere brugernes færden på ens site, og indeholder smarte funktioner som: Click-overlay, Heatmap og Confetti. Man indsætter en enkelt linje kode på de sider der skal trackes: Derefter opsættes de sider der skal trackes i CrazyEgg: Efter nogle dage begynder statistikken [...]]]></description>
			<content:encoded><![CDATA[<p>Vi har installeret <a href="http://www.crazyegg.com/">CrazyEgg</a> click tracking på <a href="http://hinnerup.net">hinnerup.net</a>.</p>
<p>CrazyEgg er et værktøj til at visualisere brugernes færden på ens site, og indeholder smarte funktioner som: Click-overlay, Heatmap og Confetti.</p>
<p>Man indsætter en enkelt linje kode på de sider der skal trackes:</p>
<pre lang="javascript"><script src="http://s3.amazonaws.com/new.cetrk.com/pages/scripts/0010/6701.js"
type="text/javascript"></script></pre>
<p>Derefter opsættes de sider der skal trackes i CrazyEgg:</p>
<div style="text-align:center"><a href="/wp-content/uploads/2010/01/Hinnerup_crazyegg_ops_tning.png"><img src="/wp-content/uploads/2010/01/Hinnerup_crazyegg_ops_tning.png" alt="" title="CrazyEgg opsætning" width="525" class="aligncenter size-medium wp-image-841" style="border:none;" /></a></div>
<p>Efter nogle dage begynder statistikken at være på plads.</p>
<p>Her er de forskellige overlays.</p>
<p>Standard overlay:</p>
<div style="text-align:center"><a href="/wp-content/uploads/2010/01/Hinnerup_front_page.png"><img src="/wp-content/uploads/2010/01/Hinnerup_front_page.png" alt="" title="Standard overlay, CrazyEgg" width="525" class="aligncenter size-medium wp-image-843" style="border:none;" /></a></div>
<p>Heatmap:</p>
<div style="text-align:center"><a href="/wp-content/uploads/2010/01/Hinnerup_heatmap.png"><img src="/wp-content/uploads/2010/01/Hinnerup_heatmap.png" alt="" title="Heatmap overlay, CrazyEgg" width="525" class="aligncenter size-medium wp-image-845" style="border:none;" /></a></div>
<p>Confetti:</p>
<div style="text-align:center"><a href="/wp-content/uploads/2010/01/Hinnerup_time_to_click.png"><img src="/wp-content/uploads/2010/01/Hinnerup_time_to_click.png" alt="" title="Confetti overlay, CrazyEgg" width="525" class="aligncenter size-medium wp-image-848" style="border:none;" /></a></div>
<p>Standard overlayet giver os mulighed for at se hvor mange clicks et givet link har fået. Heatmap er rimeligt selvforklarende &#8211; den viser os hvor &#8220;populært&#8221; et link eller anden detalje er. Den mest interessante er Confetti, som har følgende indstillings-muligheder:</p>
<div style="text-align:center"><a href="/wp-content/uploads/2010/01/Options.png"><img src="/wp-content/uploads/2010/01/Options.png" alt="" title="Options, CrazyEgg" width="123" height="185" class="aligncenter size-full wp-image-850" style="border:none;" /></a></div>
<p>Alle disse overlays skulle sammenlagt give ens hjemmesidedesigner mulighed for at designe en mere brugervenlig oplevelse.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hinnerup.net/permanent/2010/01/22/click-tracking/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>O3D &#8211; 3D i browsere</title>
		<link>http://www.hinnerup.net/permanent/2009/07/13/o3d-3d-i-browsereo3d-3d-in-browsers/</link>
		<comments>http://www.hinnerup.net/permanent/2009/07/13/o3d-3d-i-browsereo3d-3d-in-browsers/#comments</comments>
		<pubDate>Mon, 13 Jul 2009 11:34:22 +0000</pubDate>
		<dc:creator>Asger Dam Hoedt</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[3d]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[o3d]]></category>

		<guid isPermaLink="false">http://www.hinnerup.net/?p=395</guid>
		<description><![CDATA[3D grafik er sjovt. Og med O3D fra Google kan vi nu også få fuldt hardware understøttet cross browser 3D grafik i browserne. Dette kan give bedre muligheder for web spiludvikling naturligvis, men også datavisualisering, 3D grafikere, legetøjsfirmaer der vil fange interessen hos unge og hjemmesider, der bare gerne vil have et ekstra pift kan [...]]]></description>
			<content:encoded><![CDATA[<p>
  3D grafik er sjovt. Og med O3D fra Google kan vi nu også få fuldt<br />
  hardware understøttet cross browser 3D grafik i browserne. Dette kan<br />
  give bedre muligheder for web spiludvikling naturligvis, men også<br />
  datavisualisering, 3D grafikere, legetøjsfirmaer der vil fange<br />
  interessen hos unge og hjemmesider, der bare gerne vil have et<br />
  ekstra pift kan nyde godt af dette.
</p>
<p>
  For at anvende O3D skal der installeres en letvægts-plugin til din browser (lavet af Google naturligvis).<br />
  Derudover er selve O3D API&#8217;et er skrevet til Javascript, så webdevelopere behøver ikke lære et<br />
  nyt sprog for at bruge størstedelen af funktionaliteten. Undtagelsen<br />
  er shaders, der skrives i O3D&#8217;s eget shadersprog.
</p>
<p>
  Endnu en fordel ved O3D er at det er cross browser og cross platform<br />
  kompatibelt. Således er både IE, Firefox, Safari og Chrome<br />
  understøttet i Windows, Linux og Mac OS X.
</p>
<h3>3D menu</h3>
<p>
Til at demonstrere mulighederne ved O3D har jeg lavet en lille<br />
<a href="http://www.hinnerup.net/2009/07/experiments/o3d/">3D menu</a>.
</p>
<p>
3D menuen er inspiret af 3D desktoppe som Compiz til Linux, hvor<br />
brugerens forskellige desktoppe ligger på siden af en kube og der<br />
skiftes mellem dem ved at vende kuben.
</p>
<p>
I eksemplet fungerer kubens sider som links og kan drejes for at<br />
skifte link. Måden brugeren drejer kuben på er ved at trykke på<br />
knapperne ovenfor eller bruge <b>A</b> eller <b>S</b> tasterne. Desuden kan man<br />
zoome på <b>W</b> og <b>S</b>.
</p>
<h3>Muligheder demonstreret</h3>
<p>I eksemplet demonstreres følgende:</p>
<ul>
<li> Da O3D skrives i Javascript betyder det at det er nemt for<br />
  webudviklere at foretage interaktioner mellem en given hjemmeside og<br />
  3D miljøet. I eksemplet er dette demonstreret ved knapperne for<br />
  oven, der er indsat som almindeligt HTML markup, og hvordan disse får kameraet til at<br />
  svinge rundt om klodsen.</li>
<li>Dette demonstrerer en anden mulighed i O3D, nemlig at brugerne<br />
  kan bevæge sig rundt i scenen.</li>
<li>Direkte interaktion med 3D miljøet er også en mulighed. I<br />
  eksemplet illusteres dette ved at brugeren kan klikke på en figur i<br />
  miljøet og blive dirigeret til en ny side.</li>
</ul>
<p>
O3D giver dog mulighed for langt mere end bare illustreret i dette ene<br />
eksempel. De individuelle figure i scenen kan også bevæge sig, der kan<br />
lægges texture på figurene, scener fra Maya og 3D Studio Max kan<br />
importeres og der er understøttelse for shaders.
</p>
<p>
Flere eksempler og færdige programmer kan findes på<br />
<a href="http://code.google.com/apis/o3d/docs/samplesdirectory.html">code.google.com</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.hinnerup.net/permanent/2009/07/13/o3d-3d-i-browsereo3d-3d-in-browsers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Et par tricks med selvmodificerende JavaScript kode</title>
		<link>http://www.hinnerup.net/permanent/2009/02/18/self-modifying-javascript-code/</link>
		<comments>http://www.hinnerup.net/permanent/2009/02/18/self-modifying-javascript-code/#comments</comments>
		<pubDate>Wed, 18 Feb 2009 15:05:46 +0000</pubDate>
		<dc:creator>Michael Schøler</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Programmering]]></category>
		<category><![CDATA[Self modifying code]]></category>

		<guid isPermaLink="false">http://www.hinnerup.net/?p=207</guid>
		<description><![CDATA[JavaScript sproget er dynamisk, hvilket vil sige at mens ens script afvikles kan alt modificeres. Har man et objekt, er det muligt at tilføje og fjerne properties på det. Sagt med andre ord er det muligt at modificere den kode der afvikles, både ved at tilføje, fjerne og overskrive funktioner samt variable. Note: I de [...]]]></description>
			<content:encoded><![CDATA[<p>JavaScript sproget er dynamisk, hvilket vil sige at mens ens script afvikles kan alt modificeres. Har man et objekt, er det muligt at tilføje og fjerne <i>properties</i> på det. Sagt med andre ord er det muligt at modificere den kode der afvikles, både ved at tilføje, fjerne og overskrive funktioner samt variable.</p>
<div style="font-size:8pt;font-family:arial, sans-serif; padding-left:3em"><b>Note:</b> I de efterfølgende eksempler er der lagt vægt på at illustrere den selvmodificerende kode, og ikke på at opstille gyldig HTML (doctype angivelser med videre er udeladt for overblikkets skyld), og tilsvarende anvendes blot &#8220;onload&#8221; istedet for en mere <a href="http://www.quirksmode.org/js/events_advanced.html">korrekt cross browser metode</a>.
</div>
<p>De fleste kender til selvmodificerende javascript kode fra event handlers. Når et DOM elements &#8220;onclick&#8221; sættes til at pege på en javascript funktion, udnyttes faktisk blot javascript sproget selvmodificerende egenskaber:</p>
<pre>
&lt;html&gt;
&lt;body&gt;
&lt;input type="button" id="knap" value="Tryk på mig" /&gt;
&lt;script type="text/javascript"&gt;
function haandterKnapKlik() {
  alert("Hehe - det kilder!");
}
var knapObjekt = document.getElementById("knap");
knapObjekt.onclick = haandterKnapKlik;
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<input type="button" id="knap" value="Tryk på mig" />
<p><script type="text/javascript">
function haandterKnapKlik() {
  alert("Hehe - det kilder!");
}
var knapObjekt = document.getElementById("knap");
knapObjekt.onclick = haandterKnapKlik;
</script>I ovenstående simple eksempel sættes onclick eventhandler funktionen ved hjælp af et inline script. Det samme resultat kan opnåes med dynamisk en tilføjet funktion som her:</p>
<pre>
&lt;html&gt;
&lt;body&gt;
&lt;input type="button" id="knap" value="Tryk på mig" /&gt;
&lt;script type="text/javascript"&gt;
var knapObjekt = document.getElementById("knap");
knapObjekt.onclick = function () {
  alert("Hehe - det kilder osse!");
}
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<input type="button" id="knap2" value="Tryk på mig" />
<p><script type="text/javascript">
var knapObjekt = document.getElementById("knap2");
knapObjekt.onclick = function () {
  alert("Hehe - det kilder osse!");
};
</script>Fordelen ved denne metode, er at global scopet ikke bliver &#8220;forurenet&#8221; med funktionsnavne og variable. Situationen kan nemt opstå hvor der er sammenfald på navne af funktioner og variable, og det kan være ganske lumsk at debugge frem til hvor sådanne fejl er begravet, da det er helt legalt at overskrive en funktion til blot at være en simpel variabel (eller et objekt) og omvendt.</p>
<p>Når man skal sikre at kode kun køres en gang, kan flere strategier anvendes. Typisk ser man implementationer svarende til:</p>
<pre>
&lt;html&gt;
&lt;body onload="initialiser()"&gt;
var initialiseret = false;
function initialiser() {
  if (initialiseret !== false) {
    return;
  }
  // udfør initialiseringen her ...
  initialiseret = true;
}
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Der er flere problemer med ovenstående. Dels ligger funktionen &#8220;initialiser&#8221; og variablen &#8220;initialiseret&#8221; i global scopet og er dermed i fare for at blive overskrevet af andre inkluderede scripts på siden, og der er intet der forhindrer at variablen &#8220;initialiseret&#8221; senere sættes til false, hvorefter koden fint kan genafvikles.</p>
<p>Istedet kan anvendes en anden strategi med selvmodificerende kode som angivet herunder:</p>
<pre>
&lt;html&gt;
&lt;body onload="hinnerupnet.initialiser()"&gt;
var hinnerupnet = {
  initialiser: function () {
    // udfør initialiseringen her ...
    delete this.initialiser;
  },
  variabelBar: "Hejsa",
  metodeFoo: function () {
    alert(this.variabelBar);
  }
};
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Funktionen &#8220;initialiser&#8221; er her pakket ind i et objekt kaldet &#8220;hinnerupnet&#8221;. Kun selve objekt variablen &#8220;hinnerupnet&#8221; er placeret i global scope. Både funktionen &#8220;initialiser&#8221;, &#8220;metodeFoo&#8221; og variablen &#8220;variabelBar&#8221; kan dermed kun tilgåes via objektet &#8220;hinnerupnet&#8221;. Chancerne for at andre scripts på siden skulle overskrive &#8220;hinnerupnet&#8221; objektet er minimale, og bør være under programmørens egen kontrol.</p>
<p>Ved første kald til &#8220;hinnerupnet.initialiser()&#8221; udføres koden og der afsluttes med helt at slette funktionen fra objektet. Eventuelle efterfølgende kald til funktionen leder til en exception:</p>
<pre>
&gt;&gt;&gt; hinnerupnet.initialiser is not a function
</pre>
<p>Et alternativ fremt for helt at slette en funktion er at modificere koden heri, for eksempel:</p>
<pre>
&lt;html&gt;
&lt;body onload="hinnerupnet.initialiser()"&gt;
var hinnerupnet = {
  initialiser: function () {
    // udfør initialiseringen her ...
    this.initialiser = function () {
      alert("Fejl: 'hinnerupnet' objektet er allerede initialiseret!");
    };
  }
};
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Første kald til &#8220;hinnerupnet.initialiser()&#8221; vil udføre den tiltænkte initialiseringskode, og slutteligt overskrive initialiseringsfunktionen således &#8220;hinnerupnet&#8221; objektet efterfølgende er:</p>
<pre>
var hinnerupnet = {
  initialiser: function () {
    alert("Fejl: 'hinnerupnet' objektet er allerede initialiseret!");
  }
};
</pre>
<p>Leg selv videre med mulighederne, de er mange!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hinnerup.net/permanent/2009/02/18/self-modifying-javascript-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JSLint Multi widget nævnt på Ajaxian.com</title>
		<link>http://www.hinnerup.net/permanent/2009/01/23/jslint-multi-widget-mentioned-on-ajaxian/</link>
		<comments>http://www.hinnerup.net/permanent/2009/01/23/jslint-multi-widget-mentioned-on-ajaxian/#comments</comments>
		<pubDate>Fri, 23 Jan 2009 13:05:08 +0000</pubDate>
		<dc:creator>Michael Schøler</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Programmering]]></category>

		<guid isPermaLink="false">http://www.hinnerup.net/?p=186</guid>
		<description><![CDATA[JSLint Multi v1.0 widget af Michael Schøler og Jakob Kruse er nu lagt ud til fri download på code.google.com. Med JSLint Multi v1.0 kan man monitorere multiple javascript filer og kontinuerligt få dem verificeret med den nyeste JSLint motor. JSLint Multi benytter Douglas Crockford&#8217;s fortrinlige JSLint værktøj til at undersøge dine JavaScript filer for potentielle [...]]]></description>
			<content:encoded><![CDATA[<div style="float:right;padding-left:1em;"><img src="/wp-content/uploads/2009/01/jslint_multi_v1_scrshot.png" alt="JSLint Multi v1.0 skærmbillede" /></div>
<p>JSLint Multi v1.0 widget af <a href="http://michael.hinnerup.net">Michael Schøler</a> og <a href="http://www.kruse-net.dk">Jakob Kruse</a> er nu lagt ud til fri download på <a href="http://code.google.com/p/jslint-multi-widget/">code.google.com</a>. </p>
<p>Med JSLint Multi v1.0 kan man monitorere multiple javascript filer og kontinuerligt få dem verificeret med den nyeste <a href="http://jslint.org">JSLint</a> motor.</p>
<p>JSLint Multi benytter <a href="http://www.crockford.com/">Douglas Crockford&#8217;s</a> fortrinlige JSLint værktøj til at undersøge dine JavaScript filer for potentielle problemer.</p>
<p>Træk blot en mappe ind over JSLint Multi, og den giver dig et overblik over JSLint tilstanden for hver JavaScript fil i mappen og dennes undermapper. Klik på en fil i listen, eller træk en enkelt fil, URI eller &#8220;copy/paste&#8221; noget JavaScript kode over widget&#8217;en, for at få vist den først funde fejl heri. Hvis du editerer en fil der monitoreres, vil JSLint Multi øjeblikkeligt genundersøge filen, og nye filer i mappen og/eller undermapper opdages tilmed også med det samme!</p>
<p>Kræver at <a href="http://widgets.yahoo.com/">Yahoo! Widgets</a> er installeret.</p>
<div style="float:left;padding-right:1em;"><img src="/wp-content/uploads/2009/01/ajaxian_logo.jpg" alt="Ajaxian.com" /></div>
<p> Vores widget er blevet nævnt på forsiden af <a href="http://ajaxian.com/archives/jslint-multi">Ajaxian.com</a> idag &#8211; <i>how cool is that?!</i> <img src='http://www.hinnerup.net/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> <br style="clear:both;" /></p>
]]></content:encoded>
			<wfw:commentRss>http://www.hinnerup.net/permanent/2009/01/23/jslint-multi-widget-mentioned-on-ajaxian/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Multiple redigerbare overlays til Google Maps API</title>
		<link>http://www.hinnerup.net/permanent/2008/11/05/google-maps-api-multiple-editable-overlays/</link>
		<comments>http://www.hinnerup.net/permanent/2008/11/05/google-maps-api-multiple-editable-overlays/#comments</comments>
		<pubDate>Wed, 05 Nov 2008 19:58:29 +0000</pubDate>
		<dc:creator>Michael Schøler</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Programmering]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[GIS]]></category>
		<category><![CDATA[Google maps]]></category>
		<category><![CDATA[GPS]]></category>
		<category><![CDATA[JSON]]></category>

		<guid isPermaLink="false">http://www.hinnerup.net/?p=130</guid>
		<description><![CDATA[Jeg har dags dato lagt sidste hånd på et lidt spændende proof-of-concept eksperiment. Vi skal til en opgave kunne vise valgfrie polygoner over et zoombart verdenskort. Disse opsatte og redigerbare kort-koordinat polygoner (længde og breddegrader) skal derefter nemt kunne anvendes som søgekriterier i en MS SQL database indeholdende en datatabel med blandt andet en GPS [...]]]></description>
			<content:encoded><![CDATA[<p>Jeg har dags dato lagt sidste hånd på et lidt spændende proof-of-concept eksperiment. Vi skal til en opgave kunne vise valgfrie polygoner over et zoombart verdenskort. Disse opsatte og redigerbare kort-koordinat polygoner (længde og breddegrader) skal derefter nemt kunne anvendes som søgekriterier i en MS SQL database indeholdende en datatabel med blandt andet en GPS koordinat kolonne.</p>
<p>Google Maps virkede som et fornuftigt udgangspunkt hertil, så der gik jeg igang.</p>
<p>Den indledende øvelse, du kan se resultatet af herunder, gik på at lave en minimalistisk webside hvor en bruger nemt kan opsætte en eller flere regioner og her skal være istand til at redigere og slette disse. Polygonerne skal slutteligt kunne &#8220;oversættes&#8221; til en række kort-koordinater til den videre database behandling (der ligger udenfor proof-of-concept eksemplets omfang).</p>
<div style="text-align:center">
<a href="http://www.hinnerup.net:10080/2008/10/gmaps/index.html"><img src="/wp-content/uploads/2008/11/gmaps_multi_overlay_js.png" alt="Multiple redigerbare overlays til Google Maps..." title="gmaps_multi_overlay_js" class="wp-image-131" style="border:none" /></a>
</div>
<p>Prøv selv dette eksempel med <a href="http://www.hinnerup.net:10080/2008/10/gmaps/index.html">multiple editerbare overlays</a>.</p>
<p>Hvert polygon brugeren definerer i ovenstående eksempel kan udtrækkes på JSON form, indeholdende alle koordinater som længde- og breddegrader, som for eksempel:</p>
<pre>
{
  'points': [
    { 'lat': 55.70685277146149, 'lng': 12.535314559936523 },
    { 'lat': 55.70685277146149, 'lng': 12.538447380065918 },
    { 'lat': 55.70571631025774, 'lng': 12.540678977966308 },
    { 'lat': 55.705184338337496, 'lng': 12.538447380065918 },
    { 'lat': 55.70426546069018, 'lng': 12.53763198852539 },
    { 'lat': 55.705184338337496, 'lng': 12.535314559936523 },
    { 'lat': 55.70685277146149, 'lng': 12.535314559936523 }
]};
</pre>
<p>Disse koordinat data kan nu anvendes server-side som søgekriterier i MS SQL 2005, for eksempel ved brug af <a href="http://www.codeplex.com/MsSqlSpatial">MsSqlSpatial</a> udvidelsen. Eksempler på MS SQL GIS data indsættelse og forespørgsler findes på <a href="http://www.codeplex.com/MsSqlSpatial/Wiki/View.aspx?title=How%20to%20load%20data%20from%20non-spatial%20sources&#038;referringTitle=Tutorials">MsSqlSpatial</a> siden. MS SQL 2008 har indbygget understøttelse af GIS datatyper og søgning heri &#8211; <a href="http://www.microsoft.com/sqlserver/2008/en/us/spatial-data.aspx">Geometry (planar) og Geography (geodetic)</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hinnerup.net/permanent/2008/11/05/google-maps-api-multiple-editable-overlays/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Klasser og klassenedarvning i JavaScript</title>
		<link>http://www.hinnerup.net/permanent/2008/04/11/javascript_classes/</link>
		<comments>http://www.hinnerup.net/permanent/2008/04/11/javascript_classes/#comments</comments>
		<pubDate>Fri, 11 Apr 2008 10:51:20 +0000</pubDate>
		<dc:creator>Michael Schøler</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Programmering]]></category>
		<category><![CDATA[Prototype]]></category>

		<guid isPermaLink="false">http://www.hinnerup.net/2008/04/11/javascript_classes/</guid>
		<description><![CDATA[Jeg havde i dag behov for at lære fra mig med hensyn til prototype&#8217;s JavaScript klasse implementation. Der er nogle udemærkede eksempler at finde men de var ikke optimalt nemme at forklare alle begreberne udfra &#8211; man skulle have lidt baggrundsviden med sig i hvert fald. Det endte med at en mindre omskrivning af første [...]]]></description>
			<content:encoded><![CDATA[<p><img src='http://www.hinnerup.net/wp-content/uploads/2008/04/class_dog_animal1.jpg' alt='Klassediagram' style='float:right;margin-left:2em;margin-bottom:1em;margin-top:0.5em' />Jeg havde i dag behov for at lære fra mig med hensyn til <a href="http://www.prototypejs.org/api/class">prototype&#8217;s JavaScript klasse implementation</a>. Der er nogle udemærkede eksempler at finde men de var ikke optimalt nemme at forklare alle begreberne udfra &#8211; man skulle have lidt baggrundsviden med sig i hvert fald.</p>
<p>Det endte med at en mindre omskrivning af <a href="http://www.prototypejs.org/api/class/addMethods">første eksempel på prototype hjemmesiden</a> var alt der skulle til for at kunne formidle hvordan JavaScript klasser kan benyttes.</p>
<p>Her er det omskrevne eksempel, der blandt andet har flere instanser med og en lidt klarere constructor/super constructor illustration:</p>
<pre>
// Animal base class
var Animal = Class.create({
  initialize: function(type, name, sound) { // constructor
    this.type = type;
    this.name = name;
    this.sound = sound;
  },
  speak: function() {
    alert("The " + this.type + " named " +
      this.name + " says " + this.sound);
  }
});

// Extended class (inherits from Animal)
var Dog = Class.create(Animal, {
  initialize: function($super, name) { // constructor
    $super("Dog", name, "Woof Woof!"); // call super class constructor
  },
  sit: function() { // extended function
    alert("The " + this.type + " named " + this.name + " is now sitting.");
  }
});

var duck = new Animal("Duck", "Daffy", "Quack!");
var dog1 = new Dog("Pluto");
var dog2 = new Dog("King");
duck.speak();
<i style="color:#777;">// --> alerts "The Duck named Daffy says Quack!"</i>
dog1.speak();
<i style="color:#777;">// --> alerts "The Dog named Pluto says Woof Woof!"</i>
dog2.sit();
<i style="color:#777;">// --> alerts "The Dog named King is now sitting."</i>
dog2.speak();
<i style="color:#777;">// --> alerts "The Dog named King says Woof Woof!"</i>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.hinnerup.net/permanent/2008/04/11/javascript_classes/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

