1) $ts=$tsize; if($ts) return 'album'; else return 'thumb'; } /** Determines whether $fname is the name an image file. * If so, it return its MIME type. */ function image_mime($fname) { $exts=array( '%/[^.][^/]*[.]jpe?g$%i'=>'image/jpeg', '%/[^.][^/]*[.]png$%i'=>'image/png', '%/[^.][^/]*[.]gif$%i'=>'image/gif' ); foreach($exts as $patt => $mime) { if( preg_match($patt,$fname) && is_file($fname) && is_readable($fname) ) return $mime; } return FALSE; } function hsc($s) { return htmlspecialchars($s); } $admin_message_text=''; function admin_message($msg) { global $admin_message_text; $admin_message_text.='
'.$msg.'
'; } /** Takes an EXIF format rational number and translates to a decimal. */ function rational2dec($rational) { $num=explode('/',$rational); if(count($num)==1) $result=intval($num[0]); if(count($num)==2) eval('$result='.intval($num[0]).'/'.intval($num[1]).';'); return strval($result); } /** Turns file basenames into 'nice' HTML. */ function nice($s) { return hsc( str_replace('_',' ',$s) ); } ?> checked=array(); $this->path_links=array(); // { name -> link } // Make sure that the _exa directory exists. $this->album('.'); // Find out where we are. $path=getcwd(); $link=array(); while(TRUE) { if(!is_dir($path.'/_exa')) break; $L=implode('/',array_reverse($link)); $this->top=strlen($L)?$L:'.'; $this->path_links[hsc(basename($path))]=$this->top; $link[]='..'; $newpath=dirname($path); if($newpath==$path) break; $path=$newpath; } $this->path_links=array_reverse($this->path_links,true); // Read the contents of the current directory. list($this->directories,$this->imagefiles)=$this->read_contents(); } /** Ensures that the named directory is exists, and return the path. */ function get_dir($dirname) { if(!array_key_exists($dirname,$this->checked)) { if(!is_dir($dirname)) { mkdir($dirname,0777); chmod($dirname,0777); } $this->checked[$dirname]=TRUE; } return $dirname; } /** Returns the path of the current album dir. */ function album($root) { return $this->get_dir($root.'/_exa'); } /** Returns the relative path of the *top* _exa dir. */ function top_album() { return $this->top.'/_exa'; } /** Returns the relative path to the _exa/.d directory. */ function image($image) { return $this->get_dir( $this->album($image->info['dirname']).'/'.$image->info['basename'].'.d' ); } /** Returns the relative path FROM root TO here. */ function path() { global $options; $result=array_keys($this->path_links); $result[0]=$options->top_name; return implode('/',$result); } /** Returns the relative path FROM root TO here, as a series of * HTML links. * Set parameter to * - TRUE: forces last link * - FALSE: suppresses last link * - default: last is link if there are sub-directories */ function path_links($link_last=1) { global $options; if($link_last!==TRUE && 0==count($this->directories)) $link_last=FALSE; $result=array_keys($this->path_links); $result[0]=$options->top_name; $links=array_values($this->path_links); $len=count($result); for($i=0; $i<$len; $i++) { // Record where we came from, so that doc_dir() can go to the correct page if($i<$len-1) $from_dir='/index.php?from_dir='.urlencode($result[1+$i]); else $from_dir=''; if($link_last || $i<$len-1) $result[$i]='' .$result[$i].''; if($i==$len-1) $result[$i]=''.$result[$i].''; } $result=implode('/',$result); if(strlen($options->home_url)) { $result=sprintf('%s  '."\n", $options->home_url, $options->home_name ).$result; } return $result; } /** Returns a sorted list of images in $dirname. */ function read_contents($dirname='.') { $imagefiles=array(); $directories=array(); if($handle=opendir($dirname)) { while(FALSE !== ($fname=readdir($handle))) { $fpath=$dirname.'/'.$fname; if(is_dir($fpath) && is_writeable($fpath)) { if(preg_match('/^[^._]/',$fname)) $directories[]=$fname; } elseif($mime=image_mime($fpath)) { $im=new ImageFile($fpath,$mime); //$imagefiles[$fname]=new ImageFile($dirname.'/'.$fname,$mime); $imagefiles[sprintf('%012d_%s',$im->date_time(),$fname)]=$im; } } // end while closedir($handle); ksort($imagefiles); } return array($directories,array_values($imagefiles)); } function current_album_title() { global $options; if(count($this->path_links)>1) return nice(basename(getcwd())); else return $options->top_name; } } // Construct the global singleton. $dirs=new Dirs(); ?> top_album().'/config.php'; if(file_exists($config_fname)) { include $config_fname; // Check admin password. $passwd=''; if(array_key_exists('passwd',$_POST)) { $passwd=md5($_POST['passwd'].$this->secret); setcookie('exa_passwd',$passwd,0,'/'); } elseif(array_key_exists('logout',$_GET)) { setcookie('exa_passwd','',time()-100000,'/'); // Unset cookie admin_message('Logged out.'); } elseif(array_key_exists('exa_passwd',$_COOKIE)) { $passwd=$_COOKIE['exa_passwd']; } if(0==strcmp($passwd,$this->passwd)) $is_admin=true; elseif(array_key_exists('passwd',$_POST)) admin_message('Incorrect password.'); } else { // Set secret by picking 20 random characters from a list. $this->secret=''; $possible='0123456789abcdfghjkmnpqrstvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'; while(strlen($this->secret) < 20) $this->secret.=substr($possible, mt_rand(0,strlen($possible)-1), 1); $this->exif=function_exists('exif_read_data'); } } function save() { global $dirs; $config_fname=$dirs->top_album().'/config.php'; $handle=fopen($config_fname,'wb'); fwrite($handle," $v) fwrite($handle,sprintf("\$this->%s='%s';\n",$k,$v)); fwrite($handle,"?>\n"); fclose($handle); } } // Construct the global singleton $options=new Options(); ?> src=$src; if($width) { $this->width=$width; $this->height=$height; } else { list($this->width,$this->height) = getimagesize($src); } } function html() { return 'src="'.$this->src.'" ' . 'width="'.$this->width.'" ' . 'height="'.$this->height.'"'; } function ehtml() { echo $this->html(); } } ?> path=$path; $this->id=$fname; $c=file_get_contents($path); list($this->author,$this->text) = explode("\n",$c."\n",2); list($t,$dummy) = explode('-',$fname.'-',2); $this->time=intval($t); } function square() { return dirname(dirname($this->path)).'/square.jpg'; } function link() { $imgdir=dirname(dirname($this->path)); $fname=basename($imgdir); $fname=substr($fname,0,strlen($fname)-2); $dirname=dirname(dirname($imgdir)); if('.'==$dirname) return '?d=c&f='.urlencode($fname); else return $dirname.'/?d=c&f='.urlencode($fname); } } ?> mime=$mime; $this->init_fname($fname); $this->read_exif(); } function init_fname($fname) { $this->fname=$fname; $this->info=pathinfo($fname); $b=$this->info['basename']; $e=$this->info['extension']; $this->name=substr($b,0,strlen($b)-strlen($e)-1); } function move($fname) { global $dirs; if(file_exists($fname)) return FALSE; $old_thumb_dir=$dirs->image($this); if( rename($this->fname,$fname) ) { $this->init_fname($fname); $new_thumb_dir=$dirs->image($this); return rename($old_thumb_dir,$new_thumb_dir); } return FALSE; } function set_title($new_title) { return $this->move( $this->info['dirname'].'/' .preg_replace('%[ \\/]%','_',$new_title) .'.'.$this->info['extension'] ); } /** Loads the image */ function image() { if($this->mime=='image/jpeg') return imagecreatefromjpeg($this->fname); else if($this->mime=='image/png') return imagecreatefrompng($this->fname); else if($this->mime=='image/gif') return imagecreatefromgif($this->fname); else return FALSE; } /** Returns the square filename. */ function square() { global $dirs; $result=$dirs->image($this).'/square.jpg'; if(!file_exists($result) || filemtime($result)fname)) { // Make a small square thumbnail. list($w0,$h0) = getimagesize($this->fname); if($w0<$h0) { // Tall $d0=$w0; $x0=0; $y0=($h0-$w0)/2; } else { // Wide $d0=$h0; $x0=($w0-$h0)/2; $y0=0; } // Zoom in a bit more. $fringe=$d0/10; $x0+=$fringe; $y0+=$fringe; $d0-=(2*$fringe); $image=$this->image(); $square=imagecreatetruecolor(75,75); imagecopyresampled( $square, $image, 0,0, // dst x,y $x0,$y0, // src x,y 75,75, $d0,$d0 ); // Save it. imagejpeg($square,$result,85); imagedestroy($square); chmod($result,0666); } return new ImageRef($result,75,75); } /** Returns the resized filename. */ function resized($img_size) { global $dirs; if($img_size==0) return $this->square(); elseif($img_size==1) { $name='small'; $W=240; $H=240; } elseif($img_size==2) { $name='carousel'; $W=500; $H=500; } elseif($img_size==3) { $name='medium'; $W=800; $H=533; } elseif($img_size==4) { $name='large'; $W=1024; $H=700; } else { return new ImageRef($this->fname); } $result=$dirs->image($this).'/'.$name.'.jpg'; if(!file_exists($result) || filemtime($result)fname)) { // Make a file. list($w0,$h0) = getimagesize($this->fname); if( $h0*$W/$w0 <= $H) { $w1=$W; $h1=$h0*$W/$w0; } else { $w1=$w0*$H/$h0; $h1=$H; } $image=$this->image(); $resized=imagecreatetruecolor($w1,$h1); imagecopyresampled( $resized, $image, 0,0, // dst x,y 0,0, // src x,y $w1,$h1, $w0,$h0 ); // Save it. imagejpeg($resized,$result,85); imagedestroy($resized); chmod($result,0777); } return new ImageRef($result,$w1,$h1); } /** Reads the EXIF data for this image. */ function read_exif() { if(function_exists('exif_read_data')) { // exif_read_data() can spew warnings, so suppress them with @ $exif_data= @ exif_read_data($this->fname,'EXIF'); if($exif_data===FALSE) return; // Create the summary string. $pieces=array(); $fields=array('Model'=>'%s', 'FocalLength'=>'%smm', // 'DateTime'=>'%s', 'ExposureTime'=>'%ss', 'FNumber'=>'f%s', 'ISOSpeedRatings'=>'ISO%d'); $rationals=array('FNumber','FocalLength'); foreach($fields as $k => $templ) { if(array_key_exists($k,$exif_data)) { $v=$exif_data[$k]; if(in_array($k,$rationals)) $v=rational2dec($v); $pieces[]=sprintf($templ,$v); } } $this->exif=implode(",\n",$pieces); // Read the date time if(array_key_exists('DateTime',$exif_data)) { $date_time=explode(' ',$exif_data['DateTime']); if(count($date_time)>=2) { $d=explode(':',$date_time[0]); $t=explode(':',$date_time[1]); if(count($d)>=3 && count($t)>=3) $this->dt=mktime( intval($t[0]),intval($t[1]),intval($t[2]), intval($d[1]),intval($d[2]),intval($d[0]) ); } } // Read the EXIF comment if(array_key_exists('COMPUTED',$exif_data)) { $c=$exif_data['COMPUTED']; if(isset($c['UserComment']) && isset($c['UserCommentEncoding'])) { if('ASCII'==$c['UserCommentEncoding']) $this->exif_caption=hsc($c['UserComment']); // TODO: support Unicode & JIS encoding here. } } } } /** Returns the date and time (unix time) of the image, from Exif if * possible, otherwise just the file timestamp. */ function date_time() { if($this->dt==-1) $this->dt=filemtime($this->fname); return $this->dt; } /** Returns the caption for this image, or FALSE if there is none. */ function caption() { global $dirs; $caption_fname=$dirs->image($this).'/caption.html'; $result=false; if(file_exists($caption_fname)) { $handle=fopen($caption_fname,'r'); $caption=fread($handle,1024); if(strlen($caption)>0) $result=$caption; fclose($handle); } elseif(strlen($this->exif_caption)) { $result=$this->exif_caption; } return $caption; } function short_caption($limit=40,$format='%s') { $caption=$this->caption(); if(!$caption) return ''; $result=explode("\n",$caption); $result=$result[0]; if(strlen($result)>$limit) { $words=preg_split('/[\s]+/',$result); $c=0; for($i=0; $iimage($this).'/caption.html'; $handle=fopen($caption_fname,'w'); fwrite($handle,$txt); fclose($handle); } function numcomments($format='%s') { global $dirs; $result=0; $comments_dir=$dirs->image($this).'/comments'; if(is_dir($comments_dir) && $dhandle=opendir($comments_dir)) { while(FALSE !== ($fname=readdir($dhandle))) if($fname!='.' && $fname!='..') $result++; closedir($dhandle); } if($result==1) return sprintf($format,$result.' comment'); elseif($result>1) return sprintf($format,$result.' comments'); else return ''; } function comments() { global $dirs; $result=array(); $comments_dir=$dirs->image($this).'/comments'; if(is_dir($comments_dir) && $dhandle=opendir($comments_dir)) { while(FALSE !== ($fname=readdir($dhandle))) { $path=$comments_dir.'/'.$fname; if(is_file($path) && is_readable($path)) { $result[$fname]=new Comment($path,$fname); } } // end while closedir($dhandle); if(count($result)) { ksort($result); return $result; } } return FALSE; } function addcomment($author,$comment) { global $dirs; $comments_dir=$dirs->get_dir( $dirs->image($this).'/comments' ); // If the same IP address submits two comments in the same second, then // we lose one. Ho hum. $fname=sprintf($comments_dir.'/%012d-%s',time(),$_SERVER['REMOTE_ADDR']); $handle=fopen($fname,"w"); if($handle!==FALSE) { fwrite($handle,$author."\n".$comment); fclose($handle); } } function delcomment($cid) { global $dirs; $comments_dir=$dirs->get_dir( $dirs->image($this).'/comments' ); $fname=$comments_dir.'/'.$cid; if(is_file($fname)) unlink($fname); } } ?> items_per_page=$items_per_page; $this->num_items=$num_items; $this->num_pages=ceil($num_items/$items_per_page); $this->p=minmax(0,$this->num_pages-1,$p); } /** Starting point for the current page. */ function begin() { return( $this->items_per_page * $this->p ); } /** End point for the current page. */ function end() { return min( $this->num_items, $this->items_per_page * (1+$this->p) ); } /** Emit a page index. Prev/Next buttons, highlights current page. */ function index($link) { $bracket=5; $run=$bracket*2+1; $spacer=False; echo '
'; if($this->p>0) echo '   <  '; else echo '   <  '; for($j=0; $j<$this->num_pages; $j++) { if($this->p==$j) { echo ''.($j+1).''; $spacer=False; } elseif($j==0 || $j+1==$this->num_pages || abs($j-$this->p)<$bracket || ($this->p<$bracket && $j+1<$run) || ($this->p+$bracket>=$this->num_pages && $j+$run-1>=$this->num_pages) ) { echo ''.($j+1).''; $spacer=False; } elseif(!$spacer) { echo '...'; $spacer=True; } } if($this->p+1<$this->num_pages) echo '  >   '; else echo '  >   '; echo '
'; } } ?> imagefiles); if(0==$c || $img<0 || $img>=$c) { die("404"); // ?? Fix this to generate a proper error page. } } /** Emits the set of switcher icons in the #path bar at the top of the screen. */ function switcher($doc) { global $img; if($img) $img_param='&img='.$img; $i='%1$s'; echo "  \n"; if($doc=='t') $a='%s'; else $a='%s'; printf($a,sprintf( $i, 'album', get_file(tsize_name().'.gif') )); if($doc=='c') $a='%s'; else $a='%s'; printf($a,sprintf( $i, 'carousel', get_file('browse.gif') )); if($doc=='s') $a='%s'; else $a='%s'; printf($a,sprintf( $i, 'single', get_file('single.gif') )); } /** Emits a small status box, that allows the admin to log in and out, and * displays the Easy Ajax Album logo. */ function status() { global $is_admin,$options; ?>
powered by Easy Ajax Album | comments)): ?> comments | prefs | logout login
'."\n"; } /** Loads a stylesheet called theme.css, if it exists. */ function load_theme() { global $dirs,$options; if($options->theme=='DEFAULT') return; $theme=$dirs->top_album().'/themes/'.$options->theme.'.css'; if(file_exists($theme)) echo ''."\n"; } ?> '; $im=$imagefiles[$img]; ?> resized($isize); $ir->ehtml() ?> href="fname) ?>"> caption() ?>]]> short_caption() ?>]]> 0) echo ''.$admin_message_text.''; $comments=$im->comments(); if($comments) foreach($comments as $c): ?> text ?> author ?> date_format,$c->time) ?>
get_dir( $dirs->top_album().'/themes' ); foreach(array('black','neutral') as $t) get_file('themes/'.$t.'.css'); ?>
seconds
comments) ?>" />
exif) ?>" />
reload) ?>" />
Easy Ajax Album Administration
path_links() ?>
passwd)): div_setpasswd(); elseif($is_admin): div_prefs(); div_setpasswd(false); else: div_login(); endif; status(); ?>
name); $album=basename(getcwd()); $prev=normalize($img+$len-1,$len); $next=normalize($img+$len+1,$len); $on_tmpl='class="on" href="?d=s&img=%s&isize=%s"'; $off='class="off" href="#"'; if($isize>0) $smaller=sprintf($on_tmpl,$img,$isize-1); else $smaller=$off; if($isize<5) $bigger=sprintf($on_tmpl,$img,$isize+1); else $bigger=$off; ?> <?php echo $title.' « '.hsc($dirs->path()) ?>
resized($isize); $ir->ehtml() ?> />
thumbnails « <?php echo hsc($dirs->path()) ?>
path_links() ?>
begin(); $i<$paged->end(); $i++) { $image=$imagefiles[$i]; $ir=$image->resized(0); $alt=nice($image->name); $title=$alt; $caption=$image->short_caption(); if($caption) $title.=': '.$caption; echo "\n"; echo ' '."\n" . ' '.$alt.'html()." />\n" . " "; } echo "\n"; $paged->index('?d=t&') ?>
begin(); $i<$paged->end(); $i++) { if(0==($i % 3)) $rows[]=''; $ir=$imagefiles[$i]->resized(1); $rows[count($rows)-1].= '\n "; } echo implode("\n ",$rows); ?>

'.nice($imagefiles[$i]->name).'

' .'html().' />' .$imagefiles[$i]->short_caption(100,'

%s

') .$imagefiles[$i]->numcomments('

%s

')."
index('?d=t&') ?>

edit

date_format,$im->date_time()); $nc=$im->numcomments('%s'); ?> containing the caption for this image, if it has one. * The administrator also sees the controls to add of edit the caption. */ function para_caption(&$imagefile,$img) { global $is_admin; $caption=$imagefile->caption(); if($is_admin): ?>

',$caption) ?>

comments(); echo '
'."\n"; if($comments) { echo '

Comments

'."\n"; foreach($comments as $c) { global $options; echo '
'."\n"; echo '

'.$c->text."

\n"; echo '

posted by '.$c->author .' on '.date($options->date_format,$c->time); if($is_admin) echo ' delete'; echo '

'."\n"; } } else { echo ''."\n"; } // Record the time at which the form was generated. Only permit // comments to be submitted within a short time, in order to stop spam. $timestamp=time(); $checksum=md5($options->secret.strval($timestamp)); ?>

Leave a Comment

\n"; } function doc_carousel(&$imagefiles) { global $dirs,$img,$options; $len=count($imagefiles); $pos=intval($_POST['pos']); $carousel_size=min($len,6); // Number of images in the carousel. if(array_key_exists('pos',$_POST)) { // Scroll the carousel around with the image. if($pos==$img) $pos=$img-1; else if(normalize($pos+$carousel_size-1,$len) == $img) $pos=$img+2-$carousel_size; } else { // Centre the image in the carousel... $pos=$img-intval(($carousel_size-0.5)/2); // ...but start at the beginning, end at the end. if($pos+$carousel_size-1 >= $len) $pos=$len-$carousel_size; else if($pos < 0) $pos=0; } $pos=normalize($pos,$len); $title=nice($imagefiles[$img]->name); $album=$dirs->current_album_title(); ?> <?php echo $title.' « '.hsc($dirs->path()) ?> dirname=$dirname; list($d,$i)=$dirs->read_contents($dirname); $this->directories=$d; $this->imagefiles=$i; } /** Utility function. Gets the first image from a directory. * Recurses if necessary. Returns TRUE if $out_imagefiles is full. */ function get_sample_imagefiles($max,&$out_samples) { // Find images. if(count($this->imagefiles)>0) { $this->imagefiles=array_values($this->imagefiles); for($i=0; $iimagefiles); $i++) { $out_samples[] = $this->imagefiles[$i]; if(count($out_samples) >= $max) return TRUE; } } // ...else recursively call into subdirs. if(count($this->directories)>0) { sort($this->directories); foreach($this->directories as $d) { $subalbum=new Album($this->dirname.'/'.$d); if( $subalbum->get_sample_imagefiles($max,$out_samples) ) return TRUE; } } return FALSE; } /** Get the album thumbnail name. Make a new thumbnail if necessary. */ function thumb() { global $dirs; $thumb_fname=$dirs->album($this->dirname).'/thumb.jpg'; // If the thumbnail is up to date, just return it. if(!file_exists($thumb_fname)) { // Generate the thumbnail. $samples=array(); $this->get_sample_imagefiles(3,$samples); $sq=array(); for($i=0; $i<3; $i++) { if($i < count($samples)) { $ir=$samples[$i]->square(); $sq[]=imagecreatefromjpeg( $ir->src ); } else { $new=imagecreatetruecolor(75,75); $col=imagecolorallocate($new,240,240,240); // grey imagefill($new,0,0,$col); $sq[]=$new; } } $thumb=imagecreatetruecolor(113,75); $white=imagecolorallocate($thumb,255,255,255); imagefill($thumb,0,0,$white); // dst src dst-xy-src dst-size-src imagecopyresampled($thumb, $sq[0], 0,0, 0,0, 75,75, 75,75 ); imagecopyresampled($thumb, $sq[1], 76,0, 0,0, 37,37, 75,75 ); imagecopyresampled($thumb, $sq[2], 76,38, 0,0, 37,37, 75,75 ); // Save it. imagejpeg($thumb,$thumb_fname,85); imagedestroy($thumb); foreach($sq as $image) imagedestroy($image); chmod($thumb_fname,0666); } return $thumb_fname; } } // end class Album function table_dir_thumb($dir,$html,$is_here=false) { $album=new Album($dir); ?>
carousel imagefiles )) echo '

'.count($album->imagefiles ).' images

'; if(!$is_here && count($album->directories)) echo '

'.count($album->directories).' albums

'; ?>
album
<?php echo hsc($dirs->path()) ?>
path_links(FALSE) ?>
imagefiles)): ?>
begin(); $i<$paged->end(); $i++) table_dir_thumb($directories[$i],'

'.nice($directories[$i]).'

'); ?>
index('?') ?>
id.'">'; $loff=''; } else { $lon=''; $loff=''; } ?>

text.$loff ?>

posted by author ?> on date_format,$c->time).$loff ?>

÷ Latest Comments « <?php echo hsc($dirs->path()) ?>
path_links(TRUE) ?>
begin(); $i<$paged->end(); $i++) { $cmt=new Comment($comments[$i]); tr_comment($cmt); } if($is_admin) echo ''."\n"; } else { echo ''; } ?>
No comments
index('?d=l&') ?>
passwd) && !$is_admin) admin_message('You must log-in to set the password.'); elseif(strlen($p1) < 8) admin_message('Your password must be at least eight characters long.'); elseif($p1!=$p2) admin_message('You must type the same password twice.'); else { $options->passwd=md5($p1.$options->secret); $options->save(); setcookie('exa_passwd',$options->passwd,0,'/'); $is_admin=true; } } elseif(intval($options->comments) && 'addcomment'==$do) { // Dummy field 'url' must be left blank. Spam-bots will often complete all // fields. Also, insist that comments are made after a 4s wait, but // before 1hr has passed. // If a comment fails these tests, then it is silently ignored. // ?? REALLY SHOULD GIVE THE USER A SECOND CHANCE. global $options; assert_keys(array('cmt_author','cmt_txt','cmt_ts','cmt_chk','url'),$_POST); $author =htmlentities(stripslashes( $_POST['cmt_author'] )); $comment =htmlentities(stripslashes( $_POST['cmt_txt'] )); $dummy =htmlentities(stripslashes( $_POST['url'] )); $timestamp=intval($_POST['cmt_ts']); $checksum =htmlentities(stripslashes( $_POST['cmt_chk'] )); $age=( time() - $timestamp ); if( $age < $options->comment_wait_min ) admin_message('Failed to save comment. You must wait at least '. $options->comment_wait_min.' seconds.'); elseif( $age > $options->comment_wait_max ) admin_message('Failed to save comment, because you waited too long. ' . 'Refresh the page and try again.'); elseif( strlen($author)<=0 ) admin_message('You must enter your name.'); elseif( strlen($comment)<=0 ) admin_message('You must enter a comment.'); elseif( strlen($dummy)!=0 ) admin_message('You must leave the dummy field empty.'); elseif( $checksum!=md5($options->secret.strval($timestamp)) ) admin_message('Bad checksum.'); else $dirs->imagefiles[$img]->addcomment($author,$comment); } elseif(!$is_admin) { die('Not logged in.'); } // MUST BE LOGGED-IN TO GET PAST THIS POINT if('setprefs'==$do) { $options->comments=intval($_POST['comments']); $options->exif=intval($_POST['exif']); $options->reload=intval($_POST['reload']); $options->top_name=safe_input($_POST['top_name']); $options->home_name=safe_input($_POST['home_name']); $options->home_url=safe_input($_POST['home_url']); $options->slideshow_delay=max(0,1000*intval($_POST['slideshow_delay'])); $options->theme=safe_input($_POST['theme']); $options->save(); admin_message('Preferences saved.'); } elseif('edittitle'==$do) { assert_keys(array('title_txt'),$_POST); $new_title=htmlentities(stripslashes( $_POST['title_txt'] )); if( !$dirs->imagefiles[$img]->set_title($new_title) ) admin_message('Failed to rename '.$dirs->imagefiles[$img]->fname); } elseif('editcaption'==$do) { assert_keys(array('cap_txt'),$_POST); $caption=htmlentities(stripslashes( $_POST['cap_txt'] )); $dirs->imagefiles[$img]->setcaption($caption); admin_message('Caption saved.'); } elseif('delcomment'==$do) { assert_keys(array('cid'),$_POST); $cid=safe_input($_POST['cid']); $dirs->imagefiles[$img]->delcomment($cid); admin_message('Comment deleted.'); } elseif('delcomments'==$do) { assert_keys(array('cpaths'),$_POST); if(is_array($_POST['cpaths'])) foreach($_POST['cpaths'] as $cpath) if(is_file($cpath) && 'comments'==basename(dirname($cpath))) unlink($cpath); admin_message('Comments deleted.'); } } /** Force download of the current image * (by setting MIME type to application/octet-stream). */ function download() { assert_img(); global $dirs,$img; $fname=$dirs->imagefiles[$img]->fname; // header('HTTP/1.1 200 OK'); header('Date: ' . date("D M j G:i:s T Y")); header('Last-Modified: ' . date("D M j G:i:s T Y")); header("Content-Type: application/octet-stream"); header("Content-Length: " . (string)(filesize($fname)) ); header("Content-Transfer-Encoding: Binary"); header('Content-Disposition: attachment; filename="'.basename($fname).'"' ); readfile($fname); exit(0); } /** Empty document. Used for void-return XMLHttpRequest calls. */ function doc_empty() { global $admin_message_text; ?> imagefiles)) { for($img=count($dirs->imagefiles)-1; $img>0; $img--) if($_GET['f']==$dirs->imagefiles[$img]->info['basename']) break; } else { $img=intval($_GET['img']); } $isize=get_set_cookie('isize',0,5,3); $tsize=get_set_cookie('tsize'); if(array_key_exists('do',$_POST)) process_form(); elseif(array_key_exists('dl',$_GET)) download(); $doc=$_GET['d']; if(0==strlen($options->passwd) || 'a'==$doc) doc_admin(); elseif('e'==$doc) doc_empty(); elseif('l'==$doc) doc_latest(); elseif('p'==$doc) doc_code(); elseif(0==count($dirs->imagefiles)) // No images doc_dir($dirs->directories); elseif('x'==$doc) doc_xml($dirs->imagefiles); elseif('s'==$doc) doc_single($dirs->imagefiles); elseif('c'==$doc) doc_carousel($dirs->imagefiles); elseif('t'==$doc || 0==count($dirs->directories)) doc_thumb($dirs->imagefiles); else doc_dir($dirs->directories); } main(); ?> top_album().'/'.$name; if($options->reload || !file_exists($result)) { $handle=fopen($result,'wb'); if('rew.png'==$name) fwrite($handle,base64_decode(<<2) { if(e.checked) tr.cells[1].style.backgroundColor='red'; else tr.cells[1].style.backgroundColor='transparent'; } } function do_mark_to(id) { var to=true; var inputs=document.getElementsByTagName('input'); for(var i=0; i. by Andrew Gregory http://www.scss.com.au/family/andrew/webdesign/xmlhttprequest/ This work is licensed under the Creative Commons Attribution License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/2.5/ or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. Attribution: Leave my name and web address in this script intact. Not Supported in Opera ---------------------- * user/password authentication * responseXML data member Not Fully Supported in Opera ---------------------------- * async requests * abort() * getAllResponseHeaders(), getAllResponseHeader(header) */ // IE support if (window.ActiveXObject && !window.XMLHttpRequest) { window.XMLHttpRequest = function() { var msxmls = new Array( 'Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0', 'Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP'); for (var i = 0; i < msxmls.length; i++) { try { return new ActiveXObject(msxmls[i]); } catch (e) { } } return null; }; } // Gecko support /* ;-) */ // Opera support if (window.opera && !window.XMLHttpRequest) { window.XMLHttpRequest = function() { this.readyState = 0; // 0=uninitialized,1=loading,2=loaded,3=interactive,4=complete this.status = 0; // HTTP status codes this.statusText = ''; this._headers = []; this._aborted = false; this._async = true; this._defaultCharset = 'ISO-8859-1'; this._getCharset = function() { var charset = _defaultCharset; var contentType = this.getResponseHeader('Content-type').toUpperCase(); val = contentType.indexOf('CHARSET='); if (val != -1) { charset = contentType.substring(val); } val = charset.indexOf(';'); if (val != -1) { charset = charset.substring(0, val); } val = charset.indexOf(','); if (val != -1) { charset = charset.substring(0, val); } return charset; }; this.abort = function() { this._aborted = true; }; this.getAllResponseHeaders = function() { return this.getAllResponseHeader('*'); }; this.getAllResponseHeader = function(header) { var ret = ''; for (var i = 0; i < this._headers.length; i++) { if (header == '*' || this._headers[i].h == header) { ret += this._headers[i].h + ': ' + this._headers[i].v + '\\n'; } } return ret; }; this.getResponseHeader = function(header) { var ret = getAllResponseHeader(header); var i = ret.indexOf('\\n'); if (i != -1) { ret = ret.substring(0, i); } return ret; }; this.setRequestHeader = function(header, value) { this._headers[this._headers.length] = {h:header, v:value}; }; this.open = function(method, url, async, user, password) { this.method = method; this.url = url; this._async = true; this._aborted = false; this._headers = []; if (arguments.length >= 3) { this._async = async; } if (arguments.length > 3) { opera.postError('XMLHttpRequest.open() - user/password not supported'); } this.readyState = 1; if (this.onreadystatechange) { this.onreadystatechange(); } }; this.send = function(data) { if (!navigator.javaEnabled()) { alert("XMLHttpRequest.send() - Java must be installed and enabled."); return; } if (this._async) { setTimeout(this._sendasync, 0, this, data); // this is not really asynchronous and won't execute until the current // execution context ends } else { this._sendsync(data); } } this._sendasync = function(req, data) { if (!req._aborted) { req._sendsync(data); } }; this._sendsync = function(data) { this.readyState = 2; if (this.onreadystatechange) { this.onreadystatechange(); } // open connection var url = new java.net.URL(new java.net.URL(window.location.href), this.url); var conn = url.openConnection(); for (var i = 0; i < this._headers.length; i++) { conn.setRequestProperty(this._headers[i].h, this._headers[i].v); } this._headers = []; if (this.method == 'POST') { // POST data conn.setDoOutput(true); var wr = new java.io.OutputStreamWriter(conn.getOutputStream(), this._getCharset()); wr.write(data); wr.flush(); wr.close(); } // read response headers // NOTE: the getHeaderField() methods always return nulls for me :( var gotContentEncoding = false; var gotContentLength = false; var gotContentType = false; var gotDate = false; var gotExpiration = false; var gotLastModified = false; for (var i = 0; ; i++) { var hdrName = conn.getHeaderFieldKey(i); var hdrValue = conn.getHeaderField(i); if (hdrName == null && hdrValue == null) { break; } if (hdrName != null) { this._headers[this._headers.length] = {h:hdrName, v:hdrValue}; switch (hdrName.toLowerCase()) { case 'content-encoding': gotContentEncoding = true; break; case 'content-length' : gotContentLength = true; break; case 'content-type' : gotContentType = true; break; case 'date' : gotDate = true; break; case 'expires' : gotExpiration = true; break; case 'last-modified' : gotLastModified = true; break; } } } // try to fill in any missing header information var val; val = conn.getContentEncoding(); if (val != null && !gotContentEncoding) this._headers[this._headers.length] = {h:'Content-encoding', v:val}; val = conn.getContentLength(); if (val != -1 && !gotContentLength) this._headers[this._headers.length] = {h:'Content-length', v:val}; val = conn.getContentType(); if (val != null && !gotContentType) this._headers[this._headers.length] = {h:'Content-type', v:val}; val = conn.getDate(); if (val != 0 && !gotDate) this._headers[this._headers.length] = {h:'Date', v:(new Date(val)).toUTCString()}; val = conn.getExpiration(); if (val != 0 && !gotExpiration) this._headers[this._headers.length] = {h:'Expires', v:(new Date(val)).toUTCString()}; val = conn.getLastModified(); if (val != 0 && !gotLastModified) this._headers[this._headers.length] = {h:'Last-modified', v:(new Date(val)).toUTCString()}; // read response data var reqdata = ''; var stream = conn.getInputStream(); if (stream) { var reader = new java.io.BufferedReader(new java.io.InputStreamReader(stream, this._getCharset())); var line; while ((line = reader.readLine()) != null) { if (this.readyState == 2) { this.readyState = 3; if (this.onreadystatechange) { this.onreadystatechange(); } } reqdata += line + '\\n'; } reader.close(); this.status = 200; this.statusText = 'OK'; this.responseText = reqdata; this.readyState = 4; if (this.onreadystatechange) { this.onreadystatechange(); } if (this.onload) { this.onload(); } } else { // error this.status = 404; this.statusText = 'Not Found'; this.responseText = ''; this.readyState = 4; if (this.onreadystatechange) { this.onreadystatechange(); } if (this.onerror) { this.onerror(); } } }; }; } // ActiveXObject emulation if (!window.ActiveXObject && window.XMLHttpRequest) { window.ActiveXObject = function(type) { switch (type.toLowerCase()) { case 'microsoft.xmlhttp': case 'msxml2.xmlhttp': case 'msxml2.xmlhttp.3.0': case 'msxml2.xmlhttp.4.0': case 'msxml2.xmlhttp.5.0': return new XMLHttpRequest(); } return null; }; } EOFEOF ); //############################################################### elseif('util.js'==$name) fwrite($handle,<< is now set to false //===================================================================== function addEvent(obj, evType, fn) { if(obj.addEventListener){ obj.addEventListener(evType, fn, false); return true; } else if (obj.attachEvent){ var r = obj.attachEvent('on'+evType, fn); return r; } else { return false; } } /** Constants to aid readability of onclick handlers. */ var ONCLICK_OK =false; // Do NOT execute href. var ONCLICK_FAILED =true; // DO execute href. /** Shorthand for document.getElementById. Borrowed from Prototype.js */ function $(id){ return document.getElementById(id); } /** Returns parameter p if it's set, else the delfault value d. */ function dflt(p,d) { return p==null? d: p; } /** Move 'e' to precede 'before'. */ function move_before(e,before) { if(e && before) { e.parentNode.removeChild(e); before.parentNode.insertBefore(e,before); } } /** Helper class for generating POST parameters. * If the optional parameter 'form' is set, then the object is initialised from * the form's fields. */ function Params(form) { this.value=''; if(form!=null) for(var i=0; i0) this.value+='&'; this.value+=k+'='+encodeURI(v); } function getFirstChildByTagName(e,tagName) { if(e) { var arr=e.getElementsByTagName(tagName); if(arr.length) return arr[0]; } return false; } /** Gets all of the TEXT and CDATA from an element, concatenated into a * single string. */ function getInnerText(e,result) { result=dflt(result,''); for(var i=0; i=10) { e.className=className; } else { e.className='fade'+trans+' '+className; trans+=2; setTimeout(evt_fade_in,100); } } evt_fade_in(); } /** Finds Absolute Y position of an element. */ function findPosY(obj) { var curtop = 0; if(obj.offsetParent) while(1) { curtop += obj.offsetTop; if(!obj.offsetParent) break; obj = obj.offsetParent; } else if(obj.y) curtop += obj.y; return curtop; } /** Cross-browser compatible implementation of scrollIntoView. */ function scrollIntoView(e) { window.scrollTo(0,e.offsetHeight + findPosY(e)); } EOFEOF ); //############################################################### elseif('carousel.js'==$name) fwrite($handle,<<'); cap_ctrl.innerHTML=txt.length? 'edit': 'add caption'; // Now set the carousel caption. var para=$('cap_'+img); xc=xml.getElementsByTagName('brief'); para.innerHTML=getInnerText(xc[0]); } } if( ajax_form(cap_form,{oncomplete:update_caption}) ) { cap_sub.style.display='none'; cap_busy.style.display='inline'; return ONCLICK_OK; } else alert('Error submitting comment.'); } return ONCLICK_FAILED; } function do_show_comment_editor() { var cmt_ctrl=$('cmt_ctrl'); var cmt_form=$('cmt_form'); if(cmt_ctrl && cmt_form) { cmt_ctrl.parentNode.style.display='none'; cmt_form.style.display='block'; scrollIntoView(cmt_form); cmt_form.cmt_author.focus(); return ONCLICK_OK; } return ONCLICK_FAILED; } function update_num_comments(num) { var nc=$('nc_'+img); if(num==0) { if(nc) fade_out(nc); } else { if(!nc) { nc=document.createElement('span'); nc.id='nc_'+img; nc.className='num_comments'; var dtc=$('dtc_'+img); fade_in(nc); dtc.insertBefore(nc,dtc.firstChild); } nc.innerHTML=''+num+' comment'+(num>1? 's': ''); } } /** Construct a DIV from XML */ function div_comment(comment) { var div=document.createElement('div'); div.className='comment'; var txt =getInnerText( getFirstChildByTagName(comment,'text') ); var author=getInnerText( getFirstChildByTagName(comment,'author') ); var date =getInnerText( getFirstChildByTagName(comment,'date') ); div.innerHTML='

'+txt+'

posted by ' +author+' on '+date+'

'; return div; } /** Pop up an alert dialogue, if the album XML contains an element. */ function report_alert(album) { var a=getFirstChildByTagName(album,'alert'); if(a) { alert(getInnerText(a)); return true; } return false; } function do_submit_comment() { var cmt_head=$('cmt_head'); var cmt_ctrl=$('cmt_ctrl'); var cmt_form=$('cmt_form'); var cmt_busy=$('cmt_busy'); var cmt_sub =$('cmt_sub'); if(cmt_ctrl && cmt_form && cmt_busy && cmt_sub) { function update_comment(xml) { cmt_sub.style.display='inline'; cmt_busy.style.display='none'; if(report_alert(xml)) return; var cs=xml.getElementsByTagName('comment'); for(var i=cs.length-1; i>=0; i--) { var id=cs[i].getAttribute('id'); if($(id)) break; // We already have this comment, so give up. else if(i==0) fade_in(cmt_head,'block'); // Make the Comment header visible. var div=div_comment(cs[i]); div.setAttribute('id',id); fade_in(div); cmt_form.parentNode.insertBefore(div,cmt_form); } update_num_comments(cs.length); cmt_form.reset(); cmt_form.style.display='none'; cmt_ctrl.parentNode.style.display='block'; } if( ajax_form(cmt_form,{oncomplete:update_comment}) ) { cmt_sub.style.display='none'; cmt_busy.style.display='inline'; return ONCLICK_OK; } else alert('Error submitting comment.'); } return ONCLICK_FAILED; } function do_delete_comment(e) { // Find comment. while(e && e.parentNode) { e=e.parentNode; if(e.className=='comment') { var id=e.getAttribute('id'); if(id==null || id=='') break; ajax('?d=e&img='+img,{data:'do=delcomment&cid='+id}); fade_out(e); // If there are no more comments, then hide the header, too. var cdivs=$('comments').getElementsByTagName('div'); if(!(cdivs && cdivs.length>1)) fade_out($('cmt_head'),true); update_num_comments(Math.max(0,cdivs.length-1)); return ONCLICK_OK; } } return ONCLICK_FAILED; } EOFEOF ); //############################################################### elseif('slideshow.js'==$name) fwrite($handle,<<=0) { ss.next=new SlideShowNext(ss.curr+1,this.last,ss.delay); ss.next.load() } } // // SlideShow // function SlideShow(curr,len,isize,delay) { this.curr=curr; this.curr_id='img_'+curr+'_'+isize; this.len=len; this.next=false; // The desired image size (may not always be the size of the current image). this.isize=isize; this.delay=dflt(delay,4000); // constants this.min_isize=0; this.max_isize=5; } SlideShow.prototype.do_prev=function() { this.do_next(-1); } SlideShow.prototype.do_next=function(offset) { offset=dflt(offset,1); var next=new SlideShowNext(this.curr+offset); if(this.next) { if(this.next.last>=0) return; // Playing slide show - ignore. if(this.next.idx==next.idx) return; // already loading this one (for some reason). this.next.cancel() } this.next=next; this.next.load() } SlideShow.prototype.do_play=function() { var next=new SlideShowNext(this.curr+1,this.curr,this.delay); if(this.next) { if(this.next.idx==next.idx) return; // already loading this one this.next.cancel(); } this.next=next; this.next.load() // Set control to STOP document.getElementById('play').style.display='none'; document.getElementById('stop').style.display='inline'; document.getElementById('prev').className='off'; document.getElementById('next').className='off'; } SlideShow.prototype.do_stop=function() { if(this.next) this.next.cancel(); document.getElementById('busy').style.display='none'; document.getElementById('play').style.display='inline'; document.getElementById('stop').style.display='none'; document.getElementById('prev').className='on'; document.getElementById('next').className='on'; } SlideShow.prototype.do_bigger=function() { if(this.isize < this.max_isize) ss.resize(this.isize+1); } SlideShow.prototype.do_smaller=function() { if(this.isize > this.min_isize) ss.resize(this.isize-1); } /** Utility function called by do_bigger() & do_smaller(). */ SlideShow.prototype.resize=function(isize) { this.isize=isize; // Store isize to a cookie - this tells the server which size image to send. document.cookie=('isize='+this.isize+'; path=/'); // Load the new image. var last=-1; if(this.next) { last=this.next.last; this.next.cancel(); } this.next=new SlideShowNext(this.curr,last); this.next.load() // toggle size icons var smaller=document.getElementById('smaller'); if(this.isize > this.min_isize) smaller.className='on'; else smaller.className='off'; var bigger=document.getElementById('bigger'); if(this.isize < this.max_isize) bigger.className='on'; else bigger.className='off'; } EOFEOF ); //############################################################### elseif('transparent.gif'==$name) fwrite($handle,base64_decode(<<