Kürzlich stand ich wieder einmal vor einem interessanten Problem, welches es zu lösen galt. Eine bestimmte Aktion sollte ausgeführt werden, wenn ein Element (bspw. durch Klicken) sichtbar gemacht wird. In diesem Fall sollte eine asynchrone Anforderung (AJAX) ausgeführt werden. Weil ich aber ein jQuery-Accordion eingesetzt habe, hatte ich keinen direkten Zugriff auf die show-Methode (mal abgesehen vom Umschreiben selbiger). Daher musste ein Weg gefunden werden, eine Aktion auszulösen, sobald sich die Sichtbarkeit des Elements änderte.
Nach ein wenig rumsuchen habe ich schließlich die passenden Events gefunden: DOMAttrModified für Firefox und Opera und onpropertychange für den Internet Explorer (für Chrome und Safari gibt es leider keine einfache Lösung). Dazu die beiden Definitionen:
Occurs after Attr.value has been modified and after an Attr node has been added to or removed from an Element. Document Object Model (DOM) Level 3 Events Specification
Fires when a property changes on the object. onpropertychange Event
Beide Events können in jQuery für ein Element zugleich an einen Eventhandler gebunden werden (man beachte, dass das Event für den IE hier nur propertychange heißt):
$('#elem').bind('DOMAttrModified propertychange', myfunc)
Beim Accordion ändern sich aber ständig die Attribute, wenn es auf- oder zuklappt aufgrund der Animation. Daher wollte ich es nur einmal ausführen lassen. Dies geschieht über die jQuery-Methode one().
$('#elem').one('DOMAttrModified propertychange', myfunc)
In der Funktion myfunc(e) hat man dann, über den standardmäßig übergebenen ersten Parameter e, Zugriff auf das Event-Objekt. Im Internet Explorer geschieht dies über das globale window.event-Objekt.
Im Event-Objekt kann man schließlich leicht überprüfen, ob das Objekt gerade eingeblendet wurde. Dazu stellt das Event-Objekt die Eigenschaft newValue zur Verfügung, welche man nur auf den Wert display: block; überprüfen muss (wird von show() standardmäßig gesetzt). prevValue gibt die vorherigen Attribute (evtl. mehrere!) aus.
Im Internet Explorer sollte man das globale window.event.propertyName überprüfen und gleichzeitig checken, wie die neuen Attribute sind. Dazu prüft man die Eigenschaft window.event.srcElement.style.display auf den Wert block.
Zusammenfassend sieht der Code für die (einmalige) Überwachung der Zustandsänderung und die auszuführende Aktion so aus:
$('#elem').one('DOMAttrModified propertychange', function(e) {
if((e.attrName == 'style' && e.newValue == 'display: block;') || (window.event.propertyName == 'style.display' && window.event.srcElement.style.display == 'block')) {
alert('Ich bin sichtbar geworden');
// weitere Aktionen hier einfügen
}
});
Heute werde ich wieder einmal eine nützliche PHP-Funktion vorstellen. Genauer gesagt handelt es sich um eine Klasse, die CSS und JavaScript komprimieren kann. Bei diesem Minification (oder minify) genannten Prozess, werden unnötige Kommentare, Leerzeichen, Tabulatoren und Zeilenumbrüche entfernt, wodurch die Datei kleiner wird und so schneller an den Benutzer übertragen werden kann. Zusätzlich kann in der Klasse der „bereinigte“ Quellcode auch noch vor der Übertragung mit gzip oder Deflate komprimiert werden, damit er noch schneller übertragen werden kann.
<?php
class minifier {
public function minify_CSS($str, $zip = FALSE) {
$str = str_replace(array("\r", "\n", "\t"), '', $str);
$str = preg_replace('#/\*(.*)\*/#U', '', $str);
header('Content-Type: text/css; charset=UTF-8');
header('Cache-Control: public, max-age=1209600');
if($zip === TRUE)
$str = $this->zip($str);
echo $str;
}
public function minify_JS($str, $zip = FALSE) {
$str = preg_replace('#//.*#', '', $str);
$str = str_replace(array("\r", "\n", "\t"), '', $str);
$str = preg_replace('#/\*(.*)\*/#U', '', $str);
header('Content-Type: text/javascript; charset=UTF-8');
header('Cache-Control: public, max-age=1209600');
if($zip === TRUE)
$str = $this->zip($str);
echo $str;
}
protected function zip($str) {
if(isset($_SERVER['HTTP_ACCEPT_ENCODING']) === TRUE) {
if(stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') !== FALSE) {
header('Content-Encoding: deflate');
$str = gzdeflate($str, 9);
} elseif(stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) {
header('Content-Encoding: gzip');
$str = gzencode($str, 9);
}
}
return $str;
}
}
?>
Die Klasse kann dann in einer beliebigen Datei so aufgerufen werden:
<?php
require_once('minifier.class.php');
$min = new minifier();
$min->minify_JS('[JavaScript-Quellcode]', TRUE);
$min->minify_CSS('[CSS-Quellcode]', TRUE);
?>
Im Gegensatz zu vielen anderen Minifiern habe ich versucht die Optimierungen „leichtgewichtig“ zu gestalten, also einen Kompromiss aus Bandbreiteneinsparung und Ressourcenschonung zu finden. Viele andere Minifier nehmen nämlich noch mehr Optimierungen vor (Variablenumbenennung, komplizierte RegEx-Konstrukte), die viel Rechenzeit benötigen und daher nur bedingt für die On-the-fly-Erstellung von komprimiertem Code geeignet sind. Sicherlich lassen sich damit die Dateigrößen teils erheblich weiter reduzieren aber auf Kosten der serverseitigen Performance.
Auf blogeum wird die Klasse in einer erweiterten Form auch eingesetzt, um CSS und JS vor der Übertragung zu verkleinern und die Übertragungszeiten zu vermindern.
Wie immer steht der Code unter einer Creative Commons-Lizenz.
Mal wieder eine kleine aber feine PHP-Funktion. Diesmal geht es darum, einen Rechner per Wake-on-LAN-Signal (WOL) über das Netzwerk aufzuwecken. Prinzipiell muss dazu nur eine Verbindung zu einem anderen Rechner im Netzwerk oder einem Router aufgebaut werden, der dann das entsprechende Signal im Netzwerk herumschickt und so den gewünschten Computer aufweckt, sobald dieser das Signal empfängt.
Das Signal ist relativ einfach aufgebaut: 6 Mal der Hexwert FF und anschließend 16 Mal die MAC-Adresse der Netzwerkkarte des zu weckenden Rechners. Dieses sogenannte MagicPacket wird via UDP an Port 0, 7 oder 9 gesendet.
Wenn man die IP-Adresse des Routers nicht kennt, kann auch ein Broadcast (Signal an alle Geräte im Netzwerk) gesendet werden, sofern der Router dies zulässt.
<?php
$mac = '00:00:00:00:00:00';
$ip = 'udp://255.255.255.255';
function wakeup($mac, $router) {
if(strlen($mac) !== 17)
return FALSE;
if(filter_var($router, FILTER_VALIDATE_URL) === FALSE)
return FALSE;
$raw = sscanf($mac, '%2x:%2x:%2x:%2x:%2x:%2x');
$func = create_function('$v', 'return isset($v);');
$raw = array_filter($raw, $func);
if(sizeof($raw) !== 6)
return FALSE;
$fp = fsockopen($router, 9, $errnum, $errstr, 4);
if(is_resource($fp) === FALSE) {
trigger_error('['.$errnum.'] '.$errstr, E_USER_ERROR);
return FALSE;
}
$magicp = "\xFF\xFF\xFF\xFF\xFF\xFF";
$hexmac = '';
foreach($raw AS $c) {
$hexmac .= chr($c);
}
$magicp .= $hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac;
fwrite($fp, $magicp);
fclose($fp);
printf('Wake-Up-Signal an %02X:%02X:%02X:%02X:%02X:%02X übertragen!', $raw[0], $raw[1], $raw[2], $raw[3], $raw[4], $raw[5]);
return TRUE;
}
wakeup($mac, $ip);
?>
Ich benutze diese Funktion um den Server aus der Ferne aus dem Ruhezustand zu holen, um mit ihm arbeiten zu können.
Bei meiner Arbeit am Blog stoße ich immer wieder auf Probleme, die eine adäquate Lösung verlangen. Im Web gibt es zahlreiche Funktionen, die diese Probleme lösen (sollen). Doch nicht immer sind diese Funktionen auch nach meinem Geschmack. Deshalb nutze ich meist nur die grundlegenden Ideen dieser Funktionen und schreibe dann einfach meine eigenen.
Daher möchte ich nun einmal die Thumbnail-Funktion vorstellen, die ich hier benutze und die mir schon sehr gut gefällt.
<?php
function create_thumbnail($src_file, $max_x = 60, $max_y = 60) {
if(extension_loaded('gd') === FALSE)
return FALSE;
list($src_x, $src_y, $type) = getimagesize($src_file);
switch($type) {
case IMAGETYPE_JPEG:
$src_img = imagecreatefromjpeg($src_file);
break;
case IMAGETYPE_PNG:
$src_img = imagecreatefrompng($src_file);
break;
case IMAGETYPE_GIF:
$src_img = imagecreatefromgif($src_file);
break;
default:
return FALSE;
}
if(is_resource($src_img) === FALSE)
return FALSE;
$ratio = min($max_x/$src_x, $max_y/$src_y);
$thumb_x = round($src_x * $ratio);
$thumb_y = round($src_y * $ratio);
if($ratio < 1) {
$thumb_img = imagecreatetruecolor($thumb_x, $thumb_y);
imagecopyresampled($thumb_img, $src_img, 0, 0, 0, 0, $thumb_x, $thumb_y, $src_x, $src_y);
} else {
$thumb_img =& $src_img;
}
$thumb_file = '<Thumbnailordner>/'.$src_file;
switch($type) {
case IMAGETYPE_JPEG:
imagejpeg($thumb_img, $thumb_file, 50);
break;
case IMAGETYPE_PNG:
imagepng($thumb_img, $thumb_file);
break;
case IMAGETYPE_GIF:
imagegif($thumb_img, $thumb_file);
break;
default:
imagejpeg($thumb_img, $thumb_file, 50);
}
@imagedestroy($src_img);
if($ratio < 1)
@imagedestroy($thumb_img);
return TRUE;
}
?>
Die Schnittstelle gestaltet sich sehr einfach. Übergeben werden nur Pfad und Dateiname der Ursprungsdatei und die maximale Ausdehnung in X- und Y-Richtung der Thumbnail-Datei. Die Ursprungsdatei wird dann proportional verkleinert, je nachdem welche Dimension überschritten wurde.
Die angegebene Funktion ist sicher nicht perfekt. Für Verbesserungsvorschläge bin ich immer offen und werde sie wenn nötig auch gerne einbauen.
Die Funktion steht, wie alle anderen Blog-Inhalte auch, unter einer Creative Commons-Lizenz.