Archiv für Juli 2009

Freitag, 31. Juli 2009

Mac OS X: Google Reader Desktop Client selber bauen

Nachdem ich für mein neues Macbook Pro auf der Suche nach einem schönen Desktop Client für Google Reader war – mir aber Adobe AIR Applikationen nicht so gut gefallen – war ich zunächst sehr erfreut darüber, dass NewsGator in der neuen NetNewsWire Version einen Google Reader Connector eingebaut hat. Was mich aber am neuen NetNewsWire gestört hat, war die blinkende Werbung in der linken Fensterecke. Darum habe ich mich daran gemacht, mir mit Fluid, HelvetiReader und ein paar Userscripts einen schicken Desktop Client zu bauen. Fluid ist eine Software mit der sich Anwendungen aus Webseiten erstellen lassen, die man mit Scripten erweitern kann.

Dieser von mir gebaute Desktop Client unterstützt Growl Notifications das Dock Icon hat ein Badge, dass die ungelesenen Reader-Items zeigt, Favicons der Feeds werden angezeigt und er sieht so aus:

reader

Nur wie habe ich die Applikation gebaut? Als Erstes habe ich Fluid herunter geladen und installiert. Hinterher habe ich damit ein neues Programm erstellt:

fluid

Das Dock Icon für die Anwendung gibt es von Joshua Brewer bei Flickr.

Als nächstes kann das soeben erstellte Programm gestartet werden und es geht ans erstellen der Userscripts. Dazu muss auf die schwarze Papierrolle in der Menuleiste geklickt werden und ein neues Userscript erstellt werden.

Das erste Stück Script ist für das Aussehen unseres Desktop Clients nötig. Da mir der HelvetiReader gefällt habe ich auch dessen Skript eingefügt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// ==UserScript==
// @name Helvetireader
// @description Helvetireader style for Google Reader
// @include https://*.google.com/reader/view/*
// @include http://*.google.com/reader/view/*
// @include htt*://*.google.*/reader/view*
// @author Helvetireader by Jon Hicks (http://www.hicksdesign.co.uk) with favicon override by MkFly
// ==/UserScript==

var favvy = document.createElement('link');
favvy.setAttribute('type', 'image/x-icon');
favvy.setAttribute('rel', 'shortcut icon');
favvy.setAttribute('href', 'http://www.helvetireader.com/favicon.png');
var head = document.getElementsByTagName('head')[0];
head.appendChild(favvy);

var cssNode = document.createElement('link');
cssNode.type = 'text/css';
cssNode.rel = 'stylesheet';
cssNode.href = 'http://www.helvetireader.com/css/helvetireader.css';
cssNode.media = 'screen';
cssNode.title = 'dynamicLoadedSheet';
document.getElementsByTagName("head")[0].appendChild(cssNode);

Als nächstes braucht ihr den Code für das Laden der Favicons den ich beim Userscripts User sethaurus gefunden habe:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
function fetch(url, callback) {
    var xhr = new XMLHttpRequest;
    xhr.open('get', url);
    xhr.onload = function () {
        callback(xhr.responseText);
    };
    xhr.send(null);
};

function each(list, callback) {
    Array.prototype.forEach.call(list, callback);
};

function filter(list, callback) {
    return Array.prototype.filter.call(list, callback);
};

// --

var EXPORT_URL = '/reader/subscriptions/export',
    ICON_CLASS = 'sub-icon',   
    UNFIXED_ICONS = '.' + ICON_CLASS + ':not([iconbase])',
    ICON_CLASS = new RegExp('\b' + ICON_CLASS + '\b'),
    POLL_INTERVAL = 1000,
    FAVICON_TEMPLATE = ['background-position:0px; background-image:url(/s2/favicons?domain=', ')'],
    SOURCE_URL_PREFIX = ['xmlUrl="', '" htmlUrl="'];

function drawFavicon(node) {
    node.style.cssText = FAVICON_TEMPLATE.join(node.getAttribute('iconbase').split('/')[2]);
};

function getSourceUrlFromOpml(feedUrl, opml) {
    return (opml.split(SOURCE_URL_PREFIX.join(feedUrl))[1] || '').split('"')[0];
};

function getIconNodes() {
    if (document.querySelectorAll)
        return document.querySelectorAll(UNFIXED_ICONS);
       
    return filter(document.getElementsByTagName('span'), function (span) {
        return ICON_CLASS.test(span.className)
            && ! span.hasAttribute('iconbase');
    });
};

(function () {
    setTimeout(arguments.callee, POLL_INTERVAL);
   
    var iconNodes = document.querySelectorAll(UNFIXED_ICONS);
   
    if (! iconNodes.length)
        return;

    each(iconNodes, function(icon){
        icon.setAttribute('iconbase', unescape(icon.parentNode.href.split('/')[6]));
       
        drawFavicon(icon);
    });

    fetch(EXPORT_URL, function(opml) {
        each(iconNodes, function(icon){
            var iconbase = icon.getAttribute('iconbase');
           
            icon.setAttribute('iconbase', getSourceUrlFromOpml(iconbase, opml));
           
            drawFavicon(icon);
        });
    });
})();

Für die Growl Notifications ist der Code von tanguy zuständig:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
(function() {
   
    if (!window.fluid) {
        return;
    }
    var focused = true;
    window.onfocus = function(){focused=true;};
    window.onblur = function(){focused=false;};
   
    var fluid_unread = 0;
    var old_fluid_unread = 0
   
    window.fluid.addDockMenuItem("Refresh", function() { refresh(); });
   
    function updateDockBadge() {
        var title = document.title;
        old_fluid_unread = fluid_unread || 0;

        if (title && title.length) {
            var start = title.indexOf("(");
            var end = title.indexOf(")");
            if (start > -1 && end > -1) {
                start++;
                fluid_unread = title.substring(start, end);
            } else {
                fluid_unread = 0;
            }
        }
       
        //set the dock badge
        /*
        if ((fluid_unread || 0) > 0) {
            window.fluid.setDockBadge(fluid_unread);
           
        } else {
            window.fluid.setDockBadge("");  
        }
        */

        //growl if there are more unread items than last time
        if ((fluid_unread || 0) > old_fluid_unread)
        {
            window.fluid.setDockBadge(fluid_unread);
            if(!focused)
            {
                if(refresh())
                    setTimeout(growleachnewnews, 5000);
            }
            fluid.showGrowlNotification({
                title: "Google Reader",
                description: (fluid_unread || "") + " unread item(s)",
                priority: 3,
                onclick: activate_window,
                sticky: false
            });
       
               
           
        }
        else if(fluid_unread == 0)
        {
            window.fluid.setDockBadge("");
        }
    }
    setInterval(function(){updateDockBadge();}, 6000);
   
    function refresh()
    {
        //alert(window.fluid.dockBadge);
        var refreshelm = document.getElementById('viewer-refresh');
       
        if(refreshelm)
        {
            var e = document.createEvent('MouseEvents');
            //e.initEvent('click', true, false);
            e.initMouseEvent("click", true, true, window,
                0, 0, 0, 0, 0, false, false, false, false, 0, null);
            //document.getElementById('viewer-refresh').dispatchEvent(e);
            refreshelm.dispatchEvent(e);
            return true;
        }
        else
            return false;
    }
    function growleachnewnews()
    {
       
        //return;
        var news = getElementsByClassName('entry read');
        for(i=0; i < ((fluid_unread/1)-(old_fluid_unread/1)); i++)
        {
            var feed = news[i].getElementsByClassName('entry-source-title link')[0].innerText;
            var preview = news[i].getElementsByClassName('entry-title')[0].innerHTML;
            //alert(i);
           
            fluid.showGrowlNotification({
                title: feed,
                description: preview,
                priority: 1,
                onclick: activate_window,
                identifier: "greader_" + i,
                sticky: false
            });
           
        }
    }
    function activate_window()
    {
        //alert('hi');
        window.fluid.activate();
        window.fluid.unhide()
        this.window.focus();
        window.focus();
       
    }
    function setfocused()
    {
        focused = true;
    }
    function notfocused()
    {
        focused = false;
    }

})();


/* === GETELEMENTSBYCLASSNAME ===
   Developed by Robert Nyman, http://www.robertnyman.com
   Code/licensing: http://code.google.com/p/getelementsbyclassname/
   ============================== */



var getElementsByClassName = function(className, tag, elm) {
    if (document.getElementsByClassName) {
        getElementsByClassName = function(className, tag, elm) {
            elm = elm || document;
            var elements = elm.getElementsByClassName(className),
            nodeName = (tag) ? new RegExp("\\b" + tag + "\\b", "i") : null,
            returnElements = [],
            current;
            for (var i = 0, il = elements.length; i < il; i += 1) {
                current = elements[i];
                if (!nodeName || nodeName.test(current.nodeName)) {
                    returnElements.push(current);
                }
            }
            return returnElements;
        };
    }
    else if (document.evaluate) {
        getElementsByClassName = function(className, tag, elm) {
            tag = tag || "*";
            elm = elm || document;
            var classes = className.split(" "),
            classesToCheck = "",
            xhtmlNamespace = "http://www.w3.org/1999/xhtml",
            namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace) ? xhtmlNamespace: null,
            returnElements = [],
            elements,
            node;
            for (var j = 0, jl = classes.length; j < jl; j += 1) {
                classesToCheck += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]";
            }
            try {
                elements = document.evaluate(".//" + tag + classesToCheck, elm, namespaceResolver, 0, null);
            }
            catch(e) {
                elements = document.evaluate(".//" + tag + classesToCheck, elm, null, 0, null);
            }
            while ((node = elements.iterateNext())) {
                returnElements.push(node);
            }
            return returnElements;
        };
    }
    else {
        getElementsByClassName = function(className, tag, elm) {
            tag = tag || "*";
            elm = elm || document;
            var classes = className.split(" "),
            classesToCheck = [],
            elements = (tag === "*" && elm.all) ? elm.all: elm.getElementsByTagName(tag),
            current,
            returnElements = [],
            match;
            for (var k = 0, kl = classes.length; k < kl; k += 1) {
                classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)"));
            }
            for (var l = 0, ll = elements.length; l < ll; l += 1) {
                current = elements[l];
                match = false;
                for (var m = 0, ml = classesToCheck.length; m < ml; m += 1) {
                    match = classesToCheck[m].test(current.className);
                    if (!match) {
                        break;
                    }
                }
                if (match) {
                    returnElements.push(current);
                }
            }
            return returnElements;
        };
    }
    return getElementsByClassName(className, tag, elm);
};

/* === //GETELEMENTSBYCLASSNAME === */

Wenn man diese Codestücke ins Userscript eingefügt hat, kann man es speichern. Jetzt muss es nur noch aktiviert werden, dies geht indem man nochmals auf die schwarze Papier Rolle im Menü klickt und ein Häkchen vor dem Script setzt. Ein Neustart genügt und man ist fertig.

Für Kommentare und Anregungen wäre ich dankbar. :-)

Donnerstag, 30. Juli 2009

Mac OS X: Software zum Lauftraining und Tracking

Vor gut zwei Jahren bin ich schonmal auf die Software Trailrunner gestoßen, da ich meine damalige Laufstrecke vermessen wollte. Das hat sehr gut geklappt und ich hab die Software öfters verwendet. Da ich dann aber ein Handy mit GPS an Bord bekommen habe, war ich nicht mehr auf die Software angewiesen und sie ist in Vergessenheit geraten. Als ich dann neulich die Strecke, der Bergtour aufs Nebelhorn getrackt habe, wollte ich mir diese auch am Mac anzeigen lassen und bin dabei wieder auf Trailrunner gestoßen. Laut Beschreibung der Webseite ist Trailrunner ein

Routenplaner für Langstreckensportler, der ideale Begleiter für ein ausdauerorientiertes aber abwechlungsreiches Training: Ausgangspunkt auswählen, aus Lieblingsabschnitten eine Route mit gewünschter Länge zusammenstellen, Zielgeschwindigkeit zur Kontrolle von Zwischenzeiten angeben, Routenverlauf ausdrucken oder auf einen iPod exportieren und los.

Meiner Meinung nach eignet sich Trailrunner nicht nur zur Routenplanung, sondern auch um Wanderstrecken, Bergtouren oder Fahrradtouren zu archivieren. Für ambitionierte Sportler ist ein Trainingskalender und die Möglichkeit Trainingspläne zu verwalten enthalten.

TrailRunner

Trailrunner bietet verschiedene Kartenmodi wie Geländekarte, Openstreetmap, Google Earth und Höhenprofil.

Ich finde Trailrunner ist ein tolles Stück Software, welches jedem Sportler, Bergsteiger oder Fahrradfahrer viel Freude bereiten kann.

Dienstag, 28. Juli 2009

Vierter Webmontag in Augsburg

Nachdem ich den vierten Webmontag in Augsburg schon angekündigt habe, war ich gestern natürlich auch vor Ort. Mit mir trotzten ungefähr 15 Webmontagler der Versuchung in den Biergarten oder an den See zu gehen. Der Sponsor des Begrüßungsgetränks war gestern: Meistertipp. de – News und Tipps für Unternehmer im Bauhandwerk.

Leider gab es auch diesmal nur ein Standup, das von Marc Frey gehalten wurde. Thema des Standups war: My-MIKI.com – Neue Medien für das neue Web: Wie Print und Online wirklich zusammenpassen. Bei My-Miki handelt es sich um eine Plattform, auf der jeder auf einfachem Weg ein Online Magazin – wie man es aus dem Zeitschriftenhandel kennt – erstellen kann. Diese so genannten Mikis können Bilder, Texte, Grafiken, Videos, Verlinkungen, Votings und Kommentarfunktionen beinhalten und lassen sich ähnlich wie YouTube Videos auf anderen Seiten einbinden.

Hier könnt ihr ein Miki zum Thema Outdoor bewundern (mit den Pfeiltasten <- -> könnt ihr Blättern):

Ich denke, ein Miki ist eine interessante Möglichkeit sich kreativ auszutoben, für welche man auf Grund der niedrigen Einstiegshürde sicherlich auch Schüler begeistern kann.

Ich freu mich schon auf den nächsten Webmontag. :-)

Donnerstag, 23. Juli 2009

Vorbericht zum vierten Augsburger Webmontag

Am Montag den 27. Juli um 19:00 Uhr findet der vierte Augsburger Webmontag im Capitol am Moritzplatz statt. Wer Lust hat zu kommen, kann sich auf der Wiki Seite des Webmontags eintragen. Leider steht noch kein Standup und kein Begrüßungsdrink-Sponsor fest, also meldet Euch fleißig. ;-)

Für alle die nicht wissen, was es mit dem Webmontag auf sich hat, hier die Beschreibung aus dem Wiki:

Web Montag ist ein informelles, nicht-kommerzielles, dezentral organisiertes Treffen, das zum Ziel hat, all diejenigen miteinander zu verbinden, die die Zukunft des Internet gestalten. Inspiriert von der Kultur Silicon Valleys startete der Web Montag gegen Ende 2005 in Köln als Versuch, ein bisschen “kalifornischen Sonnenschein” nach Deutschland zu bringen.

Seitdem hat sich der Web Montag schnell weiterverbreitet: Treffen finden mittlerweile in mehr als 40 Städten überall in Deutschland und Österreich, in Schweden, Silicon Valley sowie auf Second Life statt. Als Treffpunkt und Anlaufstelle der verschiedenen lokalen Web 2.0- und Startup-Szenen hat der Web Montag mit seinen bisher 100+ Veranstaltungen bereits 1.000+ Teilnehmer angezogen, mit teils sehr erfreulichen Auswirkungen.

Alle, die mit Web 2.0 und benachbarten Themen zu tun haben und interessiert daran sind, ihr Wissen zu teilen und sich miteinander auszutauschen, sind herzlich willkommen. Ob Erfinder, Ingenieur, Designer, Gründer oder Finanzier – Web Montag ist die Gelegenheit, sein neues Produkt, Service, Startup, oder die nächste große Idee einem stetig wachsenden Publikum von Webbegeisterten vorzustellen.

Außerdem gibt es von mir noch Berichte vom Zweiten und Dritten Webmontag.

Würde mich freuen den ein oder anderen von Euch am Webmontag zu treffen.

Dienstag, 14. Juli 2009

Bergtour: Aufs Nebelhorn über die Gaißalpseen und den Gaißfußsattel

Gestern haben wir eine wunderbare Bergtour aufs Nebelhorn gemacht. Zwar hatte der Wetterbericht besseres Wetter als immer wieder einsetzenden Nieselregen vorhergesagt, jedoch haben wir und vom Wetter die Tour nicht versauen lassen und diese genossen.

Die Tour beginnt am Parkplatz der Nebelhornbahn und führt an der Erdinger Skisprungarena vorbei zum Café Breitenberg. Von dort geht es dann weiter zur Gaißalpe. Hier beginnt schließlich der eigentliche Anstieg hinauf zu den Gaißalpseen und weiter zum Gaißfußsattel. Hat man den Sattel erreicht, kann man je nach Kondition, Lust und Laune, entweder den wenig anstrengenden Weg weiter zum Edmund Probst Haus wählen oder so wie wir weiter zum Gipfel des Nebelhorns steigen.
Vom Gipfel kann man entweder mit der Gondel ins Tal fahren oder über den Normalweg zurück nach Oberstdorf laufen. Der Normalweg ist eine sehr steile und grobkörnige Teerstraße, auf der es wegen des Kieses auf dem Asphalt sehr mühsam ist, talwärts zu laufen.

Bis zum Gipfel braucht man ungefähr 4h 45min und für den Abstieg nochmals 3h 30min. Die ganze Rundtour hat ungefähr 31km und 1400 Höhenmeter.

Ein paar Eindrücke von der Wanderung findet Ihr bei meinen Bildern oder direkt hier.

GPX Datei der Bergtour für Euer GPS herunterladen.