Hi! Welcome...

WeizenSpr.eu Schön, dass es dich auf diese Seite verschlagen hat. Der Blog hier auf WeizenSpr.eu ist meine eigene kleine Welt. Hier schreibe ich über dies und das - von Alltagssituationen bis hin zu Projekten, die mich momentan beschäftigen.

Ich wünsche dir viel Spaß beim Lesen der einzelnen Artikel. Vielleicht findest du ja ein paar Informationen, die du bisher noch nicht gekannt hast.

02 September 2010 ~ 0 Comments | ÄHNLICHE ARTIKEL

[Update] Gedanken zum Licht…


Diesen Artikel drucken Diesen Artikel drucken
Diesen Artikel vorlesen Diesen Artikel vorlesen

An sich mag ich es ja, dass ich jeden Tag mit dem Auto etwa eine dreiviertel Stunde bis zur Arbeit fahren muss. Denn obwohl ich Auto fahre, habe ich dabei genug Zeit, mal über Gott und die Welt nachzudenken.

Dieses Mal habe ich über das Licht, die Farben und Wärme nachgedacht. Eigentlich hatt die Themen ja jeder in der Schule. Licht und Farben gibt es so gesehen garnicht – im Grunde handelt es sich dabei einfach nur um Photonen, also sowas wie Energiekügelchen. Wenn die nun in unserem Auge auf einen der Farbrezeptoren (die Zapfen) treffen, werden sie als eine bestimmte Farbe interpretiert. Ob weiße Menschen nun weiß sind, hängt also im Grunde nur davon ab, wie unser Gehirn die Informationen der Rezeptoren auswertet und zusammenfügt.

Das ist relativ spannend, denn unser Körper besitzt neben den Farbrezeptoren des Auges noch ein weiteres Sinnesorgan, dass diese Photonen wahrnehmen kann: die Haut. Spannend ist das deshalb, weil die Haut zwar theoretisch die gleiche Aufgabe erledigt wie die Augen, das aber auf eine völlig andere Weise tut.
Vielleicht sagt sich jetzt der ein oder andere “Meine Haut kann doch kein Licht sehen!” Das ist ja auch richtig… die Haut reagiert nicht auf das sichtbare Licht des elektromagnetischen Spektrums, aber sie reagiert auf die Wärmestrahlung, die sich meist im infraroten Spektrum befindet :D ! Die Wärmestrahlung – oder speziell das Infrarotlicht – könnte theoretisch auch von unseren Augen wahrgenommen werden, wenn die dafür fein genug wären.
Das würde uns in der Natur sogar einen Vorteil verschaffen, denn dann könnten wir Lebewesen auch bei völliger Dunkelheit erkennen – einfach nur anhand des (für den Menschen unsichtbaren) Lichtes, das sie abstrahlen.
Oder auch andersrum gedacht: Stellt euch mal vor, man könnte eine künstliche Haut erschaffen, die nicht auf Wärmestrahlung reagiert, sondern auf die Strahlen des sichtbaren Lichtes. Dann könnte diese Haut eventuell “fühlen”, wenn sie sich einem Gegenstand nähert – irgendwie eine gruselige Vorstellung. ;-)

Auch lustig: Wenn man mal ans andere Ende des Lichtspektrums geht, gelangt man zur ultravioletten Strahlung – das sind die Strahlen, die das Erbgut angreifen, Hautkrebs verursachen und wegen denen unsere Haut schützende Pigmente bildet.
Eine Besonderheit dieser UV-Strahlen ist, dass das lichtdurchlässige Glas in der Regel undurchlässlig für UV-Strahlen ist. Wenn wir uns jetzt vorstellen würden, dass wir ausschließlich ultraviolettes Licht sehen könnten, dann würde das bedeuten, dass Glas für uns garnicht durchsichtig wäre.
Die Frage, die sich aus dieser Erkenntnis ergibt, wäre: Ist es möglich, dass es da draußen Lebewesen gibt, die durch Steinwände sehen können, so wie wir durch Glasscheiben sehen können? :D

Was meint ihr? Ist dieses sichtbare und unsichtbare Licht nicht eine großartige Sache? :-)

Update:
Ich wurde gefragt, ob es denn theoretisch auch möglich wäre, die gefährliche radioaktive Strahlung zu sehen. Die Antwort darauf lautet: teils, teils. Die radioaktive Strahlung, die wir kennen, besteht grundlegend aus drei Strahlungsarten: der Alphastrahlung, der Betastrahlung und der Gammastrahlung. Während man die Gammastrahlung – als elektromagnetische Strahlung – potentiell “sehen” könnte, sieht es bei der Alpha- und Betastrahlung anders aus: dort werden nämlich keine Energiekügelchen verschossen, sondern Teilchenstrahlung emittiert. Oder anders ausgedrückt: Neben der Taschenlampe (Gammastrahlung) beinhaltet die radioaktive Strahlung auch Pflastersteine (Alpha- und Betastrahlung).
Per Definition ist übrigens die Alphastrahlung (besteht aus Helium-Kernen) die gefährlichste: Wenn diese Teilchen z.B. auf das Erbgut innerhalb einer Zelle treffen, kann der DNA-Strang zerstört werden und bei der Zellteilung Mutationen (Krebs) auslösen. Trotzdem ist die Gammastrahlung gefürchteter: diese lässt sich nämlich nur schwer abschirmen. Das bedeutet, dass man einen Gammastrahler mit viel Materie ummanteln muss, bis keine Strahlung mehr nach außen dringen kann.
Es gibt allerdings eine Möglichkeit, diese herumschwirrenden Teilchen nachzuweisen: das so genannte Geiger-Müller-Zählrohr (besser bekannt als Geigerzähler). In diesem treffen die positiv geladenen Teilchen der Strahlung auf ein Gas, dessen Elektronen sie freisetzen. Diese Elektronen erzeugen dann einen elektrischen Strom, der das Auftreffen der Teilchen signalisiert.
Ich hoffe, ich konnte die Frage halbwegs zufriedenstellend beantworten. :D

Erleuchtete Grüße, Kenny

P.S.: Liebe Biologen, Chemiker und Physiker, bitte haut mich nicht für die pseudowissenschaftlichen Ausflüge, die ich in diesem Artikel unternommen habe. Wenn ihr Korrekturen habt, würde ich mich über Kommentare freuen. :-)




Hochwertiger Leinwanddruck muss nicht teuer sein: Auf Top-Fotoleinwand.de bestellen Sie Ihr individuelles Foto auf Leinwand zum dauerhaften Niedrigpreis - angefangen bei einem Format von 30x30cm für 12 Euro bis hin zu XXL-Leinwänden in einer Größe von 120x80cm für 55 Euro.

Für Ihr Foto auf Leinwand verwendet Top-Fotoleinwand nur echte Künstler-Leinwände von hoher Qualität und Beständigkeit. Dank modernster Drucktechnik wird Ihr Bild im Handumdrehen in ein echtes Foto-Kunstwerk verwandelt - und das zum unschlagbaren Preis!



01 September 2010 ~ 0 Comments | ÄHNLICHE ARTIKEL

Was bedeutet die Netzneutralität?


Diesen Artikel drucken Diesen Artikel drucken
Diesen Artikel vorlesen Diesen Artikel vorlesen

Derzeit wird an vielen Ecken darüber diskutiert, ob die sogenannte Netzneutralität gesetzlich festgeschrieben werden muss, oder ob man es sogar verkraften könnte, die Netzneutralität aufzuweichen.
Da jedoch der Großteil der Bevölkerung wahrscheinlich garnicht versteht, was es mit der Netzneutralität auf sich hat, dachte ich mir, dass ich mal versuchen, für das ganze eine halbwegs gute Erklärung zu liefern.

Stellen wir uns also mal vor, dass es weltweit nur 2 Webseiten gäbe (der Rest lässt sich analog herleiten). Diese drei Webseiten sind “Deutschland.de” und “Youtube.de”. Man selber ist nun Kunde bei deutschlandCOM und bestellt einen Internetzugang.

Bei einer zugesicherten Netzneutralität könnte man nun problemlos beide Webseiten besuchen. Beide Webseiten würden gleichschnell laden, egal, ob man Texte, Bilder oder Videos abruft. Da “Deutschland.de” nur langweilige Texte liefert, wird diese Seite kaum aufgerufen. Da man bei “Youtube.de” aber die coolsten und neusten Videos angucken kann, wird diese Seite oft aufgerufen und produziert viele Daten, die quer durch das Netz geschickt werden. Ist aber alles kein Problem, denn “Youtube.de” bezahlt dafür, dass es so viele Daten verbreiten darf. So ist es heutzutage.

Ohne Netzneutralität könnte es in Zukunft so aussehen: Bei der Bestellung der Internetverbindung hat man die Wahl zwischen 3 Paketen – “Internet Basic”, “Internet All” und “Internet All Premium”.

Bei “Internet Basic” erreicht man bei deutschlandCOM nur ausgewählte Webseiten. In unserem Beispiel wäre das nur “Deutschland.de” mit den langweiligen Regierungstexten. Um mehr zu bekommen, müsste man mehr zahlen – nämlich für das “Internet All”-Paket. Nun darf man endlich alle Seiten angucken… aber merkwürdig… “Youtube.de” lädt viel langsamer als “Deutschland.de”? Da “Youtube.de” mehr Daten produziert, wurde es heruntergeregt – zum “Wohle” aller. Denn wenn man “Youtube.de” in normaler Geschwindigkeit sehen will, kann man doch gefälligst das noch teurere “Internet All Premium” bestellen. Dass “Youtube.de” bereits die Mehrdaten bezahlt hat, ist dabei völlig egal.

So kommt es, dass Leute, die sich nur einen normalen Internetanschluss leisten können, nur bestimmte Inhalte im Web sehen können. Leute mit mehr Geld können das gesamte Internet betrachten – solange es solch ein Paket gibt. Denn wer sagt denn, dass nicht irgendwann die “Internet All”-Pakete abgeschafft werden? Wie solch eine Welt aussieht, kann man schon heute in Staaten wie China beobachten. Aber auch dort geschieht das Ganze natürlich nur zum “Wohle” aller…

Neutrale Grüße, Kenny

29 August 2010 ~ 3 Comments | ÄHNLICHE ARTIKEL

Das WordPress Multisite-Feature ist kacke!


Diesen Artikel drucken Diesen Artikel drucken
Diesen Artikel vorlesen Diesen Artikel vorlesen

Ich habe es wirklich versucht! Mehrfach sogar! Ich wollte mich wirklich mit dem Multisite-Feature von Wordpress anfreunden, das seit der Version 3.0 durch die Integration der Multiuser-Edition verfügbar ist. Aber es klappt einfach nicht!

Ich habe ehrlich gesagt keine Ahnung, welcher Frickler da am Werk war, um dieses – eigentlich recht praktische – Feature dermaßen zu versauen! Dabei ist der Grundgedanke doch eigentlich recht praktisch: Mit einer WordPress-Installation will man mehrere Blogs – mit jeweils eigenem Inhalt – bereitstellen. Das ganze soll durch eine Userverwaltung untermauert werden, sodass jede User – potentiell – Zugriff auf mehrere Blogs der gleichen Installation hat.

MultiSite Superadmin

Aber wie sieht das Ergebnis aus? Das Einrichten ist noch ziemlich einfach: Eine Konfiguration, um das “Netzwerk” erzeugen zu können. Dann alles nochmal umkonfigurieren, um das Netzwerk nutzbar zu machen. Dann kann man neue Unterseiten anlegen und User berechtigen, auf Blog A, B und C zuzugreifen.
Doch zu welchem Preis? Das fängt schonmal damit an, dass man WordPress nicht mehr in einen Unterordner installieren darf – warum auch immer das nicht unterstützt wird. Dann verschwinden urplötzlich Einstellungsmöglichkeiten, die unter Garantie nichts, aber auch wirklich garnichts mit dem Multisite-Feature zu tun haben…

Leseeinstellungen vorher vs. nachher

…um die verschwundenen Einstellungen tätigen zu können, muss man die mehr als lächerliche Superadmin-Blog-Konfigurationsmöglichkeit benutzen, in der man die Werte händisch ändern darf…

Superadmin Blog-Optionen

…und zur Krönung des ganzen darf man dann auch noch irgendwelche Drittkomponenten einsetzen, um den Blogs auch jeweils eigene Domains zuweisen zu können (wobei das Plugin bei mir einfach nur zu einer Endlosschleife führt und den Login kaputt macht).

Ich für meinen Teil werde dieses völlig verbastelte Feature jedenfalls nicht nutzen. Wenn man nicht gerade Blogs für irgendwelche Fremden Leute bereitstellen muss, gibt es sicherlich bessere Wege, um eine akzeptable Alternative zu erreichen. Zwei Beispiele für solche Alternativen möchte ich euch zeigen. Die sind zwar weniger bequem (was die geteilte User-Verwaltung angeht), aber sie ermöglichen genau das, was ein normaler Bloguser wahrscheinlich will:

  • mehrere Blogs betreiben können
  • jedem Blog eine eigene Domain geben können
  • nur eine WordPress-Installation pflegen müssen

Die Lösungen selbst ähneln denen, die WordPress.org selber vorschlägt, nur, dass das ganze mit einer einzigen Codebasis funktioniert. Die erste Lösung ist für Leute geeignet, die nur eine Datenbank zur Verfügung haben, aber trotzdem mehrere WordPress-Blogs parallel nutzen wollen:
In der Datei wp-config.php gibt es die Variable $table_prefix. Durch Ändern dieser Variable kann man zwischen verschiedenen WordPress-Instanzen hin- und herschalten. Wenn man nun also für verschiedene Domains verschiedene WordPress-Instanzen (aber mit der gleichen Codebase) haben will, muss man diesen Prefix einfach nur vom Host abhängig machen:

1
$table_prefix  = 'wp_' + strtolower(ereg_replace("[^A-Za-z]", "", $_SERVER["HTTP_HOST"])) + "_";

Hier gibt es jedoch einen Schönheitsfehler: Sollte jemand mit einer bisher unbekannten Domain die Blog-Installation aufrufen können, hat dieser die Möglichkeit, einen eigenen Blog zu installieren! Um das zu verhindern, sollte man entweder eine Abfrage einbauen, ob der Host zu einer Liste von Hosts gehört, zu denen der Blog bereits existiert, oder man macht die Datei wp-admin/install.php unzugänglich.

Leute, die beliebig viele Datenbanken anlegen können, können auch einen anderen Weg gehen – diesen werde ich persönlich wahrscheinlich nutzen. Was man einfach macht: Man verwendet einen Datenbank-Namen und (optional) einen Nutzernamen und ein Passwort, die sich vom Host ableiten lassen. Der Vorteil ist, dass das Installationsproblem bei inkorrekten Hosts wegfällt.

1
2
3
4
5
6
7
8
9
10
$clearedHost  = strtolower(ereg_replace("[^A-Za-z]", "", $_SERVER["HTTP_HOST"]));
$generatedPwd = ereg_replace("[^A-Za-z0-9]", "", base64_encode(pack("H*" , sha1("SALT" . $clearedHost))));
 
// ** MySQL settings - You can get this info from your web host ** //
define('DB_NAME',     'DBNAME_' . $clearedHost);
define('DB_USER',     'USERNAME_' . $clearedHost);
define('DB_PASSWORD', $generatedPwd);
define('DB_HOST',     'DBSERVER');
define('DB_CHARSET',  'utf8');
define('DB_COLLATE',  '');

In diesem Beispiel sollte man die Platzhalter “SALT”, “DBNAME”, “USERNAME” und “DBSERVER” durch seine eigenen Daten ersetzen. “SALT” dient übrigens dazu, damit außenstehende nicht anhand der Domain auf das Datenbank-Passwort schließen können. ;-)

Ich hoffe, ich konnte euch ein paar neue Einblicke verschaffen und wünsche euch viel Spaß mit eurer WordPress-Installation! :D

Und nicht vergessen: Ich hafte nicht für Schäden an Software, Hardware oder für Vermögensschäden, die durch Anwendung dieser Änderungen entstanden sind oder entstehen könnten. ;-)

Multiple Grüße, Kenny

29 August 2010 ~ 8 Comments | ÄHNLICHE ARTIKEL

Und der Gewinner ist…


Diesen Artikel drucken Diesen Artikel drucken
Diesen Artikel vorlesen Diesen Artikel vorlesen

Das Gewinnspiel ist vorbei und die Sieger stehen nun fest. Da es nur 12 Teilnehmer gab, erhält natürlich auch jeder der Teilnehmer einen der Sofort-Gewinne! :D

Die glücklichen Gewinner sind…

  1. Blog.Kschymyk.de
  2. Blog.Preciosa304.de
  3. Bloggewinn.de
  4. Chaosweib.com
  5. Cvbler.Wordpress.com
  6. FGnet.de
  7. Gewinnspiele.Maunzblog.de
  8. Glitzerfrosch.de
  9. Hannes-Schurig.de
  10. Hosenwelt.com
  11. Lasst-uns-testen-und-berichten.over-blog.de
  12. XYonline.de

Wer bekommt die Gutscheine?

Das Random.org-Los hat entschieden und folgende Gewinner preisgegeben: Blog.Kschymyk.de und Hannes-Schurig.de erhalten zusätzlich jeweils einen 20€ Gutschein für Shirt-Selbst-Bedrucken.de. Herzlichen Glückwunsch! :D

Random.org Ergebnisliste

Was müsst ihr nun tun?

  1. Geht auf diese Gewinnartikel-Seite und sucht euch einen der Preise aus. (Bitte den Artikel nicht über den Shop bestellen! Nur den Artikel aussuchen und dann mit Schritt 2 weitermachen!)
  2. Kopiert den Link zum ausgesuchten Gewinn (z.B. das Born to Grill Shirt).
  3. Schreibt eine E-Mail an gewinn [at] shirt-selbst-bedrucken [punkte] de und beachtet folgendes:
    1. Nutzt die Mail-Adresse aus eurem Impressum, die Mail-Adresse aus eurem Teilnahme-Kommentar oder eine Adresse, die sich eindeutig eurer Domain zuordnen lässt (damit euch niemand den Gewinn wegnehmen kann).
    2. Gebt den kopierten Link zum gewünschten Artikel in der E-Mail mit an!
    3. Gebt die gewünschte Größe (falls zur Auswahl), die gewünschte Farbe (falls zur Auswahl) und eure Adresse mit an!
  4. Die beiden Gewinner der Gutscheine erhalten als Antwort auf Ihre E-Mail auch ihren persönlichen 20€ Gutschein für Shirt-Selbst-Bedrucken.de mitgeteilt.

Vielen Dank für eure Teilnahme! :D

24 August 2010 ~ 4 Comments | ÄHNLICHE ARTIKEL

Dein Twitter – wie Du willst!


Diesen Artikel drucken Diesen Artikel drucken
Diesen Artikel vorlesen Diesen Artikel vorlesen

@dqmhose hatte mich letztens gefragt, ob es mit Stylish möglich sei, bei Twitter Nachrichten von Leuten auszublenden, die man nicht kennt. Seine Begründung: Viele dieser Nachrichten sind eigentlich Spam und damit vernachlässigbar.

Da Twitter jedoch die Nachrichten von unbekannten Personen nicht zusätzlich per CSS markiert, war meine Antwort “Nein.” – allerdings versprach ich ihm, mir mal anzugucken, ob das ganze mit Greasemonkey möglich wäre. Im Gegensatz zu Stylish definiert man bei Greasemonkey nämlich nicht einfach nur ein bisschen CSS, sondern kann mit Hilfe von JavaScript größeren Einfluss auf die Webseite nehmen.

Einfach gestaltete sich die Arbeit allerdings nicht, da es garnicht so einfach ist, herauszufinden, welche Leute man denn überhaupt kennt. Auf die Verwendung der Twitter-API wollte ich jedoch auf jeden Fall verzichten – den Aufwand wäre es mir nicht Wert gewesen :D !
Aus diesem Grund ist meine Lösung eine ziemliche Knobelarbeit geworden – funktionieren tut sie trotzdem ;-) . Wie genau, das will ich euch gleich verraten…

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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
// ==UserScript==
// @name           Block Messages from Non-Followings
// @namespace      http://weizenspr.eu
// @include        http://twitter.com/*
// @include        https://twitter.com/*
// ==/UserScript==

var checkHomeScreen = true;
var hideAgainTimer  = 2000; // in milliseconds (values <= 0 deactivate the timer)
var showThesePeople = ["followers", "followings"]; // can either be "followers" and/or "followings"
var showDebugAlerts = true;

// DO NOT EDIT BELOW THIS LINE

// options
var followersOption  = "followers";
var followingsOption = "followings";

// twitter page titles
var followersTitle        = "Twitter / People who follow ";
var followingsTitle_Start = "Twitter / People ";
var followingsTitle_Stop  = " is following";
var homeTitle             = "Twitter / Home";
var replyTitle            = "Twitter / @";

// twitter URLs
var followingsURL = "http://twitter.com/following";
var followersURL  = "http://twitter.com/followers";
var twitterHost   = "http://twitter.com";

// twitter ids and classes
var className_Start = "hentry u-";
var className_Stop  = " status";
var className2_Stop = " share status";
var nextLinkRel     = "me next";
var paginationId    = "pagination";
var timelineId      = "timeline";
var usernameMeta    = "session-user-screen_name";
var userSpanClass   = "label screenname";

// html and css fragments
var anchorTag    = "a";
var displayNone  = "none";
var divTag       = "div";
var itemTag      = "li";
var metaTag      = "meta";
var paragraphTag = "p";
var spanTag      = "span";

// greasemonkey option names
var followingOption      = "following_";
var followingCountOption = "followingCount";

var username = getUsernameFromMeta();
if (username != null) {
  if ((document.title.toLowerCase() == (followingsTitle_Start + username + followingsTitle_Stop).toLowerCase()) ||
      (document.title.toLowerCase() == (followersTitle + username).toLowerCase())) {
    var showFollowers  = false;
    var showFollowings = false;
   
    if (showThesePeople != null) {
      for (var index = 0; index < showThesePeople.length; index++) {
        if (showThesePeople[index].toLowerCase() == followersOption.toLowerCase()) {
          showFollowers = true;
        }
        if (showThesePeople[index].toLowerCase() == followingsOption.toLowerCase()) {
          showFollowings = true;
        }
      }
     
      if (showFollowers && showFollowings) {
        var proceed = confirm("Do you want to update your followings userlist?");
        if (proceed) {
          var allFollowers  = null;
          var allFollowings = null;
         
          if (showFollowers) {
            allFollowers = retrieveFollowings(followersURL);
          }
          if (showFollowings) {
            allFollowings = retrieveFollowings(followingsURL);
          }

          var totalList = new Array();
          var count     = 0;

          if (allFollowers != null) {
            for (var indexA = 0; indexA < allFollowers.length; indexA++) {
              totalList[count++] = allFollowers[indexA];
            }            
          }
          if (allFollowings != null) {
            for (var indexB = 0; indexB < allFollowings.length; indexB++) {
              totalList[count++] = allFollowings[indexB];
            }            
          }
         
          saveFollowings(totalList);

          if (showDebugAlerts) {
            alert("Finished update: " + totalList.length + " entries added");
          }
        }
      }
    }
  }
   
  if ((document.title.toLowerCase() == homeTitle.toLowerCase()) && (checkHomeScreen)) {
    hideUnwantedReplies();
  }
   
  if (document.title.toLowerCase() == (replyTitle + username).toLowerCase()) {
    hideUnwantedReplies();
  }
}

function getUsernameFromMeta() {
  var result = null;
 
  var allMetas   = document.getElementsByTagName(metaTag);
  var singleMeta = null;

  for (var index = 0; index < allMetas.length; index++) {
    singleMeta = allMetas[index];
 
    if (singleMeta != null) {
      if (singleMeta.name.toLowerCase() == usernameMeta.toLowerCase()) {
        result = singleMeta.content;
       
        break;
      }
    }
  }
 
  return result;
}
function clearFollowings() {
  var followingCount = GM_getValue(followingCountOption, 0);

  for (var index = 0; index < followingCount; index++) {
    GM_deleteValue(followingOption + index);
  }
  GM_deleteValue(followingCountOption);
}

function loadFollowings() {
  var result = null;
 
  var followingCount = GM_getValue(followingCountOption, 0);
 
  if (followingCount > 0) {
    result = new Array(followingCount);
   
    for (var index = 0; index < followingCount; index++) {
      result[index] = GM_getValue(followingOption + index);
    }
  }
 
  return result;
}

function saveFollowings(followingsArray) {
  clearFollowings();
 
  if (followingsArray != null) {
    GM_setValue(followingCountOption, followingsArray.length);

    for (var index = 0; index < followingsArray.length; index++) {
      GM_setValue(followingOption + index, followingsArray[index]);
    }
  }
}

function checkReplyClass(followingsArray, itemClassName) {
  var result = false;
 
  if (followingsArray != null) {
    for (var index = 0; index < followingsArray.length; index++) {
      if (((className_Start + followingsArray[index] + className_Stop).toLowerCase() == itemClassName.toLowerCase()) ||
          ((className_Start + followingsArray[index] + className2_Stop).toLowerCase() == itemClassName.toLowerCase())){
        result = true;
       
        break;
      }
    }
  }
 
  return result;
}

function hideUnwantedReplies() {
  var followings = loadFollowings();
  var timeline   = document.getElementById(timelineId);
 
  var allItems   = timeline.getElementsByTagName(itemTag);
  var singleItem = null;

  for (var index = 0; index < allItems.length; index++) {
    singleItem = allItems[index];
 
    if (singleItem != null) {
      if (!checkReplyClass(followings, singleItem.className)) {
        singleItem.style.display = displayNone;
      }
    }
  }
 
  if (hideAgainTimer > 0) {
    setTimeout(hideUnwantedReplies, hideAgainTimer);
  }
}

// as taken from http://www.developers-guide.net/c/117-eine-einfuehrung-in-ajax-und-xmlhttprequest.html
function createHttpRequest() {
  var result = null;
 
  if (window.ActiveXObject) {
    try {
      // IE 6 and higher
      result = new ActiveXObject("MSXML2.XMLHTTP");
    } catch (e) {
      try {
        // IE 5
        result = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (e) {}
    }
  } else {
    if (window.XMLHttpRequest) {
      try {
        // Mozilla, Opera, Safari ...
        result = new XMLHttpRequest();
      } catch (e) {}
    }
  }
 
  return result;
}

function retrieveSource(url) {
  var result = null;

  var httpRequest = createHttpRequest();

  if (httpRequest != null) {
    httpRequest.open("GET", url, false);
    httpRequest.send(null);

    if ((httpRequest.readyState == 4) && (httpRequest.status == 200)) {
      result = httpRequest.responseText;
    }
  }

  return result;
}

function retrieveEntries(source) {
  var result = new Array();
  var count  = 0;

  if (source != null) {
    var element = document.createElement(paragraphTag);  
    element.innerHTML = source;
   
    var allAnchors   = null;
    var singleAnchor = null;

    var allSpans     = element.getElementsByTagName(spanTag);
    var singleSpan   = null;

    if (allSpans != null) {
      for (var index = 0; index < allSpans.length; index++) {
        singleSpan = allSpans[index];
       
        if (singleSpan != null) {
          if (singleSpan.className.toLowerCase() == userSpanClass.toLowerCase()) {
            allAnchors = singleSpan.getElementsByTagName(anchorTag);
           
            if ((allAnchors != null) && (allAnchors.length > 0)) {
              singleAnchor = allAnchors[0];
             
              if (singleAnchor != null) {
                result[count++] = singleAnchor.innerHTML;
              }
            }
          }
        }
      }
    }
   
    element.innerHTML = "";
  }
 
  return result;
}

function retrieveNextPage(source) {
  var result = null

  if (source != null) {
    var element = document.createElement(paragraphTag);  
    element.innerHTML = source;
   
    var allAnchors   = null;
    var singleAnchor = null;

    var allDivs   = element.getElementsByTagName(divTag);
    var singleDiv = null;

    if ((allDivs != null) && (allDivs.length > 0)) {
      for (var indexA = 0; indexA < allDivs.length; indexA++) {
        singleDiv = allDivs[indexA];

        if ((singleDiv != null) && (singleDiv.className.toLowerCase() == paginationId.toLowerCase())) {
          allAnchors = singleDiv.getElementsByTagName(anchorTag);
   
          if ((allAnchors != null) && (allAnchors.length > 0)) {
            for (var indexB = 0; indexB < allAnchors.length; indexB++) {
              singleAnchor = allAnchors[indexB];
         
              if ((singleAnchor != null) && (singleAnchor.rel.toLowerCase() == nextLinkRel.toLowerCase())) {
                result = singleAnchor.href;
           
                break;
              }
            }
          }
        }
      }
    }
   
    element.innerHTML = "";
  }
 
  return result;
}

function retrieveFollowings(url) {
  var result = new Array();
  var count  = 0;
 
  var source      = retrieveSource(url);
  var nextPage    = null;
  var outputArray = retrieveEntries(source);
 
  if ((outputArray != null) && (outputArray.length > 0)) {
    for (var index = 0; index < outputArray.length; index++) {
      result[count++] = outputArray[index];
    }
   
    do {
      nextPage = retrieveNextPage(source);
     
      if (nextPage != null) {
        if (showDebugAlerts) {
          alert("Proceeding with next page: " + nextPage);
        }
     
        source      = retrieveSource(nextPage);
        outputArray = retrieveEntries(source);
       
        if ((outputArray != null) && (outputArray.length > 0)) {
          for (var index = 0; index < outputArray.length; index++) {
            result[count++] = outputArray[index];
          }
        }
      }
    } while (nextPage != null);
   
    if (showDebugAlerts) {
      alert(result.length + " entries found");
    }
  }
 
  return result;
}

Im ersten Schritt muss man das Script konfigurieren. Es gibt ein paar Parameter die man setzen kann:

  • Sollen nur die Replies gefiltert werden, oder auch der Home-Screen z.B. von API-Retweets bereinigt werden?
  • Alle wieviele Millisekunden soll die Ansicht erneut von Unrat befreit werden?
  • Wessen Tweets sollen noch angezeigt werden? Die von den Leuten, denen man selber folgt, von den Leuten, die einem folgen oder von beiden Gruppen?
  • Sollen Debug-Meldungen angezeigt werden?

Als nächstes muss man das Script aktivieren und dann entweder die Seite mit der Liste seiner Follower oder seiner Followings besuchen. Der Grund? Tjaaa… weil ich die API nicht benutzen wollte, habe ich einen Crawler geschrieben, der diese Seiten durchcrawled und dadurch die Leute findet, die zu den entsprechenden Gruppen gehören… und das Aufrufen einer der beiden Seiten ist das Startsignal des Crawlers. Das bedeutet allerdings auch, dass man eine der beiden Seite regelmäßig besuchen sollte. ;-)

Filternde Grüße, Kenny

23 August 2010 ~ 5 Comments | ÄHNLICHE ARTIKEL

[Update] Memory in 2*n Schritten lösen…


Diesen Artikel drucken Diesen Artikel drucken
Diesen Artikel vorlesen Diesen Artikel vorlesen

Vorhin bin ich in den IT-Nachrichten über die internationale Informatik-Olympiade gestolpert – diese fand dieses Jahr zum 22sten Mal statt; dieses Mal in Ontario/Kanada. Eine Aufgabe hat mich besonders fasziniert: Wie löst man am besten eine Partie Memory?

In der Aufgabe war das Gerüst bereits vorgegeben – man musste nur noch die eigentliche Logik entwickeln. Dabei gab es zwei Teilaufgaben… bei der ersten musste man lediglich so weit kommen, dass alle Karten mindestens einmal aufgedeckt wurden. Um das zu Zählen, wurde für jedes aufgedeckte Paar – auch, wenn es nicht gleichzeitig aufgedeckt wurde – ein Bonbon verteilt. In einer vorgegebenen Zeit musste das selbstgeschriebene Programm alle 25 möglichen Bonbons erhalten.
Die nächste Schwierigkeitsstufe war dann, dass das gesamte Spiel in maximal 100 Kartenumdrehungen (bei 50 Karten im Spiel) gelöst werden musste. Natürlich ist die erste Aufgabe auch dann gelöst, wenn man auch direkt die zweite Aufgabe gelöst hat. ;-)

Als ich das laß, kam mir folgende einfache Idee: Wieso nicht erstmal alle Karten umdrehen, die Werte merken und dann einfach nur noch die Paare abgrasen? Dann würde man genau 50 Umdrehungen benötigen, um alle Karten zu kennen und weitere 50 Umdrehungen, um die Karten paarweise aufzudecken. :-)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
program PasMemory;

uses
  CardGiver,
  Memory;

var
  Line : String;

begin
  PrepareCards;
  PlayGame;

  WriteLn();
  WriteLn('Moves: ', TurnCounter);
  WriteLn('Over : ', GameOver);
  ReadLn(Line);
end.
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
unit CardGiver;

interface

function GameOver : Boolean;
function PrepareCards : Boolean;
function TurnCard(CardID : Byte) : Char;
function TurnCounter : Integer;

implementation

var
  CardStack : array [1..50] of Char;
  FirstCard : Byte;
  TurnCount : Integer;

function GameOver : Boolean;
var
  Index : Byte;
begin
  Result := true;

  for Index := Low(CardStack) to High(CardStack) do
  begin
    if (CardStack[Index] <> #0) then
      Result := false;
  end;
end;

function PrepareCards : Boolean;
const
  CardCount : Byte = 25;
var
  Cards : array [1..25] of Byte;
  Done  : Boolean;
  Index : Byte;
  Temp  : Byte;
begin
  FirstCard := 0;
  TurnCount := 0;

  for Index := Low(Cards) to High(Cards) do
    Cards[Index] := 2;

  for Index := Low(CardStack) to High(CardStack) do
  begin
    Done := false;
    repeat
       Temp := Succ(Random(CardCount));
       if (Cards[Temp] > 0) then
       begin
         Cards[Temp]      := Pred(Cards[Temp]);
         CardStack[Index] := Char(Pred(Temp+65));

         Done := true;
       end;
    until Done;
  end;

  Result := true;
end;

function TurnCard(CardID : Byte) : Char;
begin
  if ((CardID >= Low(CardStack)) and (CardID <= High(CardStack))) then
  begin
    if ((FirstCard = 0) and (CardStack[CardID] <> #0)) then
    begin
      Result    := CardStack[CardID];
      TurnCount := Succ(TurnCount);

      FirstCard := CardID;
    end
    else
    begin
      if ((FirstCard <> 0) and (FirstCard <> CardID) and (CardStack[CardID] <> #0)) then
      begin
        Result    := CardStack[CardID];
        TurnCount := Succ(TurnCount);

        if (CardStack[CardID] = CardStack[FirstCard]) then
        begin
          WriteLn('Pair : ', CardStack[CardID]);

          CardStack[CardID]    := #0;
          CardStack[FirstCard] := #0;
        end;

        FirstCard := 0;
      end
      else
      begin
        Result := #0;
      end;
    end;
  end
  else
    Result := #0;
end;

function TurnCounter : Integer;
begin
  Result := TurnCount;
end;

initialization
  Randomize;

end.
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
unit Memory;

interface

function PlayGame : Boolean;

implementation

uses
  CardGiver;

function PlayGame : Boolean;
var
  CardStack : array [1..50] of Char;
  Index     : Byte;
  IndexB    : Byte;
begin
  for Index := 1 to 25 do
  begin
    CardStack[Pred(Index * 2)] := TurnCard(Pred(Index * 2));
    CardStack[(Index * 2)]     := TurnCard((Index * 2));

    if (CardStack[Pred(Index * 2)] = CardStack[(Index * 2)]) then
    begin
      CardStack[Pred(Index * 2)] := #0;
      CardStack[(Index * 2)]     := #0;
    end;
  end;

  Index := 1;
  repeat
    if (CardStack[Index] <> #0) then
    begin
      for IndexB := Succ(Index) to High(CardStack) do
      begin
        if (CardStack[Index] = CardStack[IndexB]) then
        begin
          TurnCard(Index);
          TurnCard(IndexB);

          CardStack[Index]  := #0;
          CardStack[IndexB] := #0;

          Break;
        end;
      end;
    end;

    Index := Succ(Index);
  until (Index > High(CardStack));

  Result := true;
end;

end.

Da ich den vorgegebenen Kartenumdreh-Mechanismus leider nicht zum Laufen gekriegt hatte, habe ich auch diesen einfach nochmal neu implementiert (inklusive automatischem Durchmischen der Karten und einer Überprüfung, ob das Spiel wirklich vorbei ist). Und was soll ich sagen? Meine Idee war genau die richtige – und dank einer kleinen Optimierung (falls ein gleiches Paar beim erstmaligen Aufdecken schon gefunden wird, muss es kein zweites Mal betrachtet werden) braucht man teilweise sogar weniger als 100 Kartenumdrehungen… ;-)

Update:
Jan Hendrik Burdinski hatte die Idee geäußert, nicht einfach nur alle Karten einmal aufzudecken und dann abzuräumen, sondern schon beim Aufdecken vorher gefundene Partnerkarten mit zu berücksichtigen. Die Idee hat mir so gut gefallen, dass ich sie auch noch umsetzen wollte:

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
unit Memory;

interface

function PlayGame : Boolean;

implementation

uses
  CardGiver;

function PlayGame : Boolean;
var
  CardA     : Char;
  CardB     : Char;
  CardStack : array ['A'..'Z'] of Byte;
  Index     : Char;
  IndexB    : Byte;
begin
  for Index := 'A' to 'Z' do
    CardStack[Index] := 0;

  IndexB := 1;
  while (IndexB <= 50) do
  begin
    CardA := TurnCard(IndexB);
    if (CardA <> #0) then // card is valid
    begin
      if ((CardStack[CardA] >= 1) and (CardStack[CardA] <= 50)) then // card value has been seen before
      begin
        // flip matching card
        TurnCard(CardStack[CardA]);
        CardStack[CardA] := 255;
      end
      else
      begin
        if (CardStack[CardA] = 0) then // card value has not been seen before
        begin
          // save card position
          CardStack[CardA] := IndexB;

          // flip second card
          IndexB := Succ(IndexB);
          if (IndexB <= 50) then
          begin
            CardB := TurnCard(IndexB);

            if (CardA = CardB) then // 1st and 2nd card are a pair
              CardStack[CardB] := 255
            else
            begin
              if ((CardStack[CardB] >= 1) and (CardStack[CardB] <= 50)) then // card value has been seen before
              begin
                // flip matching cards
                TurnCard(IndexB);
                TurnCard(CardStack[CardB]);
                CardStack[CardB] := 255;
              end
              else
              begin
                // save card position
                CardStack[CardB] := IndexB;
              end;
            end;
          end;
        end;
      end;
    end;

    // move to next card
    IndexB := Succ(IndexB);
  end;

  Result := true;
end;

end.

Wie man sieht, ist die Logik etwas umfangreicher, da nun mehrere Schritte gemacht werden müssen:

  • Die erste Karte wird aufgedeckt.
  • Es wird überprüft, ob das Symbol der ersten Karte schonmal gefunden wurde.
  • Falls ja, wird die Partnerkarte aufgedeckt und von vorne begonnen (ein Pärchen wurd gefunden).
  • Falls nein, wird die Position des Symbols der ersten Karte gespeichert und die zweite Karte aufgedeckt.
  • Es wird überprüft, ob beide Karten identisch sind.
  • Falls ja, wird von vorne begonnen (ein Pärchen wurde gefunden).
  • Falls nein, wird geprüft, ob das Symbol der zweiten Karten schonmal gefunden wurde.
  • Falls ja, wird die zweite Karte (nochmal) und ihre Partnerkarten aufgedeckt und von vorne begonnen (ein Pärchen wurde gefunden).
  • Falls nein, wird die Position des Symbols der zweiten Karte gespeichert und von vorne begonnen.

Der Algorithmus ist meiner Meinung ziemlich performant und benötigt im Schnitt ca. 80 Kartenumdrehungen. Des Best-Case ist, dass die Karten paarweise sortiert sind. Der Worst-Case liegt übrigens dann vor, wenn die Karten so angeordnet sind, dass die Paarfindung immer durch das Umdrehen der zweiten Karte der Runde ausgelöst wird. Also z.B. in diesem Fall: “AYBACBDCEDFEGFHGIHJIKJLKMLNMONPOQPRQSRTSUTVUWVXWYX”

Spielerische Grüße, Kenny

23 August 2010 ~ 30 Comments | ÄHNLICHE ARTIKEL

Gewinne ein T-Shirt!


Diesen Artikel drucken Diesen Artikel drucken
Diesen Artikel vorlesen Diesen Artikel vorlesen

DAS GEWINNSPIEL WURDE BEREITS BEENDET!

Ich wurde von einem Shopbetreiber hier aus meiner Heimat Berlin angeschrieben und gefragt, ob ich nicht gerne ein paar T-Shirts unter die Leute bringen will. Da ich die Idee großartig fand, möchten Shirt-Selbst-Bedrucken.de und ich euch gerne zu einem Bloggergewinnspiel einladen! :D

Zu gewinnen gibt es:

Die Teilnahme ist ziemlich einfach:

  • Schreibt einen Artikel über das Gewinnspiel
  • Verlinkt die Seite Shirt-Selbst-Bedrucken.de (Linktext egal, nofollow erlaubt)
  • Schickt einen Trackback an diesen Gewinnspiel-Beitrag
  • Die ersten 15 Personen, die teilgenommen haben, bekommen ein T-Shirt! :D

Solltet ihr keinen Trackback schicken können, könnt ihr auch einen Link zu eurem Artikel unten in den Kommentaren hinterlassen. Für die Gewinnerermittlung gilt der Zeitstempel des Trackbacks/des Kommentars hier im Blog. Der Rechtsweg ist ausgeschlossen. Das Gewinnspiel endet am Sonntag, dem 29.08.2010, um Punkt 12:00 Mittags.

Unter allen, die bis zum Gewinnspielende teilgenommen haben, werden zusätzlich die beiden Gutscheine à 20€ verlost. Die Gewinnerziehung erfolgt per Random.org.

Was ihr von der Teilnahme habt?

  • Die ersten 15 Teilnehmer bekommen ein Gratis-T-Shirt :D !
  • Alle Teilnehmer erhalten einen dofollow-Backlink!
  • Alle Teilnehmer haben die Chance auf einen von zwei verlosten 20€ Shirt-Selbst-Bedrucken.de-Einkaufsgutschein!

Was die Gewinner tun müssen, um ihren Gewinn zu erhalten, erkläre ich euch am nächsten Sonntag in einem separaten Artikel. :-)

Viel Spaß beim Gewinnspiel! :D

23 August 2010 ~ 5 Comments | ÄHNLICHE ARTIKEL

sp!!k II: Die Funktionsweise


Diesen Artikel drucken Diesen Artikel drucken
Diesen Artikel vorlesen Diesen Artikel vorlesen

Nachdem ich euch das letzte Mal die Idee von sp!!k vorgestellt habe, wollte ich euch diese Woche erklären, was ich mir überlegt hatte, um die Theorie praktisch umzusetzen. Dabei sollte man wissen, dass mit der Umsetzung mehrere Fliegen mit einer Klappe geschlagen werden sollten:

  • Es sollte möglich sein, dass man seinen eigenen sp!!k-Server betreibt, aber trotzdem andere Nutzer auf diesem Server arbeiten können.
  • Es sollte möglich sein, alle wichtigen, schriftlichen Kommunikationsformen mit sp!!k abzudecken.
  • Der Erzeuger eines Inhaltes sollte stets die Kontrolle über seine eigenen Inhalte haben.
  • Spam sollte, soweit möglich, verhindert werden.

Fangen wir mit der Beschreibung der Lösung an der Wurzel an – den Conversations (engl. für “Gespräche”). Alle Gespräche laufen in sogenannten Konversationssträngen ab – diese bestehen aus einem Ausgangspunkt und beliebig vielen Anhängen (sogenannte Chapter [engl. für "Kapitel"]). Die Anhänge sind dabei selbst auch wieder Konversationsstränge, die Anhänge beinhalten können.

Nehmen wir als Beispiel ein Forum: In so einem Forum können Mitglieder “Threads” anlegen – das sind Themen, die besprochen werden sollen. Ein Thread besteht immer mindestens aus dem Ausgangspost und den Antworten der Threadteilnehmer. Je nach Forensoftware kann entweder nur auf den Ausgangspost geantwortet werden (wodurch alle Antworten automatisch zeitlich geordnet sind), oder aber man kann auf die Antwort eines anderen Threadteilnehmers antworten. Genau dies ist in sp!!k durch die Conversations und die Chapter möglich.
Aber nicht nur dort sind sie möglich. Ein gutes Beispiel sind E-Mail-Konversationen: Dort gibt es die verschiedensten Versuche, Konversationsstränge abzubilden – anhand der Betreffzeile, anhand der Empfängeradressen, anhand von zusätzlichen Header-Informationen… bei sp!!k handelt es sich beim “E-Mailing” einfach um eine Conversation (“Initial-E-Mail”) und beliebig vielen Chapters (“Antworten”).
Bei Chaträumen ist es wieder da gleiche: Irgendjemand eröffnet durch die erste Nachricht den Raum (die Conversation) und alle anderen können Antworten in diesen Raum posten.
Wer jetzt mitgedacht hat, weiß, dass es bei Webseiten natürlich der gleiche Vorgang ist: ein Inhalt (z.B. ein “Blogeintrag”) plus beliebig viele Kommentare… es ist immer das gleiche Bild. :-)

So eine Conversation (und damit auch die Chapter) zeichnen sich durch ein paar Besonderheiten aus. Zum einen gibt es für jede Conversation eine Art Access Control List, mit der der Initiator bestimmen kann, wer die Konversation lesen darf und wer der Konversation weitere Kapitel hinzufügen darf. Über diesen Mechanismus wird der Unterschied zwischen öffentlichen Inhalten und privaten Inhalten erreicht: Eine “E-Mail” hat also eine sehr strenge ACL, während eine öffentliche “Webseite” eine weitreichende ACL hat. :-)

Zudem besitzt so eine Konversation eine Art Haltbarkeitsdatum – je nach Aufgabe (E-Mail, Webseite oder Chat) muss so ein Inhalt nämlich unterschiedlich lang gültig sein. Die Haltbarkeitsspanne fängt an bei “für immer” und endet bei “bis die Nachricht abgerufen wurde”.

Der nächste Punkt dürfte ein wenig überraschend sein: ALLE Inhalte, die jemand produziert, verbleiben in dem Speicherbereich dieser Person. Dabei ist es egal, ob es sich um eine Webseite, einen Forenbeitrag, eine Chatnachricht oder um eine E-Mail handelt. Es gibt keine Inhalte, die irgendwo anders hin dupliziert werden oder ähnliches.
Dadurch wird gewährleistet, dass jeder sp!!k-Nutzer zu jederzeit bestimmen kann, welche seiner Inhalte abrufbar sind und von wem diese abrufbar sind. Ein netter Nebeneffekt ist, dass Spam-Nachrichten damit (hoffentlich) effektiv unterbunden werden. Der Grund ist, dass ein Spamer jede Spam-Nachricht in seinem eigenen Bereich aufbewahren muss. Sollte sich eine Nachricht also als Spam herausstellen, könnte man soetwas wie eine Blacklist verweden, um andere Empfänger vor dieser Nachricht zu warnen.

Jetzt stellt sich der ein oder andere sicherlich die Frage: “Wenn die Nachrichten beim Absender bleiben, wie weiß der Empfänger dann, dass er sie lesen soll?”

Genau dafür gibt es sogenannte Calls (engl. für “Anrufe”). Sollte ein neues Dokument veröffentlicht werden, das an eine (oder mehrere Personen) addressiert ist, wird den Empfängern ein Call zugesendet, der ihnen mitteilt “hier gibt es eine neue Nachricht, auf die ihr Zugriff habt”. Neben diesen Calls für private Inhalte kann man zudem die Calls öffentlicher Inhalte abonnieren – damit man z.B. über neue Blogbeiträge informiert wird.

Da öffentliche Inhalte normalerweise nicht per Call verteilt werden, gibt es für sie zwei Catalogs (engl. für “Kataloge”), in denen alle öffentlichen Inhalte indiziert werden. Dabei ist der eine Katalog für lokale Conversations gedacht (Konversationen, die nur Mitglieder des eigenen Servers lesen dürfen) und ein Katalog für globale Konversationen (also Conversations, die auch von Mitgliedern anderer Server gelesen werden dürfen).

Und da wären wir auch schon beim letzten Thema für heute: andere Server. Ich habe mir sp!!k als Mischung zwischen einem zentralen und einem dezentralen Netzwerk vorgestellt. Dafür gibt es einige Gründe: So wird es wahrscheinlich Nutzer geben, die lieber ihren eigenen Server betreiben wollen, um ihre Daten keinem Provider anvertrauen zu müssen. Andere wiederum haben wahrscheinlich garnicht die Technik und das Wissen, um einen Server zu betreiben – die werden dann auf freie oder bezahlte Angebote zurückgreifen. Natürlich sollen trotzdem alle die Möglichkeit haben, miteinander zu kommunizieren, wenn sie das wollen.

Um nun den Zugriff auf die Inhalte eines anderen Servers zu erleichtern, habe ich mir überlegt, dass es das einfachste wäre, jeder Conversation einen eindeutigen Namen zuordnen zu lassen. Die URL-Struktur habe ich mir so vorgestellt:

spiik://Username@Host:Port/Conversation

Wenn solch eine URL nun auf einen fremden Host zeigt, soll sich der eigene Server zu dem fremden Server verbinden, die Identität sicherstellen und anschließend den Inhalt abrufen und an den User weiterleiten. Wie das ganze sicherheitstechnisch ablaufen soll, erkläre ich euch beim nächsten Mal… ;-)

Konversationsgrüße, Kenny

20 August 2010 ~ 1 Comment | ÄHNLICHE ARTIKEL

[Update] Eigene Tweets auf Twitter.com verstecken…


Diesen Artikel drucken Diesen Artikel drucken
Diesen Artikel vorlesen Diesen Artikel vorlesen

Auf Twitter habe ich vorhin spaßeshalber verkündet, dass ich mich selber entfolgen würde, wenn ich es könnte :D . Um nicht darauf warten zu müssen, bis Twitter dieser Bitte nachkommt, habe ich mich selbst kurz dran gesetzt und einen Stil für das Firefox-Addon Stylish erstellt:

1
2
3
4
5
6
7
@namespace url(http://www.w3.org/1999/xhtml);

@-moz-document domain("twitter.com") {
  .mine {
    display: none !important;
  }
}

Wie ihr Stylish benutzt, könnt ihr unter anderem in diesem Artikel erfahren. :-)

Update:
Die Idee dazu lieferte übrigens @dqmhose, von dem man seit Kurzem auch seinen neuen Blog bewundern darf. :-)

Versteckte Grüße, Kenny

19 August 2010 ~ 9 Comments | ÄHNLICHE ARTIKEL

[Update] Die Fußgänger-Ampel…


Diesen Artikel drucken Diesen Artikel drucken
Diesen Artikel vorlesen Diesen Artikel vorlesen

Heute während der Autofahrt zur Arbeit musste ich unweigerlich an die Fußgängerampel denken. Und da ich die dabei herausgefundene Erkenntnis so großartig fand, wollte ich ihr gern einen eigenen Artikel widmen… :D

Es hat mich immer schon geärgert, dass bei getrennten Fahrspuren (z.B. durch eine Verkehrsinsel) eine Straßenseite immer eher grün bekommt als die andere. Und man selber steht natürlich immer auf der falschen Seite und muss den anderen Fußgängern dabei zugucken, wie sie einem entgegen laufen, während man selber noch auf sein Grün wartet. Aber warum ist das so? Warum werden bei einer breiten Straße nicht beide Laufrichtungen gleichzeitig auf grün geschaltet? Die Antwort darauf ist verblüffend! :D

Um die Situation – die damit vermieden wird – mal darzustellen, habe ich eine typische Kreuzung aufgemalt. Die Autos, die von links nach rechts fahren haben gerade rot bekommen und die, die von oben nach unten fahren, kriegen gleich grün. Die Fußgänger dürfen natürlich zuerst loslaufen.
In unserem fiktiven Beispiel dürfen alle Fußgänger gleichzeitig loslaufen. Und da passiert das Unglück! Ein von rechts kommender Autofahrer war bei Gelb noch schnell über die Ampel gefahren und rast nun direkt auf die Fußgänger zu, die bereits auf die Straße gelaufen sind… eine gefährliche Situation!

Die Fußgängerampel

Um genau diese gefährliche Situation zu vermeiden, dürfen die Fußgänger, die aus der Sicht der Autofahrer “hinter der Kreuzung” über Straße laufen, erst später losgehen als die Fußgänger, die “vor der Kreuzung” die Straße überqueren. Dadurch haben alle Autos die Kreuzung bereits verlassen, bevor die gefährdeten Fußgänger die Straße betreten dürfen.

So gesehen also super einfach und super intelligent – aber welcher normale Fußgänger wird schon solchen Gedankengängen folgen, während er sich ärgert, dass er mal wieder länger warten muss, als alle anderen…

Update:
Ich habe die Illustration nochmal neu gemacht: Hoffentlich wird das Problem jetzt deutlicher. Die alte Abbildung sah so aus:

Fußgängerampeln



Verkehrte Grüße, Kenny

187
no-www.org extra-www.org

Datenbank: 33 Abfragen in 0.9260.926 Sekunden