August 2009 - Posts

Med tiden som webbläsare utvecklas och kan tolka JavaScript snabbare så ökar möjligheten att skriva tunga webbaserade applikationer baserade på just JavaScript. När JavaScript-funktionerna blir mer avancerade så blir även logiken bakom det mer komplicerad. Det händer allt oftare att man har en stor mängd affärslogik i JavaScript.

För den affärslogiken som är baserad på server-kod som till exempel C# så är det lätt att utveckla den enligt TDD (Test Driven Development) vilket ökar säkerheten i koden och minskar antalet buggar genom att man skriver tester och sedan själva logiken.

Vad gäller JavaScript så brukar det ofta bli så att man utvecklar JavaScripten allt eftersom (gärna med ett bibliotek som jQuery, MooTools, YUI, Prototype eller liknande bakom). Dessvärre så har bristen på testverktyg (eller åtminstonde uppmärksamheten bland dessa) lett till att det lätt kan bli fel. En anledning till detta är att det är svårt att testa alla funktioner i alla webbläsare, då olika webbläsare tenderar att tolka ECMAScript-standarden olika.

För att göra det lättare för sig så finns det faktiskt ett par verktyg som kan lösa det här, och ett som jag gillar är JsUnitTest. Det gör det möjligt att skapa HTML-dokument som sedan används för att testa de olika funktionerna. Det är ett verktyg som är helt baserat på JavaScript, och är taget från Prototype. Det kräver dock inte att man använder Prototype själv på sidan.

Jag kommer att gå igenom ett enkelt exempel där vi skapar en Bil-klass med olika egenskaper.

Först och främst så behöver vi ladda ned js-filen för JsUnitTest (verktyget i sig är skrivet i JavaScript). Den finns här:

http://jsunittest.com/dist/jsunittest.js

Sen behöver vi skapa upp ett enkelt HTML-dokument där vi skall ha våra tester.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <title>Test: Bil</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <script src="../assets/jsunittest.js" type="text/javascript"></script>
    <script src="../assets/bil.js" type="text/javascript"></script>
    <link rel="stylesheet" href="../assets/unittest.css" type="text/css" />
</head>
<body>
    <div id="content">
        <div id="header">
            <h1>Test: Bil</h1>
            <p>
                This file tests <strong>bil.js</strong>.
            </p>
        </div>
        <div id="testlog" />
    </div>
 
    <script type="text/javascript">
        //Här kommer våra tester
    </script>
 
</body>
</html>

Mitt projekt i Visual Studio ser ut så här nu:

1

Bil.js låter vi än så länge vara tom, och unittest.css kan ni antingen ladda ned i ett demoprojekt från JsUnitTests hemsida, eller genom den bifogade filen i bloggposten.

Till att börja med så ska vi lägga till ett test i html-sidan som kollar om objektet ”Bil” existerar.

Vi lägger till detta i script-elementet på sidan:

var test = new Test.Unit.Runner({
 
    testBilFinns: function() {
        with (this) {
            var bil = Bil;
            assert(bil);
        }
    }
});

Alla test vi skapar måste skickas in som parameter i metoden Test.Unit.Runner. Här har vi ett test vid namn ”testBilFinns” och som helt enkelt kollar om objektet finns. Om man använder ett objekt som är null i en if-sats så returneras false, annars true. Därmed kan vi enkelt kolla om objektet existerar genom att köra en assert på objektet i sig.

När vi nu kör sidan så kommer div-elementet med id ”testlog” att fyllas på med information från testet.

Det vi får upp är detta:

2

Eftersom vi inte har skapat objektet så får vi undefined som svar, vilket betyder att det inte är skapat.

Om vi öppnar upp bil.js och lägger till detta:

var Bil = { }

Så får vi istället:

3

Eftersom vi nu har skapat upp objektet “Bil” så går testet igenom.

Nu hoppar jag fram lite för att inte behöva ta upp alla olika tester, utan tar upp ett exempel där vi har två properties, en för färgen på bilen och en som visar antalet däck.

Först och främst så ska vi ha två tester som kollar de olika funktionera. I båda testerna så skickar vi in värden till metoder som vi sedan skall få tillbaka.

Testerna:

testBilensFärgÄrBlå: function() {
    with (this) {
        var bil = Bil;
        bil.setFärg('blå');
        assert(bil.färg, 'blå');
    }
},
 
testBilenHarFyraDäck: function() {
    with (this) {
        var bil = Bil;
        bil.setDäck(4);
        assert(bil.däck == 4);
    }
}

De här testerna skall ligga i samma metod som det första testet.

När vi kör det så får vi:

4

Eftersom vi inte har skapat upp egenskaperna bil.färg och bild.däck, samt metoderna bil.setFärg och bil.setDäck så får vi fel här.

Genom att skapa upp dem:

var Bil = {
    färg: null,
    däck: 0,
    setFärg: function(färg) { this.färg = färg; },
    setDäck: function(däck) { this.däck = däck; }
}

Så får vi:

5

Är man petig så finns det många fler tester att skapa upp på vägen, men det var bara en demo over hur det fungerar, så klaga inte på det. ;-)

Det är alltså möjligt att genom att skapa upp separata html-filer, kunna testa sina JavaScript-metoder på ett väldigt enkelt sätt. Genom att öppna upp dessa HTML-filer i andra webbläsare så kan vi även se om de fungerar som väntat i dessa vilket kan spara enorma mängder tid under felsökning.

Lycka till och testa på!

Ladda ned exemplet

När man utvecklar klasser som innehåller många överlagrade metoder så händer det ofta att man först har en “huvudmetod” som innehåller alla parametrar, och sedan ett par överlagrade metoder med ett visst antal parametrar, vilka sedan anropar huvudmetoden.

Ett exempel på det är:

using System;
 
namespace OptionalNamedParameters
{
    public class OldParameters
    {
        public static string HelloWorld(string Word1, string Word2, string Word3)
        {
            return String.Format("{0} {1} {2}", Word1, Word2, Word3);
        }
 
        public static string HelloWorld(string Word1, string Word2)
        {
            return HelloWorld(Word1, Word2, "Tom");
        }
 
        public static string HelloWorld(string Word1)
        {
            return HelloWorld(Word1, "Tom", "Tom");
        }
    }
}

Här har vi tre överlagrade metoder. Den första har tre parametrar och returnerar alla strängar hopsatta med mellanslag som separator. De övriga två anropar den första, men med värdet ”Tom” för den eller de parametrar som inte används.

Om vi sedan kör den här koden:

Console.WriteLine(OldParameters.HelloWorld("Hello,", "world", "!"));
Console.WriteLine(OldParameters.HelloWorld("Hello,", "world"));
Console.WriteLine(OldParameters.HelloWorld("Hello,"));

Så får vi:

Hello, world !
Hello, world Tom
Hello, Tom Tom

Problemet här är att vi väldigt ofta behöver skapa metoder likt dessa för att ändå bara anropa en av dem, vilket gör att vi får fler metoder som inte tillför något.

En lösning på detta har sedan länge funnits i Visual Basic, nämligen optional parameters. I och med .NET 4.0 så kan vi använda detta även med C#. Vi kan med optional parameters förkorta ned detta till enbart en enda metod, men som har standardvärden för de parametrar som inte behöver anges.

För att göra detta så lägger vi till =”Standardvärde” efter parametern.

using System;
 
namespace OptionalNamedParameters
{
    public class NewParameters
    {
        public static string HelloWorld(string Word1, string Word2="Tom", string Word3="Tom")
        {
            return String.Format("{0} {1} {2}", Word1, Word2, Word3);
        }
    }
}

Här har vi först en obligatorisk parameter (Word1), och sedan två valfria med standardvärden satta. Genom att lägga till ett standardvärde så blir parametern automatiskt valfri att använda. Om parametern inte skickas in så används standardvärdet.

För att använda detta så kan vi köra samma kod som innan (jag har dock ett annat klassnamn i exemplet):

Console.WriteLine(NewParameters.HelloWorld("Hello,", "world", "!"));
Console.WriteLine(NewParameters.HelloWorld("Hello,", "world"));
Console.WriteLine(NewParameters.HelloWorld("Hello,"));

Detta ger samma resultat som innan:

Hello world !
Hello world Tom
Hello Tom Tom

Genom att sätta standardvärden på de två valfria parametrarna så har vi gått från tre metoder till en enda. Det här gör koden mer lättläst och lättare att underhålla. Vid de fall där man har många olika varianter så kan det göra stor skillnad.

Problemet nu uppstår om vi vill använda specifika parametrar här, och inte alla i ordning. Det kan vi lösa med en annan nyhet – named parameters.

För att använda named parameters så anger vi vid anropet namnet på parametern och dess värde separerat med kolon. Parametrarna behöver inte anges i rätt ordning, utan vi kan blanda som vi vill.

Om vi tar den nya klassen med bara en metod så kan vi enkelt använda detta:

Console.WriteLine(NewParameters.HelloWorld(Word3: "!", Word2: "world", Word1: "Hello"));
Console.WriteLine(NewParameters.HelloWorld(Word2: "world", Word1: "Hello"));
Console.WriteLine(NewParameters.HelloWorld(Word1: "Hello"));

Här anropar vi parametrarna i bakvänd ordning. Vi anropar dock exakt samma parametrar med samma värden som innan, så om vi kör det så får vi återigen:

Hello world !
Hello world Tom
Hello Tom Tom

Det är även möjligt att ange parametrarna som vanligt, samtidigt som vi använder namngivna parametrar för de resterande parametrarna.

Ett exempel på det är:

Console.WriteLine(NewParameters.HelloWorld("Hello", Word3: "!", Word2: "world"));
Console.WriteLine(NewParameters.HelloWorld("Hello", Word2: "world"));

I det första exemplet så anger vi “Hello” som den första parametern, “!” som den tredje parametern (dock tvåa i ordningen), samt ”world” som den andra parametern (sist i ordningen). Vi kan inte flytta den första parametern här, utan de som inte är namngivna måste vi ange innan de som är namngivna.

Tack vare stödet för named och optional parameters i C# 4.0 så kan vi förminska antalet rader kod och samtidigt få den mer läsvänlig.

Det finns en intressat tråd på Aspsidan om hur man komprimerar sin HTML så att de går mycket snabbare att ladda genom att använda deflate eller GZip.

Tråden kan du hitta här.

I tråden tas det upp hur det görs med aspx-filer, men man bör även göra detta med andra filer så som css och js.

Det finns även andra sätt att få ned sidans storlek på, vilket jag tar upp nedan.

CSS Sprites

Ett annat sätt att få ned storleken på är att använda så kallade “CSS Sprites”. Det går ut på att man tar flera bilder (ofta mindre ikoner) och lägger de i samma bild. Sedan använder man CSS för att visa den aktuella bilden. Då det är samma bild som laddas in så behöver man bara göra en request istället för flera.

Exempel på CSS Sprites:

Använd YSlow för Firebug

Alla webbutvecklare måste använda sig utav Firebug när de utvecklar. Det finns developer tools inbyggt i Internet Explorer 8, men Firebug är fortfarande det absolut bästa som finns. Det finns även mängder av tillägg till det, och ett som underlättar när man ska få ned laddningstiden på sidan är YSlow från Yahoo!.

yslow

Packa ihop dina js- och css-filer

Js- och css-filer kan ofta bli väldigt stora, samtidigt som de innehåller många tabbar och whitespaces. Genom att plocka bort dessa så kan man få ned filstorleken väldigt mycket. Om man kikar på jQuery så finns det två varianter, en vanlig vilken används för utveckling, och en minimerad (innehåller “min” i filnamnet) vilken kan användas för produktion.

För att få ned storleken på filerna så finns dessa otroligt bra verktyg:

Få ned antalet requests

Något man bör satsa på är att ha så få requests som möjligt. Ett sätt att lösa det på är att använda CSS Sprites, vilket jag nämnde tidigare. Utöver det så kan man även packa ihop sina css- och js-filer så att man har en av varje istället för kanske 10. Ofta så används ett flertal filer för t.ex. jQuery, sidspecifika javascript m.m., men genom att få ihop dessa till en enda fil så blir det färre requests och sidan går snabbare att ladda.

Har du egna tips på hur man kan få sidan snabbare så skriv gärna ned detta!

I have just released SPLibrary, a SharePoint Library written in C# on CodePlex. It works with the SharePoint web services and do not have to run on the same server as SharePoint.

Examples

Get all sites

SPLibrary sp = new SPLibrary(new Uri("http://moss"), new NetworkCredential("user", "password", "domain"));
SPWeb[] webs = sp.GetWebs();

Get all lists for a site

SPLibrary sp = new SPLibrary(new Uri("http://moss"), new NetworkCredential("user", "password", "domain"));
SPList[] lists = sp.GetLists();

Get a specific list

SPLibrary sp = new SPLibrary(new Uri("http://moss"), new NetworkCredential("user", "password", "domain"));
SPSingleList list = sp.GetList("List ID (GUID)");

Get all items in a list

SPLibrary sp = new SPLibrary(new Uri("http://moss"), new NetworkCredential("user", "password", "domain"));
SPListItem[] listitems = sp.GetListItems("List ID (GUID)"); //All items
SPListItem[] listitems = sp.GetListItems("List ID (GUID)", Dictionary<string, string>); //Filtered


Search the site

SPLibrary sp = new SPLibrary(new Uri("http://moss"), new NetworkCredential("user", "password", "domain"));
SPSearchResult[] search = sp.Search(""); //6 different overloads for filtering

 

You can find more information and the source code here:

http://splibrary.codeplex.com/

Damn, damn, damn, DAMN!

Twitter is down and damn, I can´t tweet!

I was going to tweet about this, but DAMN Twitter is down!

Hi, my name is Mikael Söderström and I´m a Twittoholic.

I have just added som new features to the Widget Emulator.

The first one is the Devices.js file. I have added all different resolutions and settings for both Windows Mobile 6.5 Professional and Standard here, but you can add your own if you want to.

The second new feature is to change the resolution of the emulator. You can choose from the resolutions specified in Devices.js.

2-1

Since some phones have the ability to show widgets in both portrait mode and landscape mode, I have added that aswell.

The same resolution in landscape mode:

2-2

Download from CodePlex!

I have just released the first preview of a webbased Windows Mobile 6.5 Widget Emulator.

You can test your widgets without sending them to a real Windows Mobile phone or emulator but directly in your browser.

It supports most of the API’s provided in the Widget engine on Windows Mobile, such as menu, preferenceForKey() and so on.

Information about it here:

http://widgetemulator.codeplex.com/

There is one RSS widget in the package, that you can test the emulator with:

emulator

Skapa Widgets för Windows Mobile 6.5

I Windows Vista kom möjligheten att skapa Gadgets, vilket är små applikationer som gör det enkelt att komma åt data från olika hall. Dessa är skrivna i HTML, CSS och JavaScript, vilket gör att det är otroligt enkelt att skapa dem. De packas sedan ned i en zip-fil som får filändelsen ”.gadget”, vilket gör att man kan installera dem lokalt och sedan köra direkt på skrivbordet.

I Windows Mobile 6.5 så introduceras något kallat ”Widgets”. Dessa är uppbyggda på samma sätt som gadgets, det vill säga av just HTML, CSS och JavaScript. Skillnaden här är att de istället för att köras i en liten ruta körs som en fullfjädrad applikation. De får även filändelsen ”.widget” istället för ”.gadget”.

Dessa små applikationer körs genom Internet Explorer, och har ingen möjlighet att komma åt filsystemet eller en databas som SQL Server direkt, utan kör istället med egen lokal lagring genom JavaScript-API:er. Det gör dem till säkra program som kan köras utan att behöva några speciella rättigheter.

Tidigare så har man behövt skriva applikationer för Windows Mobile i t.ex. .NET, vilket har krävt att man sitter i Windows, men då dessa Widgets är uppbyggda på standardiserade webbtekniker så går det att utveckla och testa dem på vilken plattform som helst!

Kom igång!

Så, vad behövs för att skapa en Widget? Då det är uppbyggt med HTML, CSS och JavaScript så behöver vi inget annat än just en text-editor, som t.ex. anteckningar eller Visual Studio. Jag själv använder Visual Studio 2010 då det är mitt förstaval vad gäller all utveckling.

För att kunna testa dessa så behövs Windows Mobile 6.5. Då det inte är alla som har det på sina mobiltelefoner än så finns det emulatorer att ladda ned, vilket gör det mycket enklare.

Emulatorer för Windows Mobile 6.5 finns att ladda ned här:

http://www.microsoft.com/downloads/details.aspx?FamilyID=20686a1d-97a8-4f80-bc6a-ae010e085a6e&displaylang=en

När emulatorn är installerad så finns det olika varianter i startmenyn, där WVGA är den som har högst upplösning (480x800, vilket används för t.ex. HTC Touch HD). Vilken ni tar spelar dock ingen större roll.

Ni bör dock tänka på att Windows Mobile Professional har stöd för Touch, medan Standard bara har knappar!

Om vi startar upp en emulator så finns det faktiskt redan några Widgets förinstallerade. Ett exempel på det är MSN Money som visar aktuella aktiekurser. Dessa är helt byggda med de tekniker vi kommer att använda oss utav här.

Skapandet av en enkel widget

En Widget är som jag nämnde tidigare helt enkelt en zip-fil som innehåller en HTML-sida med dess resurser. Ett krav för varje Widget är att den skall innehålla minst en ikon, en HTML-sida samt en XML-fil som beskriver Widgeten.

Till att börja med så behöver vi en enkel HTML-sida, som vi kallar för index.html. Denna kan se ut så här:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 Strict" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    <title>Min widget</title>
</head>
<body>
    <h1>Min widget</h1>
    <p>
        Det här är min nya widget!
    </p>
</body>
</html>

Sen behöver vi en ikon, och då använder jag Aspsidans favicon för enkelhetens skull.

clip_image001

Det sista som behövs är XML-filen. Den har olika element som kan användas, där ”content” är obligatorisk.

Namn på element

Beskrivning

access

Har ett attribut vid namn ”network” och som kan ha värden true eller false. För att Ajax-anrop skall kunna göras mot sidor på Internet så måste detta vara true, samtidigt som användaren måste godkänna detta.

author

Author innehåller information om personen som har skapat widgeten. Värdet i denna skall vara namnet på personen. Det finns även attributen ”href” som är URL:en till personens hemsida, ”email” som är personens e-postadress och ”img” som är en bild på personen.

content

Det här är det enda obligatoriska elementet. Det har ett attribut vid namn ”src” och som pekar mot den html-fil som skall vara startsidan i vår widget.

description

När widgeten skall installeras första gången så kommer en text fram som beskriver vad widgeten har för syfte. Texten skall vara elementets värde.

icon

När widgeten är installerad så visas den i startmenyn med en ikon och namnet. Här kan vi med attributet ”src” ange sökvägen till vår ikon.

name

För att vara säkra på att widgeten har rätt namn i startmenyn så kan vi ange det här.

Min XML-fil ser ut så här, men ni kan självklart anpassa dessa efter er egen Widget:

<?xml version="1.0" encoding="utf-8" ?>
<widget xmlns="http://www.w3.org/ns/widgets" version="1.0">
    <content src="index.html" type="text/html" />
    <icon src="favicon.png" />
    <name>Min widget</name>
    <description>Det här är min nya widget!</description>
    <author href="http://weblogs.asp.net/mikaelsoderstrom" email="vimpyboy@msn.com">Mikael Söderström</author>
</widget>

Det här är allt vi behöver för att nu kunna installera vår widget. För att kunna göra det så packar vi ned allt i en vanlig zip-fil och byter filändelsen till ”.widget”. Vi skall sedan föra över denna till emulatorn för att sedan kunna installera den där.

För att få tillgång till filer från vår dator till emulatorn så skall vi emulera ett minneskort. Det gör vi enklast genom att skapa upp en mapp där vi har vår widget-fil, och sedan i emulatorn välja File -> Configure… och där bläddra fram till mappen under ”Shared folder”.

Går vi sedan in i File Explorer i Windows Mobile så får vi fram widgeten under ”Storage Card”. Vi väljer här att köra den och får då upp en fråga om vi vill installera, vilket vi gör.

Det bör se ut ungefär så här när vi startar widget-filen:

clip_image002

Efter att installationen är klar så startas applikationen och vi får upp det här:

clip_image003

Vi kan även starta vår Widget från startmenyn i fortsättningen, genom att klicka på ikonen för den.

clip_image005

Nu har vi skapat en Widget som vi enkelt kan sprida vidare till andra med Windows Mobile 6.5 och låta dem installera den. Om vi gör uppdateringar i widget-filen och installerar om så kommer den att ersätta den gamla versionen automatiskt.

Vi kan även hitta vår, samt alla andra installerade widget under /Program Files/widgets/User i Windows Mobile, vilket gör att vi kan ändra direkt där om vi vill göra ändringar snabbt.

Det här är dock en väldigt enkel variant, och ger inte särskilt mycket nytta, samtidigt som det inte är den snyggaste applikationen. Vi ska därför använda CSS för att snygga till den, samt JavaScript för att få den mer dynamisk.

Dags att snygga till widgeten

För att få ett bättre utseende på widgeten så använder vi vanlig CSS. Vi kommer även att ändra lite i HTML-koden för att kunna få ett syfte med widgeten. I fortsättningen så kommer det här att byggas ut till att bli en RSS-läsare, helt baserad på vanliga webbtekniker.

Till att börja med så skapar vi en css-fil direkt i roten som heter widget.css. Vi ska även ha en referens till denna i HTML-koden, samtidigt som vi skall ändra texterna och utseendet för att passa en RSS-läsare.

Ett exempel på hur HTML-filen kan se ut är som följande:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 Strict" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    <title>Min RSS-läsare</title>
    <link href="widget.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <h1>Min RSS-läsare</h1>
    <p>Det här är min RSS-läsare.</p>
    <ul id="feeds">
        <li><a href="http://feeds.feedburner.com/MikaelSoderstrom?format=xml">Mikael Söderström</a></li>
    </ul>
    <ul id="feeditems">
        <li>
            <h2>Rubrik #1</h2>
            <span class="rss-date">2009-01-01 00:00</span>
            <span class="rss-entry">En rss-feed...</span>
            <a href="#">Läs mer...</a>
        </li>
        <li>
            <h2>Rubrik #2</h2>
            <span class="rss-date">2009-01-01 00:00</span>
            <span class="rss-entry">En rss-feed...</span>
            <a href="#">Läs mer...</a>
        </li>
    </ul>
</body>
</html>

Här har vi först och främst en lista med olika RSS-feeds (nu finns dock bara en). Sen har vi en lista med posterna i RSS-feeden som visar upp rubriken, datumet, själva texten samt en länk till inlägget.

För att få ett lite roligare utseende på detta så ska vi använda CSS. Jag använder CSS:en som följer nedan, men vill ni få den att se annorlunda ut så kommer det inte att påverka något i resten av artikeln.

body
{
    background-color: #ccc;
    margin: 0px;
    padding: 0px;
    font-family: Arial;
}
 
h1
{
    background-color: #fff;
    color: #000;
}
 
a
{
    text-decoration: none;
}
 
ul
{
    margin: 0px;
    padding: 0px;
}
 
li
{
    background-color: #fff;
    border: 1px solid #777;
    list-style-type: none;
    margin: 10px;
    padding: 5px;
}
 
ul#feeds li
{
    background-color: #444;
    border-color: #aaa;
    margin: 0px;
}
 
ul#feeds li a
{
    display: block;
    color: #fff;
}
 
ul#feeditems h2
{
    margin: 0px;
}
 
ul#feeditems span
{
    display: block;
}
 
ul#feeditems span.rss-date
{
    font-style: italic;
}

Om vi uppdaterar filerna I widget-filen och kör den i Windows Mobile igen så ser det nu ut så här:

clip_image006

Den ser helt klart bättre ut nu!

Hämta RSS-feeds

En RSS-läsare som inte hämtar RSS-feeds är inte mycket att ha. Vi kommer därför nu att använda Ajax för att hämta en RSS-feed och visa upp den när man klickar på rubriken (Mikael Söderström).

För att vi skall kunna komma åt Internet från vår widget så behöver vi lägga till följande rad i config.xml:

<access network="true" />

Först och främst så skapar vi en js-fil. Här skall vi sedan ha metoder för att skapa Ajax-anrop mot en vald RSS-feed och till sist skapa upp elementen och lista.

Flödet kommer att se ut ungefär så här:

  1. Användaren klickar på en länk i listan över RSS-feeds.
  2. Med JavaScript har vi sett till så att alla klick på länkar i listan triggar ”RenderFeed(url)”, där parametern ”url” är den aktuella länken.
  3. Vi gör ett Ajax-anrop mot den URL:en som har angivits och anropar PrintResult där vi även skickar med resultatet (RSS-feeden).
  4. Vi renderar HTML för alla items i listan. För själva innehållet så plockar vi enbart ut de 350 första tecknen, då vi vill klicka på länken för att få se mer.
  5. Vi skriver ut vår nya HTML på sidan.

Jag kommer inte att gå igenom JavaScriptet mer grundläggande, då det inte är specifikt för Widgets. Det som visas här är bara helt vanlig JavaScript som kan köras direkt i webbläsaren på datorn.

JavaScriptet som jag kommer att använda ser ut som följande:

window.onload = function() {
    SetupRssFeedList();
};
 
function SetupRssFeedList() {
    var feeds = document.getElementById('feeds').getElementsByTagName('a');
    
    for (var i = 0; i < feeds.length; i++) {
        feeds[i].onclick = function() {
            RenderFeed(event.srcElement.href);
            return false;
        }
    }
}
 
function RenderFeed(url) {
    var xmlhttp = null;
    if (window.XMLHttpRequest)
        xmlhttp = new XMLHttpRequest();
    else
        return;
 
    xmlhttp.open('GET', url);
 
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4) {
            if (xmlhttp.status == 200) {
                var xmldoc = xmlhttp.responseXML;
                if (!xmldoc) return;
 
                PrintResult(xmldoc);
            }
 
            delete xmlhttp;
        }
    };
 
    xmlhttp.send();
}
 
function PrintResult(xmldoc)
{
    var threads = xmldoc.getElementsByTagName('item');
    var itemContainer = document.getElementById('feeditems');
 
    if (itemContainer.childNodes)
        itemContainer.innerHTML = '';
 
    for (var i = 0; i < threads.length; i++) {
        var newLi = document.createElement('li');
        var currentItem = threads[i];
        
        var header = document.createElement('h2');
        header.innerHTML = currentItem.getElementsByTagName('title')[0].text;
        newLi.appendChild(header);
        
        var datespan = document.createElement('span');
        datespan.className = 'rss-date';
        datespan.innerHTML = currentItem.getElementsByTagName('pubDate')[0].text;
        newLi.appendChild(datespan);
        
        var description = document.createElement('span');
        description.className = 'rss-entry';
        description.innerHTML = currentItem.getElementsByTagName('description')[0].text.substring(0, 350) + '...';
        newLi.appendChild(description);
        
        var readmore = document.createElement('a');
        readmore.href = currentItem.getElementsByTagName('link')[0].text;
        readmore.innerHTML = 'Läs mer....';
        newLi.appendChild(readmore);
        itemContainer.appendChild(newLi);
    }
}

Om vi nu sparar JavaScriptet som rss.js och lägger till raden nedan I vårt HTML-dokument så kommer vi, efter att ha klickat på rubriken för RSS-feeden, att få resultatet på bilden.

OBS! Om du kör vi emulatorn så kräver det att du har ställt in det korrekt så att du har en internetuppkoppling i den.

<script src="rss.js" type="text/javascript"></script>

clip_image008

Nu har vi en enkel RSS-läsare, som genom att man klickar på en lank i menyn kan hämta och presentera en RSS-feed.

Utöka funktionaliteten med lagring av feeds

För att få widgeten mer personaliserad så ska vi även ge möjligheten att lägga till egna feeds. Detta kan vi göra genom att använda de interna funktionerna för att dels påverka menyn, men även göra det möjligt att lagra information i cachen. Värt att tänka på är att det som lagras i cachen inte krypteras, och kan därmed genom en ”riktig” applikation läsas av. Vi kan dock inte komma åt en annan widgets cache genom en annan widget.

Det första vi ska göra är att anpassa menyn som syns längst ned. Vi skall i den vänstra menyn byta ut ”Exit” mot ”Lägg till”, och i den högra lägga till alternativet ”Om…” som skall hämta information från XML-filen och presentera.

För att påverka det som finns i menyn så kommer vi att bygga ut vårt JavaScript så att vi har metoder för det.

I window.onload där vi för närvarande enbart anropar ”SetupRssFeedList()” ska vi även lägga till ett anrop mot ”SetupMenu()”. Sedan skapar vi en ny funktion som heter just ”SetupMenu()” i js-filen. För att kunna arbeta mot menyn så kommer vi att använda objektet ”widget.menu” som är specifikt för just widgets. Vi kommer att använda detta för att dels ersätta menyn till vänster, men även lägga till ett alternativ till menyn till höger.

Funktionen ”SetupMenu()” kommer att se ut som följande:

function SetupMenu() {
    var menu = widget.menu;
    
    var leftMenu = menu.createMenuItem(0);
    leftMenu.text = "Lägg till";
    leftMenu.onSelect = function() { AddRssFeed(); };
    menu.setSoftKey(leftMenu, menu.leftSoftKeyIndex);
    
    var rightMenu = menu.createMenuItem(1);
    rightMenu.text = "Om...";
    rightMenu.onSelect = function() { alert('Skapad av ' + widget.authorName + '.'); };
    menu.append(rightMenu);
}

Först skapar vi upp en variabel som tillhandahåller referensen till widget.menu. Sedan skapar vi upp två nya variabler, en för vänstra menyn, och en för den högra. Dessa har en property vid namn ”text”, vilket är texten som skall synas i menyn. De har även ett event, ”onSelect”, vilket sker när användaren klickar på alternativet i menyn.

I den vänstra menyn så anropar vi en metod kallad ”AddRssFeed()”, vilken vi skall använda för att lägga till nya feeds. I den högra menyn så visar vi upp en alert-ruta med namnet på personen från XML-filen.

Så här ser det ut när vi kör applikationen nu:

clip_image009

Högra menyn:

clip_image010

Om…:

clip_image012

Nästa steg är att göra det möjligt att lägga till nya feeds.

I den vänstra menyn så har vi ett alternativ kallat ”Lägg till”, vilket anropar en JavaScript-funktion kallad ”AddRssFeed()”. Nästa steg är att skapa upp den. Det den skall göra är att visa två prompt-rutor, en för URL:en och en för namnet på feeden. Sedan skall vi skapa upp ett nytt li-element med en länk och lägga till i listan med RSS-feeds som vi har. Vi måste även binda om onclick på denna, då den som vi satte när sidan laddas inte fungerar med element som skapas upp i efterhand.

JavaScript-funktionen i sin helhet ser ut på detta vis:

function AddRssFeed() {
    var url = prompt('URL till RSS-feed', 'http://');
    var name = prompt('Namn på RSS-feed', '');
    var feedlist = document.getElementById('feeds');
    
    var newFeed = document.createElement('li');
    
    var feedurl = document.createElement('a');
    feedurl.href = url;
    feedurl.innerHTML = name;
    
    feedurl.onclick = function() {
        RenderFeed(event.srcElement.href);
        return false;
    }
    
    newFeed.appendChild(feedurl);
    feedlist.appendChild(newFeed);
}

Om vi t.ex. lägger till en lank till RSS-feeden som används för de senaste trådarna på Aspsidan, och sätter namnet till just “Aspsidan” för att sedan klicka på länken så blir resultatet det här:

clip_image014

Vi kan nu alltså lägga till RSS-feeds dynamiskt i vår widget, och sedan välja att hämta dem.

Problemet nu är att de nya feedsen försvinner när vi stänger ned applikationen, så det vi ska göra är att spara undan en lista på alla feeds och hämta när applikationen startas upp igen. Det kan vi göra tack vare den inbyggda cache-funktionen som finns.

Först och främst så kommer vi nu att refaktorisera funktionerna för att slippa skriva samma kod flera gånger.

Tidigare så genererade vi HTML direkt i AddRssFeed(), men nu ska vi plocka bort den biten som skapar element och lägga i en egen metod (AddFeedHtml(name, url)). Detta gör det möjligt att generera samma HTML från flera metoder.

AddFeedHtml(name, url) bör nu se ut så här:

function AddFeedHtml(name, url) {
    if (!(name && url)) return;
 
    var feedlist = document.getElementById('feeds');
 
    var newFeed = document.createElement('li');
 
    var feedurl = document.createElement('a');
    feedurl.href = url;
    feedurl.innerHTML = name;
 
    feedurl.onclick = function() {
        RenderFeed(event.srcElement.href);
        return false;
    }
 
    newFeed.appendChild(feedurl);
    feedlist.appendChild(newFeed);
}

Nästa steg är att se till så att alla egna feeds sparas ned I en lista som finns kvar efter att widgeten har stängts ned.

För att göra det så kommer vi att använda oss utav en widget-metod som heter setPreferenceForKey().

Med denna metod tillagd, samt efter att ändringen med AddFeedHtml() har gjorts så ser AddRssFeed() ut som följande:

Vår nya AddRssFeed ser ut på detta vis:

function AddRssFeed() {
    var url = prompt('URL till RSS-feed', 'http://');
    var name = prompt('Namn på RSS-feed', '');
 
    var oldList = null;
 
    if (widget.preferenceForKey('feedlist'))
        oldList = widget.preferenceForKey('feedlist') + '///url:' + url + ',name:' + name;
    else
        oldList = '///url:' + url + ',name:' + name;
    
    widget.setPreferenceForKey(oldList, 'feedlist');
 
    AddFeedHtml(name, url);
}

Det vi gör här är att vi hämtar url och namn. Sedan kollar vi om ett värde finns lagrat med nyckeln “feedlist”. Om värdet finns så hämtar vi det och lägger till vår nya feed efteråt. Om det inte finns så hämtar vi inget, utan sätter bara värdet direkt.

I vårt exempel så kommer vi att lagra alla feeds i formen ///url:UrlTillFeed,name:NamnPåFeed.

När vi har den nya strängen så använder vi setPreferenceForKey för att lagra listan med nyckeln ”feedlist”.

Det som sker nu är att vi sparar ned alla feeds som läggs till, så att vi kan komma åt dem senare. Om widgeten stängs ned så finns dem lagrade, men än så länge så syns inte det när den öppnas igen, så det vi behöver göra nu är att se till att de hämtas vid nästa start.

För att lösa det på enklaste vis så kan vi skapa en ny metod vid namn ”AddFeedList()”, och se till att denna körs i window.onload. Denna metod kommer att läsa av cachen och se om något finns lagrat. Om så är fallet så kommer vi att hämta det som finns lagrat och skapa upp nya länkar i vår lista.

AddFeedList():

function AddFeedList()
{
    if (widget.preferenceForKey('feedlist'))
    {
        var list = widget.preferenceForKey('feedlist').split('///url:');
 
        for (var i = 0; i < list.length; i++) {
            var name = list[i].split(',name:')[1];
            var url = list[i].split(',name:')[0];
 
            AddFeedHtml(name, url);
        }
    }
}

Det som sker här är att vi först laser av värdet för “feedlist” och ser om det har något värde. Sedan delar vi upp strängen vid ”///url:” då detta skiljer url:erna åt. Vi loopar igenom alla dessa och splittar återigen på ”,name:” då det bara finns ett sådant värde per sträng.

Till sist så anropar vi AddFeedHtml(name, url) för att rendera länkarna till listan.

Det sista som behövs är möjligheten att ta bort gamla feeds. Det mest logiska sättet för detta är att lägga till ett alternativ i högra menyn, som låter oss göra detta.

På samma ställe som vi lägger till de andra menyvalen så lägger vi även till detta:

var rightMenu = menu.createMenuItem(3);
rightMenu.text = 'Ta bort feeds';
rightMenu.onSelect = function() { widget.setPreferenceForKey(null, 'feedlist'); };
menu.append(rightMenu);

Genom att sätta värdet för “feedlist” till null så tas alla tidigare värden bort.

Nu har vi en fullt fungerande RSS-läsare för Windows Mobile 6.5 skriven helt med webbstandarder!

Här är ett exempel på hur det kan se ut när vi nu har lagt till egna feeds:

clip_image016

Språkstöd för widgets

När man skriver applikationer så händer det ofta att man vill ha dem på flera språk. Med widgets så är detta väldigt enkelt att lösa, det räcker med att helt enkelt skapa upp en mapp med landet och språket (sv-SE för svenska och Sverige), eller bara språket om man så vill (sv). I denna mapp så lägger vi sedan till kopior av originalfilerna, men översatta på det aktuella språket. Widget-motorn läser sedan automatiskt av rätt filer så att vi får dessa lokaliserade.

Jag kommer nu att skapa en mapp vid namn ”en”, då jag vill ha en engelsk variant av widgeten, för de som inte talar svenska.

Det första steget är att skapa en mapp som heter en i vår katalog. För enkelhetens skulle så kommer jag bara att ta en kopia av js-filen vi redan har och placera den i den nya mappen. Vi kan även ta en kopia på t.ex. XML-filen, eller vilken annan fil som helst som vi vill ha översatt.

För att byta text på rubriken och texten i paragrafen nedan så kommer vi att skapa en metod vid namn ”Localize()”. Vi anropar denna i window.onload, och skapar den i stil med det här:

Nästa steg är att gå igenom alla hårdkodade värden i JavaScript-filen och ersätta dem med de engelska värdena.

Om vi nu ställer in på mobiltelefonen att vi ska ha något engelskspråkigt land under regionsinställningarna så kommer programmet att se ut så här:

clip_image018

Nu har vi en fullt fungerande RSS-läsare som dessutom har stöd för flera språk!

Fullständig kod finns att laddas ned här:
http://www.aspsidan.se/default.asp?page=scriptsShow&scriptId=554&scriptCat=5&scriptSubCat=54

More Posts