# HG changeset patch # User dan@scribus.fuhry.local.fuhry.local # Date 1181765237 14400 # Node ID fe660c52c48fa5d2cda50a47c0761627e1dcca10 # Parent 902822492a682e515f25e35a7269d17f8d21a5a9 Adding /includes diff -r 902822492a68 -r fe660c52c48f includes/captcha.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/captcha.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,747 @@ +compat = false; + if(!extension_loaded("gd")){ + $this->compat = true; + } + if(!function_exists("gd_info") || !function_exists('imagettftext')){ + $this->compat = true; + } + $this->code = $code; + + $hex = Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); + $latticecolor = '#'; + for($i=0;$i<6;$i++) $latticecolor .= $hex[mt_rand(0, 15)]; + + $this->captcha_config = array ( + 'width' => '350', + 'height' => '90', + 'background_color' => '#E5ECF9', + 'jpeg' => '0', + 'jpeg_quality' => '95', + 'pre_letters' => '1', + 'pre_letters_great' => '0', + 'font' => '1', + 'chess' => '2', + 'ellipses' => '2', + 'arcs' => '2', + 'lines' => '2', + 'image' => '0', + 'gammacorrect' => '0.8', + 'foreground_lattice_x' => (string)mt_rand(25, 30), + 'foreground_lattice_y' => (string)mt_rand(25, 30), + 'lattice_color' => $latticecolor, + ); + } + function captcha($code) + { + $this->__construct($code); + } + function dss_rand() + { + global $db; + + $val = microtime() . mt_rand(); + $val = md5($val . 'a'); + return substr($val, 4, 16); + } + + function make_image() + { + $code=$this->code; + if($this->compat) + { + // We can we will generate a single filtered png + // Thanks to DavidMJ for emulating zlib within the code :) + $_png = $this->define_filtered_pngs(); + + $total_width = 320; + $total_height = 50; + $img_height = 40; + $img_width = 0; + $l = 0; + + list($usec, $sec) = explode(' ', microtime()); + mt_srand($sec * $usec); + + $char_widths = array(); + for ($i = 0; $i < strlen($code); $i++) + { + $char = $code{$i}; + + $width = mt_rand(0, 4); + $char_widths[] = $width; + $img_width += $_png[$char]['width'] - $width; + } + + $offset_x = mt_rand(0, $total_width - $img_width); + $offset_y = mt_rand(0, $total_height - $img_height); + + $image = ''; + $hold_chars = array(); + for ($i = 0; $i < $total_height; $i++) + { + $image .= chr(0); + + if ($i > $offset_y && $i < $offset_y + $img_height) + { + $j = 0; + + for ($k = 0; $k < $offset_x; $k++) + { + $image .= chr(mt_rand(140, 255)); + } + + for ($k = 0; $k < strlen($code); $k++) + { + $char = $code{$k}; + + if (empty($hold_chars[$char])) + { + $hold_chars[$char] = explode("\n", chunk_split(base64_decode($_png[$char]['data']), $_png[$char]['width'] + 1, "\n")); + } + $image .= $this->randomise(substr($hold_chars[$char][$l], 1), $char_widths[$j]); + $j++; + } + + for ($k = $offset_x + $img_width; $k < $total_width; $k++) + { + $image .= chr(mt_rand(140, 255)); + } + + $l++; + } + else + { + for ($k = 0; $k < $total_width; $k++) + { + $image .= chr(mt_rand(140, 255)); + } + } + + } + unset($hold); + + $image = $this->create_png($image, $total_width, $total_height); + + // Output image + header('Content-Type: image/png'); + header('Cache-control: no-cache, no-store'); + echo $image; + + unset($image); + unset($_png); + } elseif(defined('ENANO_CAPTCHA_BIGNFAT')) { + // Prefs + $total_width = $this->captcha_config['width']; + $total_height = $this->captcha_config['height']; + + $hex_bg_color = $this->get_rgb($this->captcha_config['background_color']); + $bg_color = array(); + $bg_color = explode(",", $hex_bg_color); + + $jpeg = $this->captcha_config['jpeg']; + $img_quality = $this->captcha_config['jpeg_quality']; + // Max quality is 95 + + $pre_letters = $this->captcha_config['pre_letters']; + $pre_letter_great = $this->captcha_config['pre_letters_great']; + $rnd_font = $this->captcha_config['font']; + $chess = $this->captcha_config['chess']; + $ellipses = $this->captcha_config['ellipses']; + $arcs = $this->captcha_config['arcs']; + $lines = $this->captcha_config['lines']; + $image = $this->captcha_config['image']; + + $gammacorrect = $this->captcha_config['gammacorrect']; + + $foreground_lattice_y = $this->captcha_config['foreground_lattice_y']; + $foreground_lattice_x = $this->captcha_config['foreground_lattice_x']; + $hex_lattice_color = $this->get_rgb($this->captcha_config['lattice_color']); + $rgb_lattice_color = array(); + $rgb_lattice_color = explode(",", $hex_lattice_color); + + $font_debug = false; + + // Fonts and images init + if ($image) + { + $bg_imgs = array(); + if ($img_dir = opendir(ENANO_ROOT.'/includes/captcha/pics/')) + { + while (true == ($file = @readdir($img_dir))) + { + if ((substr(strtolower($file), -3) == 'jpg') || (substr(strtolower($file), -3) == 'gif')) + { + $bg_imgs[] = $file; + } + } + closedir($img_dir); + } + // Grab a random Background Image or set FALSE if none was found + $bg_img = ( count($bg_imgs) ) ? rand(0, (count($bg_imgs)-1)) : false; + } + + $fonts = array(); + if ($fonts_dir = opendir(ENANO_ROOT.'/includes/captcha/fonts/')) + { + while (true == ($file = @readdir($fonts_dir))) + { + if ((substr(strtolower($file), strlen($file)-3, strlen($file)) == 'ttf')) + { + $fonts[] = $file; + } + } + closedir($fonts_dir); + } else { + die('Error reading directory: '.ENANO_ROOT.'/includes/captcha/fonts/'); + } + $font = rand(0, (count($fonts)-1)); + + // Generate + $image = ($this->gdVersion() >= 2) ? imagecreatetruecolor($total_width, $total_height) : imagecreate($total_width, $total_height); + $background_color = imagecolorallocate($image, $bg_color[0], $bg_color[1], $bg_color[2]); + imagefill($image, 0, 0, $background_color); + + // Generate backgrund + if ($chess == '1' || $chess == '2' && rand(0,1)) + { + // Draw rectangles + for($i = 0; $i <= 8; $i++) + { + $rectanglecolor = imagecolorallocate($image, rand(100,200),rand(100,200),rand(100,200)); + imagefilledrectangle($image, 0, 0, round($total_width-($total_width/8*$i)), round($total_height), $rectanglecolor); + $rectanglecolor = imagecolorallocate($image, rand(100,200),rand(100,200),rand(100,200)); + imagefilledrectangle($image, 0, 0, round($total_width-($total_width/8*$i)), round($total_height/2), $rectanglecolor); + } + } + if ($ellipses == '1' || $ellipses == '2' && rand(0,1)) + { + // Draw random ellipses + for ($i = 1; $i <= 60; $i++) + { + $ellipsecolor = imagecolorallocate($image, rand(100,250),rand(100,250),rand(100,250)); + imagefilledellipse($image, round(rand(0, $total_width)), round(rand(0, $total_height)), round(rand(0, $total_width/8)), round(rand(0, $total_height/4)), $ellipsecolor); + } + } + if ($arcs == '1' || $arcs == '2' && rand(0,1)) + { + // Draw random partial ellipses + for ($i = 0; $i <= 30; $i++) + { + $linecolor = imagecolorallocate($image, rand(120,255),rand(120,255),rand(120,255)); + $cx = round(rand(1, $total_width)); + $cy = round(rand(1, $total_height)); + $int_w = round(rand(1, $total_width/2)); + $int_h = round(rand(1, $total_height)); + imagearc($image, $cx, $cy, $int_w, $int_h, round(rand(0, 190)), round(rand(191, 360)), $linecolor); + imagearc($image, $cx-1, $cy-1, $int_w, $int_h, round(rand(0, 190)), round(rand(191, 360)), $linecolor); + } + } + if ($lines == '1' || $lines == '2' && rand(0,1)) + { + // Draw random lines + for ($i = 0; $i <= 50; $i++) + { + $linecolor = imagecolorallocate($image, rand(120,255),rand(120,255),rand(120,255)); + imageline($image, round(rand(1, $total_width*3)), round(rand(1, $total_height*5)), round(rand(1, $total_width/2)), round(rand(1, $total_height*2)), $linecolor); + } + } + + $text_color_array = array('255,51,0', '51,77,255', '204,51,102', '0,153,0', '255,166,2', '255,0,255', '255,0,0', '0,255,0', '0,0,255', '0,255,255'); + shuffle($text_color_array); + $pre_text_color_array = array('255,71,20', '71,20,224', '224,71,122', '20,173,20', '255,186,22', '25,25,25'); + shuffle($pre_text_color_array); + $white = imagecolorallocate($image, 255, 255, 255); + $gray = imagecolorallocate($image, 100, 100, 100); + $black = imagecolorallocate($image, 0, 0, 0); + $lattice_color = imagecolorallocate($image, $rgb_lattice_color[0], $rgb_lattice_color[1], $rgb_lattice_color[2]); + + $x_char_position = (round(($total_width - 12) / strlen($code)) + mt_rand(-3, 5)); + + for ($i = 0; $i < strlen($code); $i++) + { + mt_srand((double)microtime()*1000000); + + $char = $code{$i}; + $size = mt_rand(floor($total_height / 3.5), ceil($total_height / 2.8)); + $font = ($rnd_font) ? rand(0, (count($fonts)-1)) : $font; + $angle = mt_rand(-30, 30); + + $char_pos = array(); + $char_pos = imagettfbbox($size, $angle, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char); + $letter_width = abs($char_pos[0]) + abs($char_pos[4]); + $letter_height = abs($char_pos[1]) + abs($char_pos[5]); + + $x_pos = ($x_char_position / 4) + ($i * $x_char_position); + ($i == strlen($code)-1 && $x_pos >= ($total_width - ($letter_width + 5))) ? $x_pos = ($total_width - ($letter_width + 5)) : ''; + $y_pos = mt_rand(($size * 1.4 ), $total_height - ($size * 0.4)); + + // Pre letters + $size = ($pre_letter_great) ? $size + (2 * $pre_letters) : $size - (2 * $pre_letters); + for ($count = 1; $count <= $pre_letters; $count++) + { + $pre_angle = $angle + mt_rand(-20, 20); + + $text_color = $pre_text_color_array[mt_rand(0,count($pre_text_color_array)-1)]; + $text_color = explode(",", $text_color); + $textcolor = imagecolorallocate($image, $text_color[0], $text_color[1], $text_color[2]); + + imagettftext($image, $size, $pre_angle, $x_pos, $y_pos-2, $white, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char); + imagettftext($image, $size, $pre_angle, $x_pos+2, $y_pos, $black, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char); + imagettftext($image, $size, $pre_angle, $x_pos+1, $y_pos-1, $textcolor, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char); + + $size = ($pre_letter_great) ? $size - 2 : $size + 2; + } + + // Final letters + $text_color = $text_color_array[mt_rand(0,count($text_color_array)-1)]; + $text_color = explode(",", $text_color); + $textcolor = imagecolorallocate($image, $text_color[0], $text_color[1], $text_color[2]); + + imagettftext($image, $size, $angle, $x_pos, $y_pos-2, $white, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char); + imagettftext($image, $size, $angle, $x_pos+2, $y_pos, $black, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char); + imagettftext($image, $size, $angle, $x_pos+1, $y_pos-1, $textcolor, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char); + } + + + ($gammacorrect) ? imagegammacorrect($image, 1.0, $gammacorrect) : ''; + + // Generate a white lattice in foreground + if ($foreground_lattice_y) + { + // x lines + $ih = round($total_height / $foreground_lattice_y); + for ($i = 0; $i <= $ih; $i++) + { + imageline($image, 0, $i*$foreground_lattice_y, $total_width, $i*$foreground_lattice_y, $lattice_color); + } + } + if ($foreground_lattice_x) + { + // y lines + $iw = round($total_width / $foreground_lattice_x); + for ($i = 0; $i <= $iw; $i++) + { + imageline($image, $i*$foreground_lattice_x, 0, $i*$foreground_lattice_x, $total_height, $lattice_color); + } + } + + // Font debug + if ($font_debug && !$rnd_font) + { + imagestring($image, 5, 2, 0, $fonts[$font], $white); + imagestring($image, 5, 5, 0, $fonts[$font], $white); + imagestring($image, 5, 4, 2, $fonts[$font], $gray); + imagestring($image, 5, 3, 1, $fonts[$font], $black); + } + + // Display + header("Last-Modified: " . gmdate("D, d M Y H:i:s") ." GMT"); + header("Pragma: no-cache"); + header("Cache-Control: no-store, no-cache, max-age=0, must-revalidate"); + (!$jpeg) ? header("Content-Type: image/png") : header("Content-Type: image/jpeg"); + + (!$jpeg) ? imagepng($image) : imagejpeg($image, '', $img_quality); + imagedestroy($image); + } else { + /** + * The next part is orginnaly written by ted from mastercode.nl and modified for use in Enano. + **/ + header("content-type:image/png"); + header('Cache-control: no-cache, no-store'); + $breedte = 320; + $hoogte = 60; + $img = imagecreatetruecolor($breedte,$hoogte); + $achtergrond = imagecolorallocate($img, $this->color("bg"), $this->color("bg"), $this->color("bg")); + + imagefilledrectangle($img, 0, 0, $breedte-1, $hoogte-1, $achtergrond); + for($g = 0;$g < 30; $g++) + { + $t = $this->dss_rand(); + $t = $t[0]; + + $ypos = rand(0,$hoogte); + $xpos = rand(0,$breedte); + + $kleur = imagecolorallocate($img, $this->color("bgtekst"), $this->color("bgtekst"), $this->color("bgtekst")); + + imagettftext($img, $this->size(), $this->move(), $xpos, $ypos, $kleur, $this->font(), $t); + } + $stukje = $breedte / (strlen($code) + 3); + + for($j = 0;$j < strlen($code); $j++) + { + + + $tek = $code[$j]; + $ypos = rand(33,43); + $xpos = $stukje * ($j+1); + + $kleur2 = imagecolorallocate($img, $this->color("tekst"), $this->color("tekst"), $this->color("tekst")); + + imagettftext($img, $this->size(), $this->move(), $xpos, $ypos, $kleur2, $this->font() , $tek); + } + + imagepng($img); + } + } + /** + * Some functions :) + * Also orginally written by mastercode.nl + **/ + /** + * Function to create a random color + * @param $type string Mode for the color + * @return int + **/ + function color($type) + { + switch($type) + { + case "bg": + $kleur = rand(224,255); + break; + case "tekst": + $kleur = rand(0,127); + break; + case "bgtekst": + $kleur = rand(200,224); + break; + default: + $kleur = rand(0,255); + break; + } + return $kleur; + } + /** + * Function to ranom the size + * @return int + **/ + function size() + { + $grootte = rand(14,30); + return $grootte; + } + /** + * Function to random the posistion + * @return int + **/ + function move() + { + $draai = rand(-25,25); + return $draai; + } + /** + * Function to return a ttf file from fonts map + * @return string + **/ + function font() + { + $f = @opendir(ENANO_ROOT . '/includes/captcha/fonts/'); + if(!$f) die('Can\'t open '.ENANO_ROOT.'/includes/captcha/fonts/ for reading'); + $ar = array(); + while(($file = @readdir($f)) !== false) + { + if(!in_array($file, array('.','..')) && strstr($file, '.ttf')) + { + $ar[] = $file; + } + } + if(count($ar)) + { + shuffle($ar); + $i = rand(0,(count($ar) - 1)); + return ENANO_ROOT . '/includes/captcha/fonts/' . $ar[$i]; + } + } + + // This is designed to randomise the pixels of the image data within + // certain limits so as to keep it readable. It also varies the image + // width a little + function randomise($scanline, $width) + { + $new_line = ''; + $start = floor($width/2); + $end = strlen($scanline) - ceil($width/2); + + for ($i = $start; $i < $end; $i++) + { + $pixel = ord($scanline{$i}); + + if ($pixel < 190) + { + $new_line .= chr(mt_rand(0, 205)); + } + else if ($pixel > 190) + { + $new_line .= chr(mt_rand(145, 255)); + } + else + { + $new_line .= $scanline{$i}; + } + } + + return $new_line; + } + + // This creates a chunk of the given type, with the given data + // of the given length adding the relevant crc + function png_chunk($length, $type, $data) + { + $raw = $type; + $raw .= $data; + $crc = crc32($raw); + $raw .= pack('C4', $crc >> 24, $crc >> 16, $crc >> 8, $crc); + + return pack('C4', $length >> 24, $length >> 16, $length >> 8, $length) . $raw; + } + + // Creates greyscale 8bit png - The PNG spec can be found at + // http://www.libpng.org/pub/png/spec/PNG-Contents.html we use + // png because it's a fully recognised open standard and supported + // by practically all modern browsers and OSs + function create_png($raw_image, $width, $height) + + { + // SIG + $image = pack('C8', 137, 80, 78, 71, 13, 10, 26, 10); + // IHDR + $raw = pack('C4', $width >> 24, $width >> 16, $width >> 8, $width); + $raw .= pack('C4', $height >> 24, $height >> 16, $height >> 8, $height); + $raw .= pack('C5', 8, 0, 0, 0, 0); + $image .= $this->png_chunk(13, 'IHDR', $raw); + + if (@extension_loaded('zlib')) + { + $raw_image = gzcompress($raw_image); + $length = strlen($raw_image); + } + else + { + // The total length of this image, uncompressed, is just a calculation of pixels + $length = ($width + 1) * $height; + + // Adler-32 hash generation + // Optimized Adler-32 loop ported from the GNU Classpath project + $temp_length = $length; + $s1 = 1; + $s2 = $index = 0; + + while ($temp_length > 0) + { + // We can defer the modulo operation: + // s1 maximally grows from 65521 to 65521 + 255 * 3800 + // s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31 + $substract_value = ($temp_length < 3800) ? $temp_length : 3800; + $temp_length -= $substract_value; + + while (--$substract_value >= 0) + { + $s1 += ord($raw_image[$index]); + $s2 += $s1; + + $index++; + } + + $s1 %= 65521; + $s2 %= 65521; + } + $adler_hash = pack('N', ($s2 << 16) | $s1); + + // This is the same thing as gzcompress($raw_image, 0) but does not need zlib + $raw_image = pack('C3v2', 0x78, 0x01, 0x01, $length, ~$length) . $raw_image . $adler_hash; + + // The Zlib header + Adler hash make us add on 11 + $length += 11; + } + + // IDAT + $image .= $this->png_chunk($length, 'IDAT', $raw_image); + + // IEND + $image .= $this->png_chunk(0, 'IEND', ''); + + return $image; + } + + // Each 'data' element is base64_encoded uncompressed IDAT + // png image data + function define_filtered_pngs() + { + $_png = array( + '0' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A///////////////////olFAkBAAAGDyA4P///M31/////////////wD////////////////0dAgAAAAAAAAAAAAEcPipFGHn////////////AP//////////////6DAAAAAAAAAAAAAAAAAALSEAN+T///////////8A//////////////xAAAAAAAAAAAAAAAAAAAAAACPA/////////////wD/////////////oAAAAAAAAAAAAAAAAAAAAAAAev//////////////AP////////////8oAAAAAAAAPNj/zDAAAAAAAABD//////////////8A////////////1AAAAAAAABjw////5BAAAAAAAADo/////////////wD///////////+QAAAAAAAAbP//////QgAAAAAAAKj/////////////AP///////////1wAAAAAAACs/////8AXAAAAAAAAcP////////////8A////////////OAAAAAAAAND////dNwAAAAAAAABI/////////////wD///////////8gAAAAAAAA4P//7koACwAAAAAAACT/////////////AP///////////wgAAAAAAAD///VqAwaPAAAAAAAAEP////////////8A////////////AAAAAAAAAP/8kQYDavUAAAAAAAAA/////////////wD///////////8AAAAAAAAA/6kNAEru/wAAAAAAAAD/////////////AP///////////wAAAAAAAADAIwA33f//AAAAAAAAAP////////////8A////////////FAAAAAAAADYAI8D///8AAAAAAAAQ/////////////wD///////////8kAAAAAAAAAA2p////5AAAAAAAACD/////////////AP///////////0gAAAAAAAAFkfz////UAAAAAAAAQP////////////8A////////////cAAAAAAAAET1/////7AAAAAAAABo/////////////wD///////////+oAAAAAAAAXfX/////sAAAAAAAAGj/////////////AAAAALgAAAAAAAAwAAAAAAAAAAAAAAD////////////oAAAAAAAACOT////oEAAAAAAAAOD/////////////AP////////////8+AAAAAAAAKMz/zDQAAAAAAAA0//////////////8A////////////7jgAAAAAAAAAAAAAAAAAAAAAAKT//////////////wD///////////VqAwIAAAAAAAAAAAAAAAAAAAA8////////////////AP//////////rQcDaVEAAAAAAAAAAAAAAAAAKOj///////////////8A///////////nblnu/IAIAAAAAAAAAAAAAFzw/////////////////wD////////////79////+iITCAAAAAgSITg////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////w==','width' => 40), + '1' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////8BAAAAAAAP//////////////////AP////////////////////////9sAAAAAAAA//////////////////8A////////////////////////pAAAAAAAAAD//////////////////wD//////////////////////6wEAAAAAAAAAP//////////////////AP////////////////////h4AAAAAAAAAAAA//////////////////8A//////////////////ygJAAAAAAAAAAAAAD//////////////////wD//////////////9x8HAAAAAAAAAAAAAAAAP//////////////////AP//////////////AAAAAAAAAAAAAAAAAAAA//////////////////8A//////////////8AAAAAAAAAAAAAAAAAAAD//////////////////wD//////////////wAAAAAAAAR4AAAAAAAAAP//////////////////AP//////////////AAAAAAA4zP8AAAAAAAAA//////////////////8A//////////////8AAAA4sP///wAAAAAAAAD//////////////////wD//////////////yR80P//////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + '2' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP/////////////////okFAkCAAABCBIfNT///////////////////8A///////////////8hAgAAAAAAAAAAAAAAFTo/////////////////wD//////////////1QAAAAAAAAAAAAAAAAAACjo////////////////AP////////////+MAAAAAAAAAAAAAAAAAAAAADj///////////////8A////////////9BAAAAAAAAAAAAAAAAAAAAAAALD//////////////wD///////////+gAAAAAAAAAHjs+KwMAAAAAAAAVP//////////////AP///////////1gAAAAAAABM/////6QAAAAAAAAU//////////////8A////////////KAAAAAAAALj/////+AAAAAAAAAD//////////////wD///////////+MfGBMOCAI8P/////wAAAAAAAACP//////////////AP///////////////////////////5wAAAAAAAAw//////////////8A///////////////////////////oFAAAAAAAAHz//////////////wD/////////////////////////6CgAAAAAAAAE3P//////////////AP///////////////////////9ggAAAAAAAAAHT///////////////8A//////////////////////+0DAAAAAAAAAA8+P///////////////wD/////////////////////gAAAAAAAAAAAKOj/////////////////AP//////////////////9FAAAAAAAAAAADzw//////////////////8A/////////////////+g4AAAAAAAAAABk/P///////////////////wD////////////////oKAAAAAAAAAAMqP//////////////////////AP//////////////6CgAAAAAAAAAMNz///////////////////////8A//////////////g4AAAAAAAAAFT0/////////////////////////wD/////////////bAAAAAAAAABU/P//////////////////////////AP///////////8wAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A////////////SAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////9wAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////hAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////9AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////xAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP////////////////////////////////////////////////////8=','width' => 40), + '3' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD////////////////8sGg0FAAAACA4cLz8////////////////////AP//////////////rBgAAAAAAAAAAAAAACTA//////////////////8A/////////////3QAAAAAAAAAAAAAAAAAAASs/////////////////wD///////////+YAAAAAAAAAAAAAAAAAAAAAAjc////////////////AP//////////6AwAAAAAAAAAAAAAAAAAAAAAAGT///////////////8A//////////94AAAAAAAABJDw/8g4AAAAAAAAHP///////////////wD//////////yAAAAAAAACE/////9gAAAAAAAAA////////////////AP///////////NSwiGQ4FOT//////AAAAAAAABD///////////////8A//////////////////////////+YAAAAAAAAVP///////////////wD//////////////////////P/ggAQAAAAAAATM////////////////AP////////////////////9gAAAAAAAAAAAElP////////////////8A/////////////////////0AAAAAAAAAAHLj//////////////////wD/////////////////////OAAAAAAAAAAwkPj/////////////////AP////////////////////8gAAAAAAAAAAAAINj///////////////8A/////////////////////xAAAAAAAAAAAAAAIPD//////////////wD/////////////////////uOz/4HgEAAAAAAAAhP//////////////AP///////////////////////////3wAAAAAAAAw//////////////8A////////////////////////////6AAAAAAAAAj//////////////wD/////////////////////////////AAAAAAAAAP//////////////AP//////////tJh8YEQoDNz//////+AAAAAAAAAY//////////////8A//////////88AAAAAAAAaP//////dAAAAAAAAEz//////////////wD//////////6QAAAAAAAAAdOD/5HQAAAAAAAAApP//////////////AP///////////CgAAAAAAAAAAAAAAAAAAAAAACD4//////////////8A////////////yAQAAAAAAAAAAAAAAAAAAAAEuP///////////////wD/////////////rAQAAAAAAAAAAAAAAAAABJD/////////////////AP//////////////zDQAAAAAAAAAAAAAACTA//////////////////8A/////////////////8BwOCAAAAAUNGi0/P///////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + '4' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////////////////////nAAAAAAAAAD///////////////8A/////////////////////////8AEAAAAAAAAAP///////////////wD////////////////////////gGAAAAAAAAAAA////////////////AP//////////////////////9DAAAAAAAAAAAAD///////////////8A//////////////////////9UAAAAAAAAAAAAAP///////////////wD/////////////////////hAAAAAAAAAAAAAAA////////////////AP///////////////////7QAAAAAAAAAAAAAAAD///////////////8A///////////////////UDAAAAAAUAAAAAAAAAP///////////////wD/////////////////7CQAAAAABMAAAAAAAAAA////////////////AP////////////////xEAAAAAACU/wAAAAAAAAD///////////////8A////////////////cAAAAAAAZP//AAAAAAAAAP///////////////wD//////////////6AAAAAAADz8//8AAAAAAAAA////////////////AP/////////////IBAAAAAAc6P///wAAAAAAAAD///////////////8A////////////5BgAAAAADMz/////AAAAAAAAAP///////////////wD///////////g0AAAAAACk//////8AAAAAAAAA////////////////AP//////////XAAAAAAAfP///////wAAAAAAAAD///////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A////////////////////////////AAAAAAAAAP///////////////wD///////////////////////////8AAAAAAAAA////////////////AP///////////////////////////wAAAAAAAAD///////////////8A////////////////////////////AAAAAAAAAP///////////////wD///////////////////////////8AAAAAAAAA////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + '5' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////////8AAAAAAAAAAAAAAAAAAAAAAA//////////////8A///////////////MAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////////6wAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////////iAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////////9kAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////////0QAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////////IAAAAAAAYP////////////////////////////8A//////////////wAAAAAAAB8/////////////////////////////wD/////////////3AAAAAAAAIj/////////////////////////////AP////////////+4AAAAAAAAoLRYHAAEKGTE//////////////////8A/////////////5QAAAAAAAAQAAAAAAAAAABY9P///////////////wD/////////////dAAAAAAAAAAAAAAAAAAAAAA89P//////////////AP////////////9QAAAAAAAAAAAAAAAAAAAAAABg//////////////8A/////////////zAAAAAAAAAAAAAAAAAAAAAAAADQ/////////////wD/////////////IAAAAAAAAGjY/+h4BAAAAAAAAGz/////////////AP//////////////9NS0lHSc//////90AAAAAAAALP////////////8A/////////////////////////////9QAAAAAAAAE/////////////wD//////////////////////////////wAAAAAAAAD/////////////AP/////////////////////////////8AAAAAAAAEP////////////8A////////////pIRwWEAgDOD//////8wAAAAAAAA8/////////////wD///////////9EAAAAAAAAaP//////ZAAAAAAAAHz/////////////AP///////////6QAAAAAAAAAaOD/4GQAAAAAAAAE4P////////////8A/////////////CQAAAAAAAAAAAAAAAAAAAAAAGD//////////////wD/////////////yAQAAAAAAAAAAAAAAAAAAAAc7P//////////////AP//////////////rAwAAAAAAAAAAAAAAAAAGNj///////////////8A////////////////0EAAAAAAAAAAAAAAAFTo/////////////////wD//////////////////8h4QCAAAAAcQHzU////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + '6' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////////////+0ZCwMAAAUNGjI////////////////////AP/////////////////EMAAAAAAAAAAAAABM6P////////////////8A////////////////lAQAAAAAAAAAAAAAAAAo6P///////////////wD//////////////6wAAAAAAAAAAAAAAAAAAABI////////////////AP/////////////oEAAAAAAAAAAAAAAAAAAAAACw//////////////8A/////////////3AAAAAAAAAoxP/YPAAAAAAAAEj//////////////wD////////////4EAAAAAAACOD////YDCBAVGiAoP//////////////AP///////////7gAAAAAAABY//////////////////////////////8A////////////eAAAAAAAAJT//////////////////////////////wD///////////9MAAAAAAAAvP/IXBgABCx03P//////////////////AP///////////ygAAAAAAADcdAAAAAAAAAAEiP////////////////8A////////////FAAAAAAAAFAAAAAAAAAAAAAAcP///////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAlP//////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAQ8P////////////8A////////////AAAAAAAAAABAyP/kZAAAAAAAAACQ/////////////wD///////////8MAAAAAAAALPj/////WAAAAAAAAET/////////////AP///////////yQAAAAAAACY///////MAAAAAAAAFP////////////8A////////////SAAAAAAAAMD///////wAAAAAAAAA/////////////wD///////////9wAAAAAAAAvP///////wAAAAAAAAD/////////////AP///////////7QAAAAAAACI///////UAAAAAAAAJP////////////8A////////////+AwAAAAAACDw/////2wAAAAAAABY/////////////wD/////////////cAAAAAAAADC8/Ox4AAAAAAAAAKj/////////////AP/////////////oEAAAAAAAAAAAAAAAAAAAAAAk/P////////////8A//////////////+oAAAAAAAAAAAAAAAAAAAABLj//////////////wD///////////////+QAAAAAAAAAAAAAAAAAACQ////////////////AP////////////////+0JAAAAAAAAAAAAAAkuP////////////////8A///////////////////8sGg0FAAADCxgqPz//////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + '7' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAABP////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAy4/////////////wD//////////////////////////+QUAAAAAAAEuP//////////////AP/////////////////////////8QAAAAAAAAKT///////////////8A/////////////////////////4wAAAAAAAB0/////////////////wD////////////////////////cCAAAAAAANPz/////////////////AP///////////////////////0QAAAAAAATY//////////////////8A//////////////////////+0AAAAAAAAeP///////////////////wD//////////////////////CQAAAAAABTw////////////////////AP////////////////////+gAAAAAAAAkP////////////////////8A/////////////////////ywAAAAAABDw/////////////////////wD///////////////////+4AAAAAAAAbP//////////////////////AP///////////////////1wAAAAAAADQ//////////////////////8A///////////////////4DAAAAAAAMP///////////////////////wD//////////////////7QAAAAAAAB8////////////////////////AP//////////////////aAAAAAAAAMj///////////////////////8A//////////////////8oAAAAAAAM/P///////////////////////wD/////////////////8AAAAAAAAET/////////////////////////AP////////////////+0AAAAAAAAcP////////////////////////8A/////////////////4wAAAAAAACY/////////////////////////wD/////////////////WAAAAAAAAMD/////////////////////////AP////////////////80AAAAAAAA4P////////////////////////8A/////////////////xAAAAAAAAD4/////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + '8' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD////////////////////IdDQUAAAEIEiA1P//////////////////AP/////////////////gRAAAAAAAAAAAAAAAROD///////////////8A////////////////0BgAAAAAAAAAAAAAAAAAEMj//////////////wD///////////////AcAAAAAAAAAAAAAAAAAAAAHPD/////////////AP//////////////hAAAAAAAAAAAAAAAAAAAAAAAhP////////////8A//////////////8sAAAAAAAAKMz/zCgAAAAAAAAs/////////////wD//////////////wAAAAAAAADM////zAAAAAAAAAD/////////////AP//////////////BAAAAAAAAP//////AAAAAAAABP////////////8A//////////////8sAAAAAAAAzP///9QAAAAAAAAw/////////////wD//////////////3wAAAAAAAAoyP/YNAAAAAAAAIT/////////////AP//////////////7BgAAAAAAAAAAAAAAAAAAAAc8P////////////8A////////////////xBgAAAAAAAAAAAAAAAAAGNj//////////////wD/////////////////tAQAAAAAAAAAAAAAAACo////////////////AP///////////////HAAAAAAAAAAAAAAAAAAAAB8//////////////8A//////////////9gAAAAAAAAAAAAAAAAAAAAAAB8/////////////wD/////////////wAAAAAAAAABk4P/UWAAAAAAAAATQ////////////AP////////////9UAAAAAAAAaP//////XAAAAAAAAGT///////////8A/////////////xgAAAAAAADg///////cAAAAAAAAJP///////////wD/////////////AAAAAAAAAP////////8AAAAAAAAA////////////AP////////////8AAAAAAAAA4P//////3AAAAAAAAAT///////////8A/////////////ygAAAAAAABg//////9cAAAAAAAALP///////////wD/////////////ZAAAAAAAAABY1P/cXAAAAAAAAABw////////////AP/////////////QAAAAAAAAAAAAAAAAAAAAAAAABNz///////////8A//////////////9gAAAAAAAAAAAAAAAAAAAAAAB0/////////////wD///////////////Q8AAAAAAAAAAAAAAAAAAAAUPz/////////////AP////////////////x4CAAAAAAAAAAAAAAAEIT8//////////////8A///////////////////smFQwGAAAABg0ZKT0/////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + '9' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////////////ysYCwMAAAUNGiw/P//////////////////AP////////////////+4JAAAAAAAAAAAAAAkuP////////////////8A////////////////lAQAAAAAAAAAAAAAAAAAkP///////////////wD//////////////8AEAAAAAAAAAAAAAAAAAAAAqP//////////////AP/////////////8JAAAAAAAAAAAAAAAAAAAAAAQ7P////////////8A/////////////6wAAAAAAAAAfOz8vCwAAAAAAABw/////////////wD/////////////WAAAAAAAAHD/////7BgAAAAAAAz4////////////AP////////////8kAAAAAAAA1P//////hAAAAAAAALT///////////8A/////////////wAAAAAAAAD///////+4AAAAAAAAcP///////////wD/////////////AAAAAAAAAPz//////8AAAAAAAABI////////////AP////////////8UAAAAAAAAzP//////lAAAAAAAACT///////////8A/////////////0QAAAAAAABY//////gsAAAAAAAADP///////////wD/////////////kAAAAAAAAABw5P/IPAAAAAAAAAAA////////////AP/////////////wEAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A//////////////+UAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD///////////////9wAAAAAAAAAAAAAFAAAAAAAAAU////////////AP////////////////+IBAAAAAAAAABw3AAAAAAAACj///////////8A///////////////////cdCwEABhcxP+8AAAAAAAATP///////////wD//////////////////////////////5AAAAAAAAB4////////////AP//////////////////////////////UAAAAAAAALj///////////8A//////////////+kgGxUQCAM2P///+AIAAAAAAAQ+P///////////wD//////////////0gAAAAAAAA42P/EKAAAAAAAAHD/////////////AP//////////////sAAAAAAAAAAAAAAAAAAAAAAQ6P////////////8A////////////////TAAAAAAAAAAAAAAAAAAAAKz//////////////wD////////////////oKAAAAAAAAAAAAAAAAASU////////////////AP/////////////////sUAAAAAAAAAAAAAAwxP////////////////8A////////////////////yHA0FAAADCxktP///////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'A' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD//////////////////+QAAAAAAAAAAAAAAOT/////////////////AP//////////////////kAAAAAAAAAAAAAAAkP////////////////8A//////////////////88AAAAAAAAAAAAAAA8/////////////////wD/////////////////5AAAAAAAAAAAAAAAAADk////////////////AP////////////////+QAAAAAAAAAAAAAAAAAJD///////////////8A/////////////////zwAAAAAAAAAAAAAAAAAPP///////////////wD////////////////kAAAAAAAAAAgAAAAAAAAA5P//////////////AP///////////////5AAAAAAAAAAgAAAAAAAAACQ//////////////8A////////////////PAAAAAAAAAz8HAAAAAAAADz//////////////wD//////////////+QAAAAAAAAAWP9kAAAAAAAAANz/////////////AP//////////////kAAAAAAAAACk/7wAAAAAAAAAhP////////////8A//////////////88AAAAAAAABOz//BQAAAAAAAAw/////////////wD/////////////4AAAAAAAAAA8////ZAAAAAAAAADc////////////AP////////////+EAAAAAAAAAIj///+8AAAAAAAAAIT///////////8A/////////////zAAAAAAAAAA2P////wQAAAAAAAAMP///////////wD////////////cAAAAAAAAACT//////1wAAAAAAAAA3P//////////AP///////////4QAAAAAAAAAAAAAAAAAAAAAAAAAAACE//////////8A////////////MAAAAAAAAAAAAAAAAAAAAAAAAAAAADD//////////wD//////////9wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANz/////////AP//////////hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhP////////8A//////////8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw/////////wD/////////3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADc////////AP////////+EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIT///////8A/////////zAAAAAAAAAAhP///////////2QAAAAAAAAAMP///////wD////////cAAAAAAAAAADM////////////vAAAAAAAAAAA3P//////AP///////4QAAAAAAAAAHP/////////////4DAAAAAAAAACE//////8A////////MAAAAAAAAABk//////////////9cAAAAAAAAADD//////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'B' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAEDh83P///////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAEhP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAeP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAxP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAABY////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAABT///////////8A//////////8AAAAAAAAAAP/////4zEwAAAAAAAAAAP///////////wD//////////wAAAAAAAAAA////////7AAAAAAAAAAQ////////////AP//////////AAAAAAAAAAD////////sAAAAAAAAAEj///////////8A//////////8AAAAAAAAAAP/////4zEQAAAAAAAAAtP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAFz/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAiA/P////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAIjPj//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAGKz/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJT///////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAABNz//////////wD//////////wAAAAAAAAAA///////sqCAAAAAAAAAAbP//////////AP//////////AAAAAAAAAAD/////////yAAAAAAAAAAs//////////8A//////////8AAAAAAAAAAP//////////AAAAAAAAAAT//////////wD//////////wAAAAAAAAAA/////////7wAAAAAAAAAAP//////////AP//////////AAAAAAAAAAD//////+ikGAAAAAAAAAAY//////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFT//////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsP//////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAADj///////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAc6P///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAATOj/////////////AP//////////AAAAAAAAAAAAAAAAAAAEIEBkkNj///////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'C' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////////////5JRULBAAAAgkTIDQ//////////////////8A////////////////1FAAAAAAAAAAAAAAAABAyP///////////////wD//////////////4gEAAAAAAAAAAAAAAAAAAAElP//////////////AP////////////9wAAAAAAAAAAAAAAAAAAAAAAAAlP////////////8A////////////kAAAAAAAAAAAAAAAAAAAAAAAAAAEyP///////////wD//////////9wIAAAAAAAAAAAAAAAAAAAAAAAAAAAw////////////AP//////////WAAAAAAAAAAAWMz/8JwQAAAAAAAAAACw//////////8A/////////+wEAAAAAAAAAID//////9QMAAAAAAAAAET//////////wD/////////nAAAAAAAAAAo/P///////3wAAAAABDBspP//////////AP////////9gAAAAAAAAAIz/////////3BxQjMT0//////////////8A/////////zQAAAAAAAAAzP///////////////////////////////wD/////////GAAAAAAAAADo////////////////////////////////AP////////8AAAAAAAAAAP////////////////////////////////8A/////////wAAAAAAAAAA/////////////////////////////////wD/////////AAAAAAAAAAD/////////////////////////////////AP////////8cAAAAAAAAAOj///////////////////////////////8A/////////zgAAAAAAAAA0P/////////kIGio7P///////////////wD/////////bAAAAAAAAACg/////////5wAAAAAMHS49P//////////AP////////+oAAAAAAAAAEz/////////PAAAAAAAAAAc//////////8A//////////QIAAAAAAAAALz//////6QAAAAAAAAAAGT//////////wD//////////3AAAAAAAAAADIzo/+SEBAAAAAAAAAAAyP//////////AP//////////7BAAAAAAAAAAAAAAAAAAAAAAAAAAAED///////////8A////////////rAAAAAAAAAAAAAAAAAAAAAAAAAAE0P///////////wD/////////////fAAAAAAAAAAAAAAAAAAAAAAAAJz/////////////AP//////////////iAQAAAAAAAAAAAAAAAAAAASY//////////////8A////////////////yEAAAAAAAAAAAAAAAAA8yP///////////////wD//////////////////9yIUCwQAAAAIEB4yP//////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'D' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////8AAAAAAAAAAAAAAAAADChQkOT/////////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAABGjw//////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAACDY/////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAABjk////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAED///////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAKj//////////wD///////////8AAAAAAAAAAP///+isSAAAAAAAAAAANP//////////AP///////////wAAAAAAAAAA////////hAAAAAAAAAAA2P////////8A////////////AAAAAAAAAAD/////////MAAAAAAAAACQ/////////wD///////////8AAAAAAAAAAP////////+MAAAAAAAAAFj/////////AP///////////wAAAAAAAAAA/////////8gAAAAAAAAAMP////////8A////////////AAAAAAAAAAD/////////5AAAAAAAAAAY/////////wD///////////8AAAAAAAAAAP//////////AAAAAAAAAAD/////////AP///////////wAAAAAAAAAA//////////8AAAAAAAAAAP////////8A////////////AAAAAAAAAAD//////////wAAAAAAAAAA/////////wD///////////8AAAAAAAAAAP/////////wAAAAAAAAABD/////////AP///////////wAAAAAAAAAA/////////9QAAAAAAAAAJP////////8A////////////AAAAAAAAAAD/////////qAAAAAAAAABI/////////wD///////////8AAAAAAAAAAP////////9QAAAAAAAAAHj/////////AP///////////wAAAAAAAAAA////////uAAAAAAAAAAAvP////////8A////////////AAAAAAAAAAD////w0HwEAAAAAAAAACT8/////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAoP//////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAADz8//////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAY6P///////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAKNz/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAACHT0//////////////8A////////////AAAAAAAAAAAAAAAAABg4bKj0/////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'E' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP//////////AAAAAAAAAAD///////////////////////////////8A//////////8AAAAAAAAAAP///////////////////////////////wD//////////wAAAAAAAAAA////////////////////////////////AP//////////AAAAAAAAAAD///////////////////////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////8AAAAAAAAAAP///////////////////////////////wD//////////wAAAAAAAAAA////////////////////////////////AP//////////AAAAAAAAAAD///////////////////////////////8A//////////8AAAAAAAAAAP///////////////////////////////wD//////////wAAAAAAAAAA////////////////////////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'F' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'G' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD//////////////////MB8TCgQAAAACCA4YJzs////////////////AP///////////////JQcAAAAAAAAAAAAAAAAAAhw8P////////////8A/////////////9gwAAAAAAAAAAAAAAAAAAAAAAAk2P///////////wD////////////EDAAAAAAAAAAAAAAAAAAAAAAAAAAc7P//////////AP//////////2AwAAAAAAAAAAAAAAAAAAAAAAAAAAABY//////////8A//////////wwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQ/////////wD/////////kAAAAAAAAAAAEHzQ/P/gmCAAAAAAAAAAAFz/////////AP////////wcAAAAAAAAACjg////////8CwAAAAAAAAgWP////////8A////////vAAAAAAAAAAI2P//////////yBRAcJjI8P///////////wD///////94AAAAAAAAAGD/////////////////////////////////AP///////0AAAAAAAAAAsP////////////////////////////////8A////////IAAAAAAAAADc/////////////////////////////////wD///////8AAAAAAAAAAP///////wAAAAAAAAAAAAAAAAD/////////AP///////wAAAAAAAAAA////////AAAAAAAAAAAAAAAAAP////////8A////////AAAAAAAAAAD///////8AAAAAAAAAAAAAAAAA/////////wD///////8gAAAAAAAAAOD//////wAAAAAAAAAAAAAAAAD/////////AP///////0AAAAAAAAAAtP//////AAAAAAAAAAAAAAAAAP////////8A////////cAAAAAAAAABw//////8AAAAAAAAAAAAAAAAA/////////wD///////+8AAAAAAAAABDs////////////AAAAAAAAAAD/////////AP////////wYAAAAAAAAADz0//////////AAAAAAAAAAAP////////8A/////////5AAAAAAAAAAACCY4P//3KhcCAAAAAAAAAAA/////////wD/////////+CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////AP//////////xAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIP////////8A////////////rAQAAAAAAAAAAAAAAAAAAAAAAAAAAGTw/////////wD/////////////vBQAAAAAAAAAAAAAAAAAAAAAADjI////////////AP//////////////8HAQAAAAAAAAAAAAAAAAAEiw//////////////8A//////////////////iwcEAgBAAABCA4aKDk/////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'H' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'I' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'J' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAj//////////////wD//////////+zMrIxwUDAQ//////wAAAAAAAAAIP//////////////AP//////////DAAAAAAAAADo////2AAAAAAAAAA0//////////////8A//////////8wAAAAAAAAAKj///+YAAAAAAAAAFj//////////////wD//////////2gAAAAAAAAAIND/yBgAAAAAAAAAkP//////////////AP//////////vAAAAAAAAAAAAAAAAAAAAAAAAADc//////////////8A////////////MAAAAAAAAAAAAAAAAAAAAAAAUP///////////////wD////////////EBAAAAAAAAAAAAAAAAAAAABjk////////////////AP////////////+sBAAAAAAAAAAAAAAAAAAY2P////////////////8A///////////////EMAAAAAAAAAAAAAAAVOj//////////////////wD/////////////////vHBAIAAAABg8fNT/////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'K' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////8AAAAAAAAAAP//////////wAQAAAAAAAAAAABw////////AP///////wAAAAAAAAAA/////////9AMAAAAAAAAAAAAcP////////8A////////AAAAAAAAAAD////////cGAAAAAAAAAAAAHD//////////wD///////8AAAAAAAAAAP//////6CgAAAAAAAAAAABs////////////AP///////wAAAAAAAAAA//////Q0AAAAAAAAAAAAVPz///////////8A////////AAAAAAAAAAD////8RAAAAAAAAAAAAFT8/////////////wD///////8AAAAAAAAAAP///1gAAAAAAAAAAABU/P//////////////AP///////wAAAAAAAAAA//9wAAAAAAAAAAAASPz///////////////8A////////AAAAAAAAAAD/jAAAAAAAAAAAADz0/////////////////wD///////8AAAAAAAAAAKQAAAAAAAAAAAA89P//////////////////AP///////wAAAAAAAAAABAAAAAAAAAAAFPT///////////////////8A////////AAAAAAAAAAAAAAAAAAAAAAAApP///////////////////wD///////8AAAAAAAAAAAAAAAAAAAAAAAAU8P//////////////////AP///////wAAAAAAAAAAAAAAAAAAAAAAAABk//////////////////8A////////AAAAAAAAAAAAAAAAAAAAAAAAAADE/////////////////wD///////8AAAAAAAAAAAAAAAAoEAAAAAAAACz8////////////////AP///////wAAAAAAAAAAAAAAGNiAAAAAAAAAAIj///////////////8A////////AAAAAAAAAAAAABjY//gYAAAAAAAACOD//////////////wD///////8AAAAAAAAAAAAY2P///5wAAAAAAAAASP//////////////AP///////wAAAAAAAAAAGNj//////CgAAAAAAAAAqP////////////8A////////AAAAAAAAAADI////////sAAAAAAAAAAc8P///////////wD///////8AAAAAAAAAAP//////////QAAAAAAAAABs////////////AP///////wAAAAAAAAAA///////////IAAAAAAAAAATI//////////8A////////AAAAAAAAAAD///////////9YAAAAAAAAADD8/////////wD///////8AAAAAAAAAAP///////////9wEAAAAAAAAAJD/////////AP///////wAAAAAAAAAA/////////////3AAAAAAAAAADOT///////8A////////AAAAAAAAAAD/////////////7BAAAAAAAAAAUP///////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'L' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'M' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//////8AAAAAAAAAAAAAAHz//////3wAAAAAAAAAAAAAAP///////wD//////wAAAAAAAAAAAAAATP//////UAAAAAAAAAAAAAAA////////AP//////AAAAAAAAAAAAAAAc//////8cAAAAAAAAAAAAAAD///////8A//////8AAAAAAAAAAAAAAADw////8AAAAAAAAAAAAAAAAP///////wD//////wAAAAAAAAAAAAAAALz////AAAAAAAAAAAAAAAAA////////AP//////AAAAAAAAAAAAAAAAkP///5AAAAAAAAAAAAAAAAD///////8A//////8AAAAAAAAAAAAAAABc////ZAAAAAAAAAAAAAAAAP///////wD//////wAAAAAAAAAoAAAAADD///8wAAAAACQAAAAAAAAA////////AP//////AAAAAAAAAFwAAAAABPz//AgAAAAAXAAAAAAAAAD///////8A//////8AAAAAAAAAkAAAAAAA0P/UAAAAAACQAAAAAAAAAP///////wD//////wAAAAAAAADMAAAAAACg/6gAAAAAAMQAAAAAAAAA////////AP//////AAAAAAAAAPgEAAAAAHD/dAAAAAAE+AAAAAAAAAD///////8A//////8AAAAAAAAA/zQAAAAAQP9IAAAAADD/AAAAAAAAAP///////wD//////wAAAAAAAAD/bAAAAAAQ/xQAAAAAaP8AAAAAAAAA////////AP//////AAAAAAAAAP+gAAAAAADQAAAAAACc/wAAAAAAAAD///////8A//////8AAAAAAAAA/9QAAAAAAGgAAAAAAND/AAAAAAAAAP///////wD//////wAAAAAAAAD//wwAAAAAFAAAAAAM/P8AAAAAAAAA////////AP//////AAAAAAAAAP//RAAAAAAAAAAAADz//wAAAAAAAAD///////8A//////8AAAAAAAAA//94AAAAAAAAAAAAcP//AAAAAAAAAP///////wD//////wAAAAAAAAD//7AAAAAAAAAAAACo//8AAAAAAAAA////////AP//////AAAAAAAAAP//5AAAAAAAAAAAANz//wAAAAAAAAD///////8A//////8AAAAAAAAA////HAAAAAAAAAAQ////AAAAAAAAAP///////wD//////wAAAAAAAAD///9QAAAAAAAAAEz///8AAAAAAAAA////////AP//////AAAAAAAAAP///4gAAAAAAAAAfP///wAAAAAAAAD///////8A//////8AAAAAAAAA////vAAAAAAAAACw////AAAAAAAAAP///////wD//////wAAAAAAAAD////wAAAAAAAAAOz///8AAAAAAAAA////////AP//////AAAAAAAAAP////8sAAAAAAAc/////wAAAAAAAAD///////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'N' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////AAAAAAAAALD/////////////AAAAAAAAAP//////////AP////////8AAAAAAAAAFOj///////////8AAAAAAAAA//////////8A/////////wAAAAAAAAAASP///////////wAAAAAAAAD//////////wD/////////AAAAAAAAAAAAkP//////////AAAAAAAAAP//////////AP////////8AAAAAAAAAAAAI1P////////8AAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAw+P///////wAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAABw////////AAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAC8//////8AAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAAABzs/////wAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAAAAAFD/////AAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAJz///8AAAAAAAAA//////////8A/////////wAAAAAAAAAUAAAAAAAADNz//wAAAAAAAAD//////////wD/////////AAAAAAAAALQAAAAAAAAANPz/AAAAAAAAAP//////////AP////////8AAAAAAAAA/2wAAAAAAAAAfP8AAAAAAAAA//////////8A/////////wAAAAAAAAD/+CwAAAAAAAAExAAAAAAAAAD//////////wD/////////AAAAAAAAAP//0AQAAAAAAAAgAAAAAAAAAP//////////AP////////8AAAAAAAAA////jAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAD/////RAAAAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAP/////kFAAAAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAA//////+sAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAD///////9kAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAP////////QkAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAA/////////8wEAAAAAAAAAAAA//////////8A/////////wAAAAAAAAD//////////4QAAAAAAAAAAAD//////////wD/////////AAAAAAAAAP///////////DwAAAAAAAAAAP//////////AP////////8AAAAAAAAA////////////4BAAAAAAAAAA//////////8A/////////wAAAAAAAAD/////////////qAAAAAAAAAD//////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'O' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A///////////////////0qGw4HAAAABw4aKT0/////////////////wD////////////////wcAwAAAAAAAAAAAAAAAho6P//////////////AP//////////////uBQAAAAAAAAAAAAAAAAAAAAMoP////////////8A/////////////6AEAAAAAAAAAAAAAAAAAAAAAAAAkP///////////wD///////////+4BAAAAAAAAAAAAAAAAAAAAAAAAAAAoP//////////AP//////////8BQAAAAAAAAAAAAAAAAAAAAAAAAAAAAM5P////////8A//////////9wAAAAAAAAAAAsrPD/7KQsAAAAAAAAAABg/////////wD/////////+BAAAAAAAAAAUPj///////hQAAAAAAAAAAjs////////AP////////+sAAAAAAAAABDw//////////AYAAAAAAAAAKD///////8A/////////2wAAAAAAAAAdP///////////3wAAAAAAAAAYP///////wD/////////OAAAAAAAAAC4////////////xAAAAAAAAAAw////////AP////////8cAAAAAAAAAOD////////////oAAAAAAAAABT///////8A/////////wAAAAAAAAAA//////////////8AAAAAAAAAAP///////wD/////////AAAAAAAAAAD//////////////wAAAAAAAAAA////////AP////////8AAAAAAAAAAP/////////////8AAAAAAAAAAD///////8A/////////xwAAAAAAAAA5P///////////+AAAAAAAAAAHP///////wD/////////NAAAAAAAAAC8////////////uAAAAAAAAAA4////////AP////////9oAAAAAAAAAHj///////////98AAAAAAAAAGT///////8A/////////6gAAAAAAAAAGPD/////////+BgAAAAAAAAApP///////wD/////////9AwAAAAAAAAAUPz///////xcAAAAAAAAAAjs////////AP//////////cAAAAAAAAAAALKjs//CwOAAAAAAAAAAAYP////////8A///////////wFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzk/////////wD///////////+4BAAAAAAAAAAAAAAAAAAAAAAAAAAAoP//////////AP////////////+QAAAAAAAAAAAAAAAAAAAAAAAAAJD///////////8A//////////////+sEAAAAAAAAAAAAAAAAAAAAAyg/////////////wD////////////////oZAgAAAAAAAAAAAAAAARg4P//////////////AP//////////////////9KhsOCAAAAAUMFyc7P////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'P' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP///////////wAAAAAAAAAAAAAAAAAACCxguP////////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAOOD//////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAGOD/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAARP////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAxP///////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAABo////////////AP///////////wAAAAAAAAAA////6JwMAAAAAAAAADD///////////8A////////////AAAAAAAAAAD//////6AAAAAAAAAADP///////////wD///////////8AAAAAAAAAAP//////9AAAAAAAAAAA////////////AP///////////wAAAAAAAAAA///////0AAAAAAAAAAD///////////8A////////////AAAAAAAAAAD//////5gAAAAAAAAAHP///////////wD///////////8AAAAAAAAAAP///9iICAAAAAAAAABI////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAJD///////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAI6P///////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAIT/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAABU/P////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAIhPz//////////////wD///////////8AAAAAAAAAAAAAAAAABCRMkOz/////////////////AP///////////wAAAAAAAAAA//////////////////////////////8A////////////AAAAAAAAAAD//////////////////////////////wD///////////8AAAAAAAAAAP//////////////////////////////AP///////////wAAAAAAAAAA//////////////////////////////8A////////////AAAAAAAAAAD//////////////////////////////wD///////////8AAAAAAAAAAP//////////////////////////////AP///////////wAAAAAAAAAA//////////////////////////////8A////////////AAAAAAAAAAD//////////////////////////////wD///////////8AAAAAAAAAAP//////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'Q' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////SoaDQcAAAAHDhoqPT///////////////////8A//////////////BwDAAAAAAAAAAAAAAACHDo/////////////////wD///////////+4FAAAAAAAAAAAAAAAAAAAABCo////////////////AP//////////nAQAAAAAAAAAAAAAAAAAAAAAAACQ//////////////8A/////////7gEAAAAAAAAAAAAAAAAAAAAAAAAAACg/////////////wD////////wFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzo////////////AP///////3AAAAAAAAAAACyo8P/sqCwAAAAAAAAAAGT///////////8A///////4EAAAAAAAAABM+P///////FQAAAAAAAAACPT//////////wD//////7AAAAAAAAAAFPD/////////9BgAAAAAAAAApP//////////AP//////bAAAAAAAAAB4////////////fAAAAAAAAABk//////////8A//////84AAAAAAAAALz///////////+8AAAAAAAAADT//////////wD//////xwAAAAAAAAA6P///////////+QAAAAAAAAAHP//////////AP//////AAAAAAAAAAD//////////////wAAAAAAAAAA//////////8A//////8AAAAAAAAAAP//////////////AAAAAAAAAAD//////////wD//////wAAAAAAAAAA/P////////////8AAAAAAAAAAP//////////AP//////GAAAAAAAAADg////////////4AAAAAAAAAAc//////////8A//////84AAAAAAAAALT////MJHTo//+8AAAAAAAAADT//////////wD//////2wAAAAAAAAAdP///2AAABCg/3wAAAAAAAAAZP//////////AP//////rAAAAAAAAAAY9P/sCAAAAABMGAAAAAAAAACk//////////8A///////4EAAAAAAAAABU/P+0OAAAAAAAAAAAAAAACPT//////////wD///////94AAAAAAAAAAA4sPD/gAAAAAAAAAAAAABk////////////AP////////AcAAAAAAAAAAAAAAAAAAAAAAAAAAAADOT///////////8A/////////7wEAAAAAAAAAAAAAAAAAAAAAAAAAACQ/////////////wD//////////6wEAAAAAAAAAAAAAAAAAAAAAAAAABSs////////////AP///////////7gUAAAAAAAAAAAAAAAAAAAAAAAAAABAwP////////8A//////////////BwDAAAAAAAAAAAAAAABAgAAAAAAAA8/////////wD////////////////0qGg0GAAAABgwXJjkxBgAAAAAALD/////////AP//////////////////////////////////5DQAAAAk/P////////8A////////////////////////////////////+GwAAJD//////////wD//////////////////////////////////////8A49P//////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'R' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////wAAAAAAAAAAAAAAAAAAAAQgOGSk+P///////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAcuP//////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAEsP////////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ6P///////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD///////////8A/////////wAAAAAAAAAA///////svDgAAAAAAAAACP///////////wD/////////AAAAAAAAAAD/////////7AAAAAAAAAAA////////////AP////////8AAAAAAAAAAP/////////cAAAAAAAAABD///////////8A/////////wAAAAAAAAAA//////DQoCQAAAAAAAAAQP///////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACU////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIPj///////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAzU/////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAA02P//////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAxctPz///////////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAEDY/////////////////wD/////////AAAAAAAAAAD/9LAsAAAAAAAAAAzc////////////////AP////////8AAAAAAAAAAP///+wkAAAAAAAAADD8//////////////8A/////////wAAAAAAAAAA/////8QAAAAAAAAAAJD//////////////wD/////////AAAAAAAAAAD//////1QAAAAAAAAAFPD/////////////AP////////8AAAAAAAAAAP//////3AQAAAAAAAAAgP////////////8A/////////wAAAAAAAAAA////////aAAAAAAAAAAM6P///////////wD/////////AAAAAAAAAAD////////oCAAAAAAAAABs////////////AP////////8AAAAAAAAAAP////////+AAAAAAAAAAATc//////////8A/////////wAAAAAAAAAA//////////AUAAAAAAAAAFj//////////wD/////////AAAAAAAAAAD//////////5AAAAAAAAAAAND/////////AP////////8AAAAAAAAAAP//////////+CQAAAAAAAAAQP////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'S' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP/////////////////8vHBEIAgAAAQgQHC8/P////////////////8A////////////////pCQAAAAAAAAAAAAAAAAcoP///////////////wD//////////////FwAAAAAAAAAAAAAAAAAAAAAXP//////////////AP////////////9oAAAAAAAAAAAAAAAAAAAAAAAAhP////////////8A////////////zAAAAAAAAAAAAAAAAAAAAAAAAAAI6P///////////wD///////////9cAAAAAAAAAAAAAAAAAAAAAAAAAACA////////////AP///////////xgAAAAAAAAAUOD/8KwkAAAAAAAAADj///////////8A////////////AAAAAAAAAAD0/////8wABCAgICxASP///////////wD///////////8MAAAAAAAAAMz/////////////////////////////AP///////////0AAAAAAAAAACFiQxPT///////////////////////8A////////////oAAAAAAAAAAAAAAAADBwtPT//////////////////wD////////////8QAAAAAAAAAAAAAAAAAAACFTA////////////////AP/////////////oOAAAAAAAAAAAAAAAAAAAAABM6P////////////8A///////////////4fAgAAAAAAAAAAAAAAAAAAAAY2P///////////wD/////////////////7IwwAAAAAAAAAAAAAAAAAAAo+P//////////AP/////////////////////koGw0BAAAAAAAAAAAAACU//////////8A///////////////////////////4uFgAAAAAAAAAADz//////////wD//////////2BgSEA0IBwA6P///////5QAAAAAAAAADP//////////AP//////////JAAAAAAAAACc/////////AAAAAAAAAAA//////////8A//////////9YAAAAAAAAACDo///////AAAAAAAAAABT//////////wD//////////6QAAAAAAAAAACCk7P/snBQAAAAAAAAAUP//////////AP//////////+BAAAAAAAAAAAAAAAAAAAAAAAAAAAACs//////////8A////////////kAAAAAAAAAAAAAAAAAAAAAAAAAAAOP///////////wD////////////8RAAAAAAAAAAAAAAAAAAAAAAAABjc////////////AP/////////////0PAAAAAAAAAAAAAAAAAAAAAAg2P////////////8A///////////////8hBQAAAAAAAAAAAAAAAAMdPT//////////////wD/////////////////+LRwSCAMAAAAHDhoqPT/////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'T' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'U' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////JAAAAAAAAADk/////////+gAAAAAAAAAHP//////////AP////////9MAAAAAAAAAJz/////////nAAAAAAAAABE//////////8A/////////4gAAAAAAAAAHOj//////+ggAAAAAAAAAHz//////////wD/////////0AAAAAAAAAAAIJzs/+ykIAAAAAAAAAAA0P//////////AP//////////QAAAAAAAAAAAAAAAAAAAAAAAAAAAAED///////////8A///////////IBAAAAAAAAAAAAAAAAAAAAAAAAAAE0P///////////wD///////////+YAAAAAAAAAAAAAAAAAAAAAAAAAJj/////////////AP////////////+UBAAAAAAAAAAAAAAAAAAAAASU//////////////8A///////////////IPAAAAAAAAAAAAAAAAAAwyP///////////////wD/////////////////0IxYOCAIAAAEIEiAyP//////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'V' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD//////zAAAAAAAAAAYP//////////////ZAAAAAAAAAAw////////AP//////kAAAAAAAAAAU/P////////////8UAAAAAAAAAJD///////8A///////oBAAAAAAAAADE////////////xAAAAAAAAAAE7P///////wD///////9MAAAAAAAAAHD///////////94AAAAAAAAAEz/////////AP///////6gAAAAAAAAAJP///////////yQAAAAAAAAArP////////8A////////+BAAAAAAAAAA1P/////////YAAAAAAAAABT4/////////wD/////////aAAAAAAAAACE/////////4QAAAAAAAAAbP//////////AP/////////EAAAAAAAAADT/////////OAAAAAAAAADM//////////8A//////////8kAAAAAAAAAOT//////+QAAAAAAAAAKP///////////wD//////////4QAAAAAAAAAmP//////nAAAAAAAAACI////////////AP//////////5AAAAAAAAABE//////9EAAAAAAAABOT///////////8A////////////QAAAAAAAAAT0////9AgAAAAAAABI/////////////wD///////////+gAAAAAAAAAKT///+kAAAAAAAAAKj/////////////AP////////////QIAAAAAAAAXP///1wAAAAAAAAM+P////////////8A/////////////1wAAAAAAAAM+P/8DAAAAAAAAGT//////////////wD/////////////vAAAAAAAAAC8/7wAAAAAAAAAxP//////////////AP//////////////HAAAAAAAAGj/aAAAAAAAACT///////////////8A//////////////94AAAAAAAAHP8cAAAAAAAAhP///////////////wD//////////////9gAAAAAAAAAkAAAAAAAAADk////////////////AP///////////////zgAAAAAAAAQAAAAAAAAQP////////////////8A////////////////lAAAAAAAAAAAAAAAAACg/////////////////wD////////////////sCAAAAAAAAAAAAAAADPT/////////////////AP////////////////9QAAAAAAAAAAAAAABg//////////////////8A/////////////////7AAAAAAAAAAAAAAAMD//////////////////wD//////////////////BQAAAAAAAAAAAAc////////////////////AP//////////////////cAAAAAAAAAAAAHz///////////////////8A///////////////////MAAAAAAAAAAAA3P///////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'W' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//8cAAAAAAAAALz/////4AAAAAAAAAAA6P////+8AAAAAAAAABz//wD//1QAAAAAAAAAjP////+gAAAAAAAAAACo/////4wAAAAAAAAAUP//AP//jAAAAAAAAABU/////2AAAAAAAAAAAGj/////VAAAAAAAAACM//8A///EAAAAAAAAACT/////IAAAAAAAAAAAKP////8kAAAAAAAAAMT//wD///gEAAAAAAAAAPD//+AAAAAAAAAAAAAA6P//8AAAAAAAAAAE9P//AP///zAAAAAAAAAAvP//oAAAAAAAAAAAAACo//+8AAAAAAAAADD///8A////bAAAAAAAAACM//9gAAAAAAAAAAAAAGT//4wAAAAAAAAAaP///wD///+kAAAAAAAAAFT//yAAAAAAAAAAAAAAIP//VAAAAAAAAACc////AP///9gAAAAAAAAAJP/gAAAAAAAAAAAAAAAA4P8kAAAAAAAAANT///8A/////xAAAAAAAAAA8KAAAAAAAAAAAAAAAACg8AAAAAAAAAAQ/////wD/////TAAAAAAAAAC8YAAAAAAAAAAAAAAAAGC8AAAAAAAAAET/////AP////+AAAAAAAAAAIwgAAAAAAAAAAAAAAAAIIwAAAAAAAAAfP////8A/////7gAAAAAAAAANAAAAAAAACwwAAAAAAAANAAAAAAAAACw/////wD/////8AAAAAAAAAAAAAAAAAAAdHgAAAAAAAAAAAAAAAAAAOz/////AP//////KAAAAAAAAAAAAAAAAAC4vAAAAAAAAAAAAAAAAAAg//////8A//////9gAAAAAAAAAAAAAAAACPj4CAAAAAAAAAAAAAAAAFj//////wD//////5QAAAAAAAAAAAAAAABE//9IAAAAAAAAAAAAAAAAkP//////AP//////0AAAAAAAAAAAAAAAAIj//4wAAAAAAAAAAAAAAADI//////8A///////8DAAAAAAAAAAAAAAAzP//1AAAAAAAAAAAAAAABPj//////wD///////88AAAAAAAAAAAAABT/////GAAAAAAAAAAAAAA0////////AP///////3QAAAAAAAAAAAAAWP////9gAAAAAAAAAAAAAHD///////8A////////sAAAAAAAAAAAAACg/////6QAAAAAAAAAAAAApP///////wD////////kAAAAAAAAAAAAAOT/////6AAAAAAAAAAAAADc////////AP////////8cAAAAAAAAAAAo////////MAAAAAAAAAAAEP////////8A/////////1QAAAAAAAAAAHD///////94AAAAAAAAAABM/////////wD/////////jAAAAAAAAAAAtP///////7wAAAAAAAAAAID/////////AP/////////EAAAAAAAAAAT0////////+AgAAAAAAAAAuP////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'X' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////9UAAAAAAAAAKz///////////+sAAAAAAAAAFD/////////AP///////+QQAAAAAAAAFOT/////////8BwAAAAAAAAM5P////////8A/////////5gAAAAAAAAATP////////9kAAAAAAAAAJD//////////wD//////////0AAAAAAAAAAoP//////wAAAAAAAAAA0/P//////////AP//////////2AgAAAAAAAAQ4P////gkAAAAAAAABMz///////////8A////////////iAAAAAAAAABA////dAAAAAAAAABw/////////////wD////////////8MAAAAAAAAACU/9AEAAAAAAAAHPD/////////////AP/////////////IBAAAAAAAAAzYMAAAAAAAAACs//////////////8A//////////////90AAAAAAAAABAAAAAAAAAATP///////////////wD///////////////QgAAAAAAAAAAAAAAAAAAzg////////////////AP///////////////7wAAAAAAAAAAAAAAAAAjP////////////////8A/////////////////2AAAAAAAAAAAAAAADD8/////////////////wD/////////////////7BQAAAAAAAAAAAAEyP//////////////////AP/////////////////gDAAAAAAAAAAAAAjY//////////////////8A/////////////////0AAAAAAAAAAAAAAADj8/////////////////wD///////////////+UAAAAAAAAAAAAAAAAAJD/////////////////AP//////////////4AwAAAAAAAAAAAAAAAAADOD///////////////8A//////////////9AAAAAAAAAAAAAAAAAAAAAQP///////////////wD/////////////nAAAAAAAAAAAWAAAAAAAAAAAlP//////////////AP///////////+QQAAAAAAAAAGD/YAAAAAAAAAAM4P////////////8A////////////TAAAAAAAAAAs9P/0LAAAAAAAAABM/////////////wD//////////6AAAAAAAAAADNT////UDAAAAAAAAACg////////////AP/////////kEAAAAAAAAACg//////+gAAAAAAAAABDk//////////8A/////////0wAAAAAAAAAYP////////9gAAAAAAAAAEz//////////wD///////+oAAAAAAAAACz0//////////QsAAAAAAAAAKT/////////AP//////7BQAAAAAAAAM1P///////////9QMAAAAAAAAFOz///////8A//////9UAAAAAAAAAKD//////////////6AAAAAAAAAAVP///////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'Y' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP///////1QAAAAAAAAAAGj//////////2gAAAAAAAAAAFT///////8A////////5BAAAAAAAAAAAMT////////EAAAAAAAAAAAQ5P///////wD/////////mAAAAAAAAAAAKPj/////+CgAAAAAAAAAAJj/////////AP//////////PAAAAAAAAAAAgP////+AAAAAAAAAAAA8//////////8A///////////YCAAAAAAAAAAE2P//2AQAAAAAAAAACNj//////////wD///////////+AAAAAAAAAAAA4//84AAAAAAAAAACA////////////AP////////////woAAAAAAAAAACUlAAAAAAAAAAAKPz///////////8A/////////////8gAAAAAAAAAABAQAAAAAAAAAADI/////////////wD//////////////2wAAAAAAAAAAAAAAAAAAAAAbP//////////////AP//////////////8BwAAAAAAAAAAAAAAAAAABzw//////////////8A////////////////tAAAAAAAAAAAAAAAAAAAtP///////////////wD/////////////////VAAAAAAAAAAAAAAAAFT/////////////////AP/////////////////oEAAAAAAAAAAAAAAQ6P////////////////8A//////////////////+cAAAAAAAAAAAAAJz//////////////////wD///////////////////9AAAAAAAAAAABA////////////////////AP///////////////////9gAAAAAAAAAANj///////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////8AAAAAAAAAAP////////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////8AAAAAAAAAAP////////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////8AAAAAAAAAAP////////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + 'Z' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAQ//////////////8A/////////////////////////1AAAAAAAAAABLz//////////////wD///////////////////////98AAAAAAAAAACY////////////////AP//////////////////////pAAAAAAAAAAAaP////////////////8A/////////////////////8QIAAAAAAAAAET8/////////////////wD////////////////////gGAAAAAAAAAAo9P//////////////////AP//////////////////9CwAAAAAAAAAFNz///////////////////8A//////////////////xMAAAAAAAAAATA/////////////////////wD/////////////////eAAAAAAAAAAAnP//////////////////////AP///////////////5wAAAAAAAAAAHT///////////////////////8A///////////////ABAAAAAAAAABM/P///////////////////////wD/////////////3BQAAAAAAAAALPT/////////////////////////AP////////////QoAAAAAAAAABjg//////////////////////////8A///////////8SAAAAAAAAAAExP///////////////////////////wD//////////2wAAAAAAAAAAKD/////////////////////////////AP////////+YAAAAAAAAAAB8//////////////////////////////8A/////////wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), + ); + + return $_png; + } + + // These define base64_encoded raw png image data used + // when we cannot generate our own single png image + function define_raw_pngs() + { + $_png = array( + '0' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QKCNGXKO6AAAAB3RJTUUH0wUOEDQ6EUG1VwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAXNJREFUeNpj/M9AHGAiUt2wVvhyaqAqKyOjpG3jQwaGv+e+IUn9RwJfSjjg4iwFP1aKJD6HyyErfGGAYrquIoP5E2wK/zigu0v5wH9sChdgeKDqP1aFGhBZmxv/z0Dd4IxV4RWIpMQHIPuJAITzAqEQETx7IFQIP5CQNoJwDmALxzMQCuyjg1chnBPYwtECwr8AZN41h0p6YHOjAkTuwf//77wYuCEcFWwKOWA2fM1iZuuHcASwKYQ55c9ENuasrxgRjKlwJS+D17v/hBUeUGYwv/sfn0IRiJQZJIbxuFEFagjvSlDUQNgK2GIGqpC1JRhIfoAqxBYz0DRhn8IMJO+giKEqhMaMJBeI3AHhIKdkRPqG8DlAifqFADyasKRHO6h1Z/6fMYEwTbCmx3cWGCl8CTaFwBhGz+M2/7EpXMvOnBmIok7jBVaFz/Mi3/1pQORrhpgPyOr+M8IL0j9/gKpeLjhy5QEwoDVsYuRR3cE4IktcAJNx8cJaZBeQAAAAAElFTkSuQmCC', + '1' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QMi//xxVKAAAAB3RJTUUH0wUOEDYLcqnX7wAAAAlwSFlzAAALEgAACxIB0t1+/AAAAHpJREFUeNpj/M9AHGAiUh1WhR8FGUGAsMKaD9iM/I8BlmCVwVS4hoUohT8qcNiFyv2zQIWBCIV3amRwu54RKcDRAgQ1KigIcJYK7CqR3QsCFmf+Y8qgeQakbANMAz6FKjUXECbj8zWa76nm61GFw1UhI10KqVGFNFQIADdK9Zj7PsV9AAAAAElFTkSuQmCC', + '2' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QMwPUBEjoAAAAB3RJTUUH0wUOEDUqFe2UcgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAQxJREFUeNpj/M9AHGAiUt2owkGrkAWF93LFgStPfjCwyGiYRGijqfyPAH9aOJAkQl78RwbICkNQjdB4gUNhD7qzLLAr/CKA4YENSAoRvl7zAUJXvPmxhgfCXILVMxEQvg+IDVUhgtVqDYjkDhD7B2aQIMIx5cOTN29evLAAsaEKObBajQzmQOQMcIQjHLwQgSisIaDwBdS5LHfwK7yhAHVVyX+8CrdAA5HB5gdehQ3Yoxpd4ZcAmDqbD//xKISEIjhU//zHoxDmXQaeFRhOZ8CmzuDOf3wKf8DsDfnyH6/CHJi6P//xKjyDJethVehBpMI7DPgVwrPCCgb8AK5wDwGFcNMF8EkCASOx1QcAGUxu1untnFIAAAAASUVORK5CYII=', + '3' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QMxBQugk2AAAAB3RJTUUH0wUOEDU3duv4qwAAAAlwSFlzAAALEgAACxIB0t1+/AAAATdJREFUeNpj/M9AHGAiUt0IVciCzPm7ZceZB28YGBQkLHwcmNFU/keANRJI4ioH/qMAJIUlaHatwaFwBrqrOO5gVfiCB8P9KVgVVkAtnPDh/wkLCFsGq0IFiGQLiH0D06P/GWHJ7O+NOzfuXLlzQRrEhgSawHscwYPurxAcwQMBf/4/aIAYyHIGr8IEeDhO+Y9XoQNUncwOVHGMRPEDSovc+IkzrpGDCQgUbuC1WgBhhsIHfAp3vPn/oIIFKfRxKQSDGohCA4IKX0DTD7YoRAWMUJ9iyQpbn4DBBWUQ5yFEDDnFw622gXAzwBxoYvfB5sYlUI0lD/4/gWWKJdgU/tHAcKjCD6y+PsGCpo4FJbaRgmcNqkqWCThTzxkTJHXo+Ro1HA9uOPHiATDlKJj4eKCVFIzDqWgGAK7GW/haPS+zAAAAAElFTkSuQmCC', + '4' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QMyqWttCEAAAAB3RJTUUH0wUOEDUxn4hdngAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKBJREFUeNpj/M9AHGAiUh2FCucyQgCK4H9McIAFixwWhQ8kGIhS+MWAgTiFIQzEKWxhIE7hFgbiFF7hASkQIajwjQpInuUAIYV/XMDyU/4TUlgAlk75T0jhArCszR9CCk+AY07mxX8CCp+AY47nzH8CCn+YgOWW/CekMAYsVfMfl0JGmCBq4kNEDp2zAn0UMmItABjRvDykPTO43DgyFQIANP6pTFLWAdoAAAAASUVORK5CYII=', + '5' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QMzPy3XhEAAAAB3RJTUUH0wUOEDUk8lW5dQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAQpJREFUeNpj/M9AHGAiUt2oQuIVfmREBzgU3iHWxAfEKiTaRFpZnfAfAbAr/AsxUYagiVCbeQgqhPpFYmukLCOrZupRNJUIB02BCAjAZCK+/Ed2LoJZgm6bzRfsCgMw3JWAXaEBpg8uIGSRPPMBQmXc+P+iggXCnoOQZUQK1K8PgEAjGcQs7QGL6FzG5mtkcAUiyYIQYcRRUkDTLEIWR1b4ixamQMPhrKUP3rx48eDNFXmwdyFiOthixgXqaTAnBcKpwRaOS6A6Mx78fwBVx/IAm8I/KsTGzAkWNHUyb7Ar/L8GNSlK3MCRev7/v+CApC7kBUoUoAX4yQ0nHjwAWqpiE6GNFgNDoAwHAKC2Q2lMNcCmAAAAAElFTkSuQmCC', + '6' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QNAObRd4vAAAAB3RJTUUH0wUOEDUc2lcB6wAAAAlwSFlzAAALEgAACxIB0t1+/AAAATtJREFUeNpj/M9AHGAiUh2Gwq2puryMjKKmmSfRVf5HBkcMEBI+L1CkUBROYUE2QuMFLoVr0CzzwKHwhQC6szZgV1gAtfHI/xs2mEYywsPxp8QHEMVxQ56B4aaJiIKIiIRCPDZf74DwI/5jB4hwPAChbAgG+BWoExlOxkoysuqW3sUV4BoQ/p0SqARLB44AF4HIByDMKMCuEIu7phCrUOADNl/DgMOJ/09SIMwPC7B5hgfC1/kB4kRAOC7YrFaByM0Ac85AOCLYrFaBhSMIQNPlG2wBDg3HP2CSGU/MuEAoiKVXUWxB9cwPiG8UwEGSg5FCMNOjwZ4/byqgpqwgMoWr/MGeZ1agqWPZgSNz/Z+AqnDCf1wK/29B8qbKDhQpRtTE8HfLjjMP3jDwKJh4hKCGJSPNC6lRhTRWCABWpdoxd/bZ4QAAAABJRU5ErkJggg==', + '7' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QNA18/fMoAAAAB3RJTUUH0wUOEDUVo4u5TwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAM9JREFUeNpj/M9AHGAiUt2oQnorZIGzGLFJIyJ40HqGhUiFPFuQ/YUFPBGBmLcDSQybwj8OEDOW/CegsAeiruQ/AYV3OMDqTP4QUugCceCN/wQUQn1a8Z+Awj8qYHUiHwgpXAAxcMJ/Qgp1wOoEPhBSuANiYM5/QgpjIAovEFL4gweszgAz0NASxZ4vYMqHYDKDBiIWhWhWa0CS1x9CVn+8AaYsmAlZfQRC6RDMChADGTQIKjxDrMI7EEoBi0JGlMJe8AOY+sFOSCEeQHQBAABCZ7xyT9fJhwAAAABJRU5ErkJggg==', + '8' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QNBeBnwpSAAAAB3RJTUUH0wUOEDUOKe5wowAAAAlwSFlzAAALEgAACxIB0t1+/AAAATVJREFUeNpj/M9AHGAiUt1AKmRB459cc+DBGwYWGQ2LEG1Umf/I4IELkozLA2QpFIUXJFDMEDiBQ+EHGTR3yHzArrAFwwct2BXqQGQ1zvw/owFh6mBXCDXmDJB5BsOrjEhxzfoHIgkiGCGB9xtrgEPtOwvEV6FWY4+ZAAgVc5LhZgKEGYI9wN+gBiPu4Pl/BFWlxA1cMfN/C0rUr8AVhX8K0KyuwaEwASNmarAqPACVTXnw/0oENBFewKYQGhYZYE4MVBM2hVAvQ1LhHQhHBVsUMjIgYhCdhy3PPASTd6GOxBYz0KhOQHajDjY3pkC1Rlz5fweqjqEAm8ILGK5gYLlDZICXYI+ZLzZo6gL+4EgUfyo4kJQJtCCpQ8kKQPB2zZ47L14AU5iMgUMAN7IM43AqHwdQIQAhMPz6Gz5V/wAAAABJRU5ErkJggg==', + '9' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QNCQ+T2tEAAAAB3RJTUUH0wUOEDUHUDLIBwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAUZJREFUeNpj/M9AHGAiUh26wr9rE3V5GRlFTTM3/kVT+R8Z7FBBSKjsQJFCUTiFBcWMCbgUHmBBs20FdoV/VNDUMQi8wapwDVS65s2fPToQZgFWhRFIkm8kwGyeH9gUQm2+Aua0QDhb4LJI4XgHQmmDSRMIZw+emIEENAeEcwObQhEIdQHiABRbUGPGBSIQAWL/gHqbB5tnJkC1Fjz5f8IGwxwkhR8EsCQarFE4hViF/wsQCgKgHsSu8H8HLFkUQL2rgUPh/zslOiwMEjFH/kND2geXQvQgqMAWhSjgAIRygAswIuXCpXfevHjz4M0ZdQaGhxo/wAnyBTuWmPnvARGxuPH/iAa+9Ph/A7r9Ai+wK/zvg6ZwzX8cCl9oICtjmfIfl8L/bwIQ6gyO/Met8P//EwUmwHTJo5OyBU2CkdaF1KhCWisEAM/sJxmZkdWnAAAAAElFTkSuQmCC', + 'A' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QFwy1U7TfAAAAB3RJTUUH0wUOEC0ZKCZtPQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAO1JREFUeNrt1LERwiAUBuAHZ2GRwsIypQMwQEZwgBQpM4QDZBSLFI7gCA5gQWGRdA5gkTuMSh48eMTUnq96wH98B+QiDCwruTD3D76qF676ueAp0Y9lSBXeSkFWaLAje3T+kkzK4SgpBzZw8pqxJWcdOJuRsyGPbWDk0tS20zw9SXsobdfytJVXdzNsP61i6Zt3K7Ht0UeUgbPdjsrOXMd+2IS2C2qb271HVWi7YANcNXFQsUEVBTXwNdl46jYRxPl52dnwRUZbhkLSDmS8DnxFRWiULxg8UxvobefuRR8ZQYDKtffVVcQWv/RrfgJC4bd0upw4MQAAAABJRU5ErkJggg==', + 'B' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGAusrz2zAAAAB3RJTUUH0wUOEC01Gv4B3gAAAAlwSFlzAAALEgAACxIB0t1+/AAAANJJREFUeNpj/M9AHGAiUh0tFTKiAUHL2rsoKv9DARZDWFr+IwA+hQwMFcQqZDhCrMIIYhWK4FYIYv8444PuV+wK//9/A+UJwBUSCHAL3OEIsdoFyttCpGdiiAtHjoY/RCnk6PlBbBRKrCE6CqcQq5DlDs5whIT3CgUI788EvOEIBCegXB2YPCNMBSNMISqf5TeUjysK90LpP/itfrFEAhZCMHkWdKMYUbk2MAah7BqD02pUYEFkgMu8IE6hD0IdpmegwSejoKLjoY7syaFU7A0HhQA2e4cJytImvAAAAABJRU5ErkJggg==', + 'C' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGBbPqVFqAAAAB3RJTUUH0wUOEC4BEGemqAAAAAlwSFlzAAALEgAACxIB0t1+/AAAASlJREFUeNpj/M9AHGAiUt2owkGrkAWV+3TDgRtPPjBwyGiYBOijSv1HAlcCkGUcTiDLISvsQDOeZQp2hQWYDpuCTeEEbD44ganwDgc2vxpgKoyAyUWc+f9hjgCMtwFd4RuYRxog/ueBcl3QFc6BSmj8gfBrwE40yFmCrjABqrAH5mSZgJ4jX7AEjwlU4Zn/OAAsrp9AaRlccc0IzdeMsBilOPWQrBDmtpfEKnwBpZ8qZq58i6IS6vscKHcBcgQYlOz4gh6OK6AKfaB8G5hN6Aq/wBLPHjB3CczCFIzUA0u2PD0v/j9pgaf1ExgK3wgwYAEOWFL4GizqWC5gyzM1mArnEJkLZ2DPhf//n3BAVmeDkq8ZUZPL3TUn7gBLCgYFBYsAcxQZRmKrDwABNsv9SJSDwwAAAABJRU5ErkJggg==', + 'D' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGC1+orhOAAAAB3RJTUUH0wUOEC4yr7fHvgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAM9JREFUeNpj/M9AHGAiUt1AKmSBsxiRhXlkNBxCpFFU/ocBTDMyPvxHADwKGRgUbhCpkEHiCZEKGRyIVciwArdCIPPFGg8YzwSvQiBogXFvEFD43wDKnQDl44yZGCh9glAU2sCsJqRQBkq/gMUw3G2wuP6PnU/H9PgRSgsQUvgESosQUngFSqsQUrgCSsNiCFcU7oBx9+CL6w8XamB5SeUPkelxAZEJ1+YPcQolXhCXFTTuEJULOUq+IOVrFgasQELBxMaHG1mEcTiVjwOoEADAIkCnGpmJKgAAAABJRU5ErkJggg==', + 'E' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGDeDwEE0AAAAB3RJTUUH0wUOEC8CkHXGUwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAD5JREFUeNpj/M9AHGAiUt2owkGrkAXGYMQqjUgJQ8EzpPsa05+D140oMYTk4KEQ4MMqZqgUhcM1czESW30AABfqB1XDnLzcAAAAAElFTkSuQmCC', + 'F' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGQe8AkDZAAAAB3RJTUUH0wUOEC8JB6cf2wAAAAlwSFlzAAALEgAACxIB0t1+/AAAADlJREFUeNpj/M9AHGAiUt3wUsiCYDJikUYE3lDwDDm+xvTp4HUjIoaQXTsUAnxYxcyoQryAcUSWuAAW/gZTg/yEMAAAAABJRU5ErkJggg==', + 'G' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGRFI1vWIAAAAB3RJTUUH0wUOEC8QY8y3GwAAAAlwSFlzAAALEgAACxIB0t1+/AAAASZJREFUeNpj/M9AHGAiUt0IVciCwvt7ZM+FOy8+MDBwSEho2AQII8v9R4A/U2RQtHEUfEBIIim8YYBhn8oNLAqP8GBxmcwbDIU3sKljYIhAV/jHgAE7uICmcAJMQqDmwp//D2YowPgxqAr/wPyr8QAi8EEHwleIQFW4BxYicG+eEHEomHECET5QhRVQhQn/cQFoFJ6AKgwgFNcPoFwdnAoZIXmGERahKDwkIdqlR1j4PiRW4RVCCmExvQenQrSYEXiDiAoUBfC4loAK23yBSnzArhCRehRmAJPFnRUxHDgU/lDA7zZECj/Cgl2dAkaeWYNVZcoHDIX/94hgKLM4gS27/v9QIICizGMDkiQjSon7c8eBCw+e/GFgkZEwsHCRRpZiHE5FMwCa2YE+WcAOUwAAAABJRU5ErkJggg==', + 'H' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGRw2Z4k1AAAAB3RJTUUH0wUOEC8agxleBQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAD1JREFUeNpj/M9AHGAiUt2oQvyABUozQml4+KMLDAXPDAWFLGh8RlwKh4JnaB88GOlxELhxVCFewDgEynAAN2sFVHAvevkAAAAASUVORK5CYII=', + 'I' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGSlg1E0WAAAAB3RJTUUH0wUOEC86uHd+zQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAD5JREFUeNpj/M9AHGAiUt1AKmRBMBkxJJE9OhQ8Q32FjGhxDQsjjCQwFDwzqnCwKkRKZqO5EBMwDqcSl2iFAMMeB0s/kLo2AAAAAElFTkSuQmCC', + 'J' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGywiiNsbAAAAB3RJTUUH0wUOEDAFw0tdbgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKdJREFUeNpj/M9AHGAiUh3xClmwijJCaSR3Ud/qUYWjCklTyIHEhifctw8ePHgCxO+B7L9QMQlsChW+QOiX4gwMd6BiItisVoHSB6AYWQwM/kNBBszkC/9PwKyc8B8B4Ar3YPHMHWwK/xtgqAv4j1XhEfScK/EEu8L/a1BVStz4j0Ph/yPItoe8QFH3nxGlkNq75cKDB0DDVBwitNEcwjhwpdmoQrwAAN6ioiFapgUdAAAAAElFTkSuQmCC', + 'K' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHAEoFhGpAAAAB3RJTUUH0wUOEDANzZDVXAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAPZJREFUeNpj/M9AHGAiUt2owgFSyAgFMOGDrDARxkKo0H8wYEDh/b/AAzepACqEVeEdCQx1WBW+0ICry/mPR+EXE7i6kD94FP5xwaYOi8IIrOowFRbA1Xkgq8NQ2ANXZ/PlPx6FS3CpQ1fIAmOIoKn7jxbXf2CMNxvQIxvVRAQQ+YDXaiSQQqxChiOEFGoIQGidP/gVStxogLI68CqUuPH/BzSVcTzAoxCo7v//ObBIxK0QrO7/H1iCXIFT4QkIFxbaMh9wKYQJO0D5OYQUnoDF/QkCCuHJ1+APAYV3YOloAgGF8JTO84SAwjfQiGQIgPAZqV4rAACnKSarzdlc4gAAAABJRU5ErkJggg==', + 'L' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHA64qQw4AAAAB3RJTUUH0wUOEDAXMPIsJgAAAAlwSFlzAAALEgAACxIB0t1+/AAAADlJREFUeNpj/M9AHGAiUt2QUMiCYDJCaezhMBQ8M6pwVCEdFLJgCjEisRH5Zyh4hvoKGUdkQUq0QgARaARRV9jUFQAAAABJRU5ErkJggg==', + 'M' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHBhMfblpAAAAB3RJTUUH0wUOEDAqaJpgNwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAPNJREFUeNrdlK0OgzAUhS8bCQYxMYmcmEAgEAgejQfZQyG2pAIxOYlATkAu691o2tvSYia2iv7lyzn3NG0jhG1tt5H7Aggom7ZuaKPhBFqKV+pFWDGjjcxStEAYXuvBkrKtoVX+gdRiK9i6sxjgeVGUMJzWwZLACaZOTqoAOAronmrlBuvPkQsIgHn8BqnE2AMmhaaYJ57jqTRFMwsDyW249XaJLhAujizm7UFM5XCUXTqiTvBLQYWRc7H3WWt+3NmlyGbOGh9q/45mjQxUb+CA6A2jSqu5MweX0ooQWLJxLYx6fz0GwmBOsww5GP3At/dX4Ayb7qpFI9y5ygAAAABJRU5ErkJggg==', + 'N' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHC6DxyzwAAAAB3RJTUUH0wUOEDAye/b4YQAAAAlwSFlzAAALEgAACxIB0t1+/AAAALRJREFUeNpj/M9AHGAiUt0IV8gIARsRMlAROP8/BEB5Ii/+/0cVgXNRhRk8iFXIMIFYhRxXiFTIYPCDSIUMBcQqZNhDrEKZN0QqZAggViHDHIIKRSAUzx1CCrdAaZM/BBT+z4Eyaggp/KEDYbAcIaDw/wUWCEuBkML/PagBgFvhfxdiFT4RIVLh/zXEKvyfQqzCLypEKvx/hoVIhf9biFX4x4ZIhf8fCBCp8P8KNBHG4VQ0AwDEOyeZhO5p1AAAAABJRU5ErkJggg==', + 'O' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHQExDSDoAAAAB3RJTUUH0wUOEDA4myMRfwAAAAlwSFlzAAALEgAACxIB0t1+/AAAATtJREFUeNpj/M9AHGAiUt3wUsiCyv265ciZJ08YGGRkDGwCuFGk/iOBDwU8SDIcGS+Q5JAV7hBBs45nAVaFC1gwXTYBi8IdWNQxMCzAUPhBBJs6Bp4n6AoLYFI6az78f7NEB8ZNQFP4QwAqEfADwg+A+f0NqsI1UHGBDzCnSKC6EhYzB6B0Cj+UwZ+CKgNTeAZKu8C94QGlL6DGjAyU+wAeXC+gIiIQLiM0KzDC9CFCBlWICsnsL3aFMDc+hcs8QZWBKYSF2g24whvYFZpA6T1whUegNCwyoYGxAmYyLGZ+wOxYghqFX2BpO+APmP8nBspHj2uk1LPizf8PGyxgXPTUQ3x6JDqF//8/AYs6bHkGmCYF0O3FnguBCSaFA0kZS8IDJDlG1IIUVFK8eABMWzI6DgHCyDKMI7LEBQCD5YgI9wbKGgAAAABJRU5ErkJggg==', + 'P' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHQvR2Mn2AAAAB3RJTUUH0wUOEDEDMzPJGgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKVJREFUeNpj/M9AHGAiUh05ChlRAKdu4k5Ulf9hANMQiwf/EQCfQgaJB0QqZHAhViHDEbg0AV8vwRM8QN0v5vBAOSfw+BrMWQDl8MClGeEKGGEKQcRXHmQemTGD1RMy+N14o4MDyvGAS7NgGMaIzPHAYyIy4HhBZMy0EBmFIX+IUsjRgqQOi2fAgEVBwyVGGEUEQw2O3EbLzDWSFDIOhtJsVCEWAAC/Yt2X+2PYcgAAAABJRU5ErkJggg==', + 'Q' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHRxSC0wxAAAAB3RJTUUH0wUOEDEKSu9xvgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAW1JREFUeNpj/M9AHGAiUt2QUMiCzPm65cCZF08YGGRkDBx8uNFU/oeDDwU8SOIcBS/+IwOEwh0iaEYIrMCqcA4LprsmYFG4A4s6BoYFGAo/iGBTx8DzAl1hAUxKZ8WH/29W6MC4KWgKfwhAJXx+gPl/QmB+/4KqcANUXOQDVPiLBFRkCUwhJGb2wGzihzK4U6CMA6hReAbKc4F7wwFKX0CNGRkoB+HJJ1ARGZgAIziFM8J0IUIGXYjMZPaXkEJYYDyBiz+EuRFVoQKUdwWIz6qWvmRguAMVkUBVaIIUalPu9GgshIefAWrwrIHp//L/DQc4KjFiBi2uQ/7832KB5AX0uP5fAZOx2PDhfwNCIXrq+f9BhgEb4HmCkcL3YE3hSHkBnmfWYFMpsoaYXAgGDgcwFKLlaxYOCG2DqRCYrldkmIACUMIgZsaTI5Cg3IBNISp4AoovlT+EFf7/kYPkb3wK//8/YAGPGcYhUIYDAHBC9Yak1w7iAAAAAElFTkSuQmCC', + 'R' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHSkEuIgSAAAAB3RJTUUH0wUOEDEUsOBM3QAAAAlwSFlzAAALEgAACxIB0t1+/AAAAOZJREFUeNpj/M9AHGAiUh0NFLJAaUY0YRkJHYcQdmSh/xCAzRCZHf8RAJ9CBpYNRCpkEHgBV4jfMx+mEOVGIDDAaTWY82aPBZTLgV8hUCkaH6cbP8B8gxHgyODjgwstMDfiVIgWQyFE+lrhB3EBznOFuJgxuUFMXPPEbPmDpA53FH55osKMIoAe4F826MDMvPMfj9WgWFGBBeIf/Ar/H4FxJhBQ+B8WzCIfCCi8A4uvBgIK/2fA/POCgMIXHFBuDqH02ABLM3cIKPwgAuVHEFD4fwJM4AIBhT9goe4AFWAcAsXesFIIAEvJyZHTCSiTAAAAAElFTkSuQmCC', + 'S' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHTRnvuTLAAAAB3RJTUUH0wUOEDEbIF9RTAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAVZJREFUeNpj/M9AHGAiUt2oQvyABYX398CWK3de/GBgkVEw8HFgRpH7jwSWqCDLyCxAlkNS+CcG3boY7AozMB3Wgk3hGSw+4HgBl0b4egIWhT9mYPGMBFQg4MH/D2tgvrKASzPC0yMjlP7CDSTOmrDIMDDwiHBsxzSRBypw5j9WgFDoAPNAxIQjX/ApXIDsC4OCLV9wKfzjwIACOEIO4IiZFxbooePzAqvC/z9qONBUStzAqvD//zc9BqgqNX5gVwgETxbkmCClvSk4FYLdsCMCptAGI2YSGV78+PLmz5MX4mDu1ByIMM9n9JiBxe4caGChy8MZMMsUIEFyAMoVwVC4BGaEwpI3/9/MEYGlJQyFPwQYsIE1mL7GlnCR0iNSXLtgqpO4gy1mvtigq1NAxCBKgP9pEUFWxlOCnNIYUYrmn3v23Ljx5gsw88sYOPhwI0sxDoEyHAABtSc836a1EQAAAABJRU5ErkJggg==', + 'T' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHgUdTbcyAAAAB3RJTUUH0wUOEDEgkVS4aAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADdJREFUeNpj/M9AHGAiUt0IVcgCpRlxyMODeSh4hmiFjGipB+Z7jEQ1FDwzqnBU4WBSyDicimYAb/AFTaJpyH8AAAAASUVORK5CYII=', + 'U' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHhEHl2NPAAAAB3RJTUUH0wUOEDEon48wWgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKlJREFUeNpj/M9AHGAiUh3xClmgNCOUhrsEXYD6Vo8qHFVIuUIVKP0USr+E0jLoCjWg9A4ovQVNHJjUIaADZsILMPeFApRfA5X/D1N4AaZRYc6b/2+WwNQxXEBX+N8Bqxcc/mMoPMGCRR3LBUyF/2dgUTjjPxaF/6egm8ky5T9Whf9P2KCoMziBJPefEaWQurjnzIMXL34wsMhoWHiYo2hjHLjSbFQhXgAAKzejCLAOcVMAAAAASUVORK5CYII=', + 'V' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHh/gL05IAAAAB3RJTUUH0wUOEDEuduyVbwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAVNJREFUeNpj/M9AHGAiUt2owoFRaMgIAYlIMqlQMUMo/z8ITIByRP78hwMRqNgECBei8AULVPQIXN0RqAjLGwgfYrW4B1R4DdzmLVDaQxjZ6v8roDwVuIkqMK3/ka3+/0MAKn4FKn4D5uof/5GtZmCPgEpsQHNDBDsDitVwt5tA+RZQ/pn/qFYj3PQEzHsC5WnA3QyPmQQU3+5AE0VYDTfDBcxzgQbik/8YVv93gMp9AbK/cEAD8T+m1TBb/oD8veEHhs0IE2GmxADZMRAmz4//WKxGkv3DA2Gm/MeqcA/Ujj1w1hHsCv/LQKQz/megRzyawgqIvAxMRwsuhbCEAEvGT3AphEUwNCU5IEv9R8lcUH9/wAxE5HAEgjccSBI8X3CbKOyBxAnhxm3i/w1IEgdQZFA98/+PCFydDKo6VKsZmGPQ0wgOq/+fgYvfQTORkeq1AgCIAvD7+THsDgAAAABJRU5ErkJggg==', + 'W' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QFhZRKnzkAAAAB3RJTUUH0wUOEDIR66frkQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAXNJREFUeNrtlK1ywkAUhZdMZsJMKyIqKhAIBAKBiEBEVCDyCJV9iIo+Do9QGRERgUBEVCAqKhAIREVERURnTvfn3t27xSA6g+kOQ/ZkP/aec5NlBHXZSC7k/sE/AhUwoVkDPQ58/2RUQ2IC6B1XpN7MV8tg62/pUdjSDO7OwR2J0pbekpqZYlMG50bNSGwBDQ4pyV5YtCZ7mqZf1mO2IN2Jynba0XRx49pThjQCbEKWFfVRpIlBzlK4PuLdpxEWlTr4LHvYMEDOaTYS3HCW3DAJt8mmaSXYchZbOfEzkyYGZRbrEbX8qe7GMpLqFeyxV9F4fon1pwcxjxbqJpJTBPBJLoyHYSz1I3xq78aOMssepHZZHFjKhbX9/AZd6e9bsdABeyHTQXiE2PLO6PugCwiP/r1QVLYSlpXwKE1Wno7b7jY+hoWj0aegPyA9+jPrzgqwZJ0j8hhMVtElmDoD19FFPAvamc+sOXBm+KdYEzC63p/9D7Tr72kj/8qjAAAAAElFTkSuQmCC', + 'X' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHi/G9n7kAAAAB3RJTUUH0wUOEDIXAsROpAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAT9JREFUeNpj/M9AHGAiUt3IVhjKCAFr4RJroSKBMIH/YPBEAMITeQLh//8gAxHggQlAFf6fAdXnA+WnQPkT/qMp/O8AlVkA5h2A8kz+YCi8wQGREngA5PxQgXBYzvzHUPi/A2qIA5BdAmUX/Mei8I8BVHbK/wssEJbMB2wK/5+ASvPcgGlZ8x+rQriFAmghgKHwiwJKXPA8wKXw/x4UhT3/cSr8n4CkzuAPHoVvRODqWE6gyPxHTT1ffiAUCjCgAhRtDkgSFnisnoJixAScCh/wEBk8DmiucsChcA5MQQSMMQWrQlgiZ0iAByey5QiFPlBZnS//v+hgxjZc4QKYKVeAnCswby3AUAi3eAGKNoEn6Ap94A5EjXUfNIUrEA6EALgzl6AohCUGsAMhAOZMkTfICkMw3I5wZgiEyzicimYAFRFkVwgDfJ0AAAAASUVORK5CYII=', + 'Y' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHjkyIsu1AAAAB3RJTUUH0wUOEDIkvRQvsgAAAAlwSFlzAAALEgAACxIB0t1+/AAAANJJREFUeNrt1L0NgzAQBWAcUVB6AAZgBAoKhmAICoZgCAoKxmAECkbwABSUlBRILwF8duwYhFJEihJ37+6T5T9g8K6N20X3FdDDNjKKOeTIqZLtWcKBU73bCx1lPhgQNTWieY1zRLmGCZFQp1xTSSmBDUUgW754BF+GQLxAPUkMxMb0FlzUsqpKLXhxQPRqo+oIerggCvuMC7jhFJounA4gWhO2OIL6Jp/uzglHrh0fTyAaDRucQaTkUpxDQVBYDWZ/hYze6bsv/A8/DNlP/kgvwzuer4kCMGPZDgAAAABJRU5ErkJggg==', + 'Z' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHwfqWOdfAAAAB3RJTUUH0wUOEDIrLasyIwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAL5JREFUeNrl1C0OwkAQBWCWQIJEVPQIFT0GAlHBMRBIBKIHqahAIDlERY9R0UOs3ORh5qVLunmp5GfUZvczbzKzDqtltV7ofgtueHCp16h33xBGwn0KYqoTO/J868Csaj418e0cPujOkLDfmTsECcfcXOGhoC/NZQMUDBUDd5DwxiAtJGzprpCw48xVQcIhM1d6KOgLc/kIBcORgXtIeGGQOyRs6Oq0g7P92YbkRE7bRZhcwhh+6nLF5f7yx30B8Z7FgxzMWtEAAAAASUVORK5CYII=', + ); + + return $_png; + } + + // Function get_rgb by Frank Burian + // http://www.phpfuncs.org/?content=show&id=46 + function get_rgb($hex) { + $hex_array = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, + 'F' => 15); + $hex = str_replace('#', '', strtoupper($hex)); + if (($length = strlen($hex)) == 3) { + $hex = $hex{0}.$hex{0}.$hex{1}.$hex{1}.$hex{2}.$hex{2}; + $length = 6; + } + if ($length != 6 or strlen(str_replace(array_keys($hex_array), '', $hex))) + return NULL; + $rgb['r'] = $hex_array[$hex{0}] * 16 + $hex_array[$hex{1}]; + $rgb['g'] = $hex_array[$hex{2}] * 16 + $hex_array[$hex{3}]; + $rgb['b']= $hex_array[$hex{4}] * 16 + $hex_array[$hex{5}]; + return $rgb['r'].','.$rgb['g'].','.$rgb['b']; + } + + // Function gdVersion by Hagan Fox + // http://de3.php.net/manual/en/function.gd-info.php#52481 + function gdVersion($user_ver = 0) + { + if (! extension_loaded('gd')) { return; } + static $gd_ver = 0; + // Just accept the specified setting if it's 1. + if ($user_ver == 1) { $gd_ver = 1; return 1; } + // Use the static variable if function was called previously. + if ($user_ver !=2 && $gd_ver > 0 ) { return $gd_ver; } + // Use the gd_info() function if possible. + if (function_exists('gd_info')) { + $ver_info = gd_info(); + preg_match('/\d/', $ver_info['GD Version'], $match); + $gd_ver = $match[0]; + return $match[0]; + } + // If phpinfo() is disabled use a specified / fail-safe choice... + if (preg_match('/phpinfo/', ini_get('disable_functions'))) { + if ($user_ver == 2) { + $gd_ver = 2; + return 2; + } else { + $gd_ver = 1; + return 1; + } + } + // ...otherwise use phpinfo(). + ob_start(); + phpinfo(8); + $info = ob_get_contents(); + ob_end_clean(); + $info = stristr($info, 'gd version'); + preg_match('/\d/', $info, $match); + $gd_ver = $match[0]; + return $match[0]; + } + +} + +// define('ENANO_CAPTCHA_BIGNFAT', ''); + +?> diff -r 902822492a68 -r fe660c52c48f includes/captcha/fonts/assimila.ttf Binary file includes/captcha/fonts/assimila.ttf has changed diff -r 902822492a68 -r fe660c52c48f includes/captcha/fonts/elephant.ttf Binary file includes/captcha/fonts/elephant.ttf has changed diff -r 902822492a68 -r fe660c52c48f includes/captcha/fonts/swash_normal.ttf Binary file includes/captcha/fonts/swash_normal.ttf has changed diff -r 902822492a68 -r fe660c52c48f includes/captcha/fonts/trekker_regular.ttf Binary file includes/captcha/fonts/trekker_regular.ttf has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/admin-menu.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/admin-menu.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,161 @@ +/* + * Midget - an open source wiki-like CMS + * Copyright (C) 2006 Dan Fuhry + * + * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * Some code found in this script is not licensed under the GNU General Public License; however, it is believed that the license terms shown + * below are GPL-compatible. If you believe that this is not the case, please drop a note to support@midget.no-ip.org. + */ + +/* + * Title: Tigra Tree + * Description: See the demo at url + * URL: http://www.softcomplex.com/products/tigra_tree_menu/ + * Version: 1.1 + * Date: 11-12-2002 (mm-dd-yyyy) + * Notes: This script is free. Visit official site for further details. + * + * There is no license fee or royalty fee to be paid at any time for using the Tigra Tree Menu v1.x + * You may include the source code or modified source code within your own projects for either personal or commercial use but excluding the restrictions outlined below. The following restrictions apply to all parts of the component, including all source code, samples and documentation. + * + * - Header block of script file (tree.js) CAN NOT be modified or removed. + * - The above items CAN NOT be sold as are, either individually or together. + * - The above items CAN NOT be modified and then sold as a library component, either individually or together. + */ + +function tree (a_items, a_template) { + + this.a_tpl = a_template; + this.a_config = a_items; + this.o_root = this; + this.a_index = []; + this.o_selected = null; + this.n_depth = -1; + + var o_icone = new Image(), + o_iconl = new Image(); + o_icone.src = a_template['icon_e']; + o_iconl.src = a_template['icon_l']; + a_template['im_e'] = o_icone; + a_template['im_l'] = o_iconl; + for (var i = 0; i < 64; i++) + if (a_template['icon_' + i]) { + var o_icon = new Image(); + a_template['im_' + i] = o_icon; + o_icon.src = a_template['icon_' + i]; + } + + this.toggle = function (n_id) { var o_item = this.a_index[n_id]; o_item.open(o_item.b_opened) }; + this.select = function (n_id) { return this.a_index[n_id].select(); }; + this.mout = function (n_id) { this.a_index[n_id].upstatus(true) }; + this.mover = function (n_id) { this.a_index[n_id].upstatus() }; + + this.a_children = []; + for (var i = 0; i < a_items.length; i++) + new tree_item(this, i); + + this.n_id = trees.length; + trees[this.n_id] = this; + + for (var i = 0; i < this.a_children.length; i++) { + document.write(this.a_children[i].init()); + this.a_children[i].open(); + } +} +function tree_item (o_parent, n_order) { + + this.n_depth = o_parent.n_depth + 1; + this.a_config = o_parent.a_config[n_order + (this.n_depth ? 2 : 0)]; + if (!this.a_config) return; + + this.o_root = o_parent.o_root; + this.o_parent = o_parent; + this.n_order = n_order; + this.b_opened = !this.n_depth; + + this.n_id = this.o_root.a_index.length; + this.o_root.a_index[this.n_id] = this; + o_parent.a_children[n_order] = this; + + this.a_children = []; + for (var i = 0; i < this.a_config.length - 2; i++) + new tree_item(this, i); + + this.get_icon = item_get_icon; + this.open = item_open; + this.select = item_select; + this.init = item_init; + this.upstatus = item_upstatus; + this.is_last = function () { return this.n_order == this.o_parent.a_children.length - 1 }; +} + +function item_open (b_close) { + var o_idiv = get_element('i_div' + this.o_root.n_id + '_' + this.n_id); + if (!o_idiv) return; + + if (!o_idiv.innerHTML) { + var a_children = []; + for (var i = 0; i < this.a_children.length; i++) + a_children[i]= this.a_children[i].init(); + o_idiv.innerHTML = a_children.join(''); + } + o_idiv.style.display = (b_close ? 'none' : 'block'); + + this.b_opened = !b_close; + var o_jicon = document.images['j_img' + this.o_root.n_id + '_' + this.n_id], + o_iicon = document.images['i_img' + this.o_root.n_id + '_' + this.n_id]; + if (o_jicon) o_jicon.src = this.get_icon(true); + if (o_iicon) o_iicon.src = this.get_icon(); + this.upstatus(); +} + +function item_select (b_deselect) { + if (!b_deselect) { + var o_olditem = this.o_root.o_selected; + this.o_root.o_selected = this; + if (o_olditem) o_olditem.select(true); + } + var o_iicon = document.images['i_img' + this.o_root.n_id + '_' + this.n_id]; + if (o_iicon) o_iicon.src = this.get_icon(); + get_element('i_txt' + this.o_root.n_id + '_' + this.n_id).style.fontWeight = b_deselect ? 'normal' : 'bold'; + + this.upstatus(); + return Boolean(this.a_config[1]); +} + +function item_upstatus (b_clear) { + window.setTimeout('window.status="' + (b_clear ? '' : this.a_config[0] + (this.a_config[1] ? ' ('+ this.a_config[1] + ')' : '')) + '"', 10); +} + +function item_init () { + var a_offset = [], + o_current_item = this.o_parent; + for (var i = this.n_depth; i > 1; i--) { + a_offset[i] = ''; + o_current_item = o_current_item.o_parent; + } + return '
' + (this.n_depth ? a_offset.join('') + (this.a_children.length + ? '' + : '') : '') + // CODE MODIFICATION + // removed: + // + '' + this.a_config[0] + '
' + (this.a_children.length ? '' : ''); + // added: + + '' + this.a_config[0] + '' + (this.a_children.length ? '' : ''); +} + +function item_get_icon (b_junction) { + return this.o_root.a_tpl['icon_' + ((this.n_depth ? 0 : 32) + (this.a_children.length ? 16 : 0) + (this.a_children.length && this.b_opened ? 8 : 0) + (!b_junction && this.o_root.o_selected == this ? 4 : 0) + (b_junction ? 2 : 0) + (b_junction && this.is_last() ? 1 : 0))]; +} + +var trees = []; +get_element = document.all ? + function (s_id) { return document.all[s_id] } : + function (s_id) { return document.getElementById(s_id) }; + + diff -r 902822492a68 -r fe660c52c48f includes/clientside/css/enano-shared.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/css/enano-shared.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,400 @@ +/* + * Shared stuff that all Enano themes (should) use + */ + +/* Information, warning, question, error, and wait boxes */ +div.error-box { background-image: url(../../../images/error.png); background-repeat: no-repeat; background-color: #FFF4F4; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 0; min-height: 25px; } +div.info-box { background-image: url(../../../images/info.png); background-repeat: no-repeat; background-color: #F4F4FF; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 0; min-height: 25px; } +div.warning-box { background-image: url(../../../images/warning.png); background-repeat: no-repeat; background-color: #FFFFF4; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 0; min-height: 25px; } +div.question-box { background-image: url(../../../images/question.png); background-repeat: no-repeat; background-color: #F4FFF4; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 0; min-height: 25px; } +div.wait-box { background-image: url(../../../images/wait.png); background-repeat: no-repeat; background-color: #FFF4FF; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 0; min-height: 25px; } + +/* toolbar */ +div.toolbar { + border-bottom: 1px solid #909090; + background-color: #D0D0D0; + padding: 2px 0; + height: 22px; + font-family: arial, sans-serif; + font-size: 8pt; +} +div.toolbar ul { + margin: 0; + padding: 0; +} +div.toolbar ul li { + list-style: none; + margin: 0; + float: left; +} +div.toolbar a img { + opacity: 0.6; + /*filter: alpha(opacity=60);*/ +} +div.toolbar a:hover img { + opacity: 1; + /*filter: alpha(opacity=100);*/ +} +div.toolbar a { + display: block; + padding: 2px; + border: 1px solid transparent; + cursor: default; + width: auto; + color: #000000; + margin: 0 2px; + max-height: 16px; + text-decoration: none; +} +div.toolbar a:hover { + border: 1px solid #202090; + background-color: #ceceed; + color: #000000; + text-decoration: none; +} +div.toolbar a:active { + border: 1px solid #A0A0A0; + background-color: #E0E0E0; +} +div.toolbar img { + margin: 0; + padding: 0; + display: inline; + border-width: 0px; +} +div.toolbar a span { + position: relative; + top: -4px; +} +div.toolbar li span { + padding-left: 2px; + padding-right: 5px; +} + +/* vertical toolbar */ +div.toolbar_vert { + border: 1px solid #909090; + background-color: #D0D0D0; + padding: 2px 0; +} +div.toolbar_vert ul { + margin: 0; + padding: 0; +} +div.toolbar_vert ul li { + list-style: none; + margin: 0; +} +div.toolbar_vert a img { + opacity: 0.6; + /*filter: alpha(opacity=60);*/ +} +div.toolbar_vert a:hover img { + opacity: 1; + /*filter: alpha(opacity=100);*/ +} +div.toolbar_vert a { + display: block; + padding: 2px; + border: 1px solid transparent; + cursor: default; + width: auto; + color: #000000; + margin: 0 2px; + max-height: 16px; + text-decoration: none; +} +div.toolbar_vert a:hover { + border: 1px solid #202090; + background-color: #ceceed; + color: #000000; + text-decoration: none; +} +div.toolbar_vert a:active { + border: 1px solid #A0A0A0; + background-color: #E0E0E0; +} +div.toolbar_vert img { + margin: 0; + padding: 0; + display: inline; + border-width: 0px; +} +div.toolbar_vert a span { + position: relative; + top: -4px; +} +div.toolbar_vert li span { + padding-left: 2px; + padding-right: 5px; +} + +/* Tables */ +.tblholder { margin: 10px 0 0 0; padding: 0; border: 1px solid #AAAAAA; background-color: #E8E8E8; } + +/* The beautiful tables inside what may not obviously be mdg-comment divs */ +div.tblholder td.row1 { padding: 4px; background-color: #E0E0E0; } +div.tblholder td.row2 { padding: 4px; background-color: #F0F0F0; } +div.tblholder td.row3 { padding: 4px; background-color: #E8E8E8; } +div.tblholder th { padding: 4px; background-color: #7080A0; font-weight: bold; text-align: center; color: #FFFFFF; } +div.tblholder th.subhead { padding: 4px; background-color: #90A0B0; font-weight: bold; text-align: center; color: #FFFFFF; } +div.tblholder table { background-color: #FFFFFF; width: 100%; } + +/* Well, not Midget and not comments (usually), but that's what the class is called ;-). Basically an informational window or used as a wrapper for tables. */ +.mdg-comment, .mdg-infobox { margin-left: 1em; padding: 7px; border: 1px solid #AAAAAA; background-color: #E8E8E8; } + +/* JWS window theming */ +div.jswindow { border: 2px solid #7090B0; border-top: 5px solid #7090B0; padding: 0px; font-family: Trebuchet MS, tahoma, verdana, arial, sans-serif; font-size: 9pt; display: none; position: absolute; background-color: #FFFFFF; } +div.titlebar { background-color: #7090B0; color: #FFFFFF; font-family: Trebuchet MS, tahoma, verdana, arial, sans-serif; font-size: 9pt; padding-bottom: 4px; cursor: default; } +div.titlebar div.closebtn { width: 16px; height: 16px; border: 1px solid #B0D0F0; background-color: #90B0D0; display: block; } +div.titlebar div.closebtn:hover { width: 16px; height: 16px; border: 1px solid #FFFFFF; background-color: #B0D0F0; display: block; } +div.titlebar table, div.titlebar td { margin: 0; padding: 0; } +div.jswindow div.content { padding: 10px; margin: 0; background-color: #FFFFFF; } + +/* Search results */ +div.search-result h3 { font-size: 14pt; margin: 10px 0 0 0; } +div.search-result h3 a { color: blue !important; font-weight: normal; padding-bottom: 0; } +div.search-result p { margin: 10px 0 0 0 !important; font-family: arial, helvetica, sans-serif; font-size: 10pt; } +div.search-result span.search-result-info { color: green; } +div.search-result span.search-term, div.search-result span.title-search-term { background-color: #FFFFC0; font-weight: bold; } + +/* + * Search box + */ + +input.js-search-box { + font-size: 13px; + margin: 0; + padding: 1px !important; + background-image: url(../../../images/search-box-normal.gif); + height: 15px; + background-repeat: repeat-x; + border-width: 1px; + border-style: solid; + border-color: #6c6c6c; + color: #C0C0C0; +} + +input.js-search-box:focus { + background-image: url(../../../images/search-box-hilite.gif); + color: #666; +} + +div.js-search-submit { + display: block; + position: absolute; + width: 24px; + height: 19px; + font-size: 1px; + line-height: 19px; + clip: rect(0px, 24px, 19px, 0px); + overflow: hidden; + margin: 0; + padding: 0; + background: transparent url(../../../images/search-btn-normal.png) no-repeat !important; + background-repeat: no-repeat; + cursor: pointer; +} + +div.js-search-submit:hover { + background-image: url(../../../images/search-btn-hilite.png); +} + +input[type ^="text"].username, input[type ^="password"].password { + padding: 2px 2px 2px 27px; + width: 96px; + height: 15px; + border: 0px none #000; + font-size: 11px; +} +input[type ^="text"].username { + background-image: url(../../../images/login-username.png); +} +input[type ^="password"].password { + background-image: url(../../../images/login-password.png); +} + +/* + * jBox menu system + */ + +div.menu, div.menu_nojs { + background-color: #D0D0D0; + border: 1px solid #A0A0A0; + font-size: 9pt; +} +div.menu a, div.menu div.label { + padding: 2pt 5px; + text-decoration: none; + display: block; + float: left; + color: #404040; +} +div.menu_nojs a, div.menu_nojs div.label { + padding: 2pt 5px; + text-decoration: none; + display: block; + color: #404040; +} +div.menu div.label, div.menu_nojs div.label { + color: #101010; +} +div.menu span.sep, div.menu_nojs span.sep { + display: block; + float: left; + width: 5px; +} +div.menu div.multopts, div.menu_nojs div.multopts { + line-height: 17pt; +} +div.menu div.multopts a, div.menu div.multopts div.label, div.menu_nojs div.multopts a, div.menu_nojs div.multopts div.label { + float: none; + display: inline; +} +div.menu a:hover, div.menu_nojs a:hover { + color: #FFFFFF; + background-color: #808080; +} +div.menu input[type ^="text"], div.menu input[type ^="password"], div.menu_nojs input[type ^="text"], div.menu_nojs input[type ^="password"] { + border-width: 0; + font-size: 9pt; + padding: 4px 5px; + max-width: 70px; + background-color: #E0E0E0; +} +div.menu input[type ^="text"]:hover, div.menu input[type ^="password"]:hover, div.menu_nojs input[type ^="text"]:hover, div.menu_nojs input[type ^="password"]:hover { + background-color: #E8E8E8; +} +div.menu input[type ^="text"]:focus, div.menu input[type ^="password"]:focus, div.menu_nojs input[type ^="text"]:focus, div.menu_nojs input[type ^="password"]:focus { + background-color: #F0F0F0; +} +div.menu input[type ^="button"], div.menu input[type ^="submit"], div.menu_nojs input[type ^="button"], div.menu_nojs input[type ^="submit"] { + border-width: 0; + font-size: 9pt; + padding: 3px 5px; + max-width: 70px; +} +div.menu a.current, div.menu a.current:hover, div.menu_nojs a.current, div.menu_nojs a.current:hover { + color: #FFFFFF; + background-color: #505050; +} +div.menu ul { + display: none; + position: absolute; + padding: 0; + margin: 0; + background-color: #D0D0D0; + border: 1px solid #A0A0A0; + min-width: 120px; +} +div.menu_nojs ul { + display: block; + clear: both; +} +div.menu ul li, div.menu_nojs ul li { + list-style: none; +} +div.menu ul a, div.menu_nojs ul a { + float: none; + margin: 0; +} +span.menuclear { + font-size: 1px; + height: 0px; + width: 0px; + clear: left; + line-height: 0px; + display: block; +} + +/* + * Docking Boxes code (for the sidebar editor) + */ + +/* group container(s) */ +#sbedit { + margin: 0; + padding: 0; + /* position:relative; /* additional outer containers must also have position:relative */ +} +/* keyboard navigation tooltip */ +.dbx-tooltip { + display:block; + position:absolute; + margin:36px 0 0 125px; + width:185px; + border:1px solid #000; + background:#ffd; + color:#000; + font:normal normal normal 0.85em tahoma, arial, sans-serif; + padding:2px 4px 3px 5px; + text-align:left; + } +* html .dbx-tooltip { width:195px; } + +/* use CSS2 system colors in CSS2 browsers + but not safari, which doesn't support them */ +*[class="dbx-tooltip"]:lang(en) { + border-color:InfoText; + background:InfoBackground; + color:InfoText; + font:small-caption; + font-weight:normal; + } +/* additional clone styles */ +.dbx-clone { + opacity: 0.8; +} +.dbx-content ul { + margin: 0; padding: 0; + list-style: none; +} +.dbx-content li a, .dbx-content li a:hover { + text-decoration: none; color: #666; +} +.dbx-content2 { + margin: 0px 1px 0px 1px; +} + +/* Progress bars */ +div.progressbar { + padding: 2px; + background-color: #90A0B0; + width: 308px; +} +div.progressbar_inner { + min-width: 30px; + color: white; + background-color: #7080A0; + padding: 4px; +} +/* User notification - courtest of wikipedia.org (not sure if this is included with MediaWiki) */ +/* user notification thing */ + +.usermessage { + background-color: #ffce7b; + border: 1px solid #ffa500; + color: black; + margin: 10px 0 1em; + padding: .5em 1em; + vertical-align: middle; +} +.usermessage a:link, .usermessage a:active, .usermessage a:visited { + color: #CA7520; +} +.usermessage a:hover { + color: #AA5500 !important; +} +div.thumbnail { + display: table; + border: 1px solid #AAAAAA; + background-color: #F0F0F0; + padding: 4px; + margin-bottom: 10px; +} +div.thumbnail-inner { + background-image: url(../../../images/thumbnail.png); + background-position: top right; + background-repeat: no-repeat; +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/css/enano-shared.css~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/css/enano-shared.css~ Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,401 @@ +/* + * Shared stuff that all Enano themes (should) use + */ + +/* Information, warning, question, error, and wait boxes */ +div.error-box { background-image: url(../../../images/error.png); background-repeat: no-repeat; background-color: #FFF4F4; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 0; min-height: 25px; } +div.info-box { background-image: url(../../../images/info.png); background-repeat: no-repeat; background-color: #F4F4FF; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 0; min-height: 25px; } +div.warning-box { background-image: url(../../../images/warning.png); background-repeat: no-repeat; background-color: #FFFFF4; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 0; min-height: 25px; } +div.question-box { background-image: url(../../../images/question.png); background-repeat: no-repeat; background-color: #F4FFF4; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 0; min-height: 25px; } +div.wait-box { background-image: url(../../../images/wait.png); background-repeat: no-repeat; background-color: #FFF4FF; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 0; min-height: 25px; } + +/* toolbar */ +div.toolbar { + border-bottom: 1px solid #909090; + background-color: #D0D0D0; + padding: 2px 0; + height: 22px; + font-family: arial, sans-serif; + font-size: 8pt; +} +div.toolbar ul { + margin: 0; + padding: 0; +} +div.toolbar ul li { + list-style: none; + margin: 0; + float: left; +} +div.toolbar a img { + opacity: 0.6; + /*filter: alpha(opacity=60);*/ +} +div.toolbar a:hover img { + opacity: 1; + /*filter: alpha(opacity=100);*/ +} +div.toolbar a { + display: block; + padding: 2px; + border: 1px solid transparent; + cursor: default; + width: auto; + color: #000000; + margin: 0 2px; + max-height: 16px; + text-decoration: none; +} +div.toolbar a:hover { + border: 1px solid #202090; + background-color: #ceceed; + color: #000000; + text-decoration: none; +} +div.toolbar a:active { + border: 1px solid #A0A0A0; + background-color: #E0E0E0; +} +div.toolbar img { + margin: 0; + padding: 0; + display: inline; + border-width: 0px; +} +div.toolbar a span { + position: relative; + top: -4px; +} +div.toolbar li span { + padding-left: 2px; + padding-right: 5px; +} + +/* vertical toolbar */ +div.toolbar_vert { + border: 1px solid #909090; + background-color: #D0D0D0; + padding: 2px 0; +} +div.toolbar_vert ul { + margin: 0; + padding: 0; +} +div.toolbar_vert ul li { + list-style: none; + margin: 0; +} +div.toolbar_vert a img { + opacity: 0.6; + /*filter: alpha(opacity=60);*/ +} +div.toolbar_vert a:hover img { + opacity: 1; + /*filter: alpha(opacity=100);*/ +} +div.toolbar_vert a { + display: block; + padding: 2px; + border: 1px solid transparent; + cursor: default; + width: auto; + color: #000000; + margin: 0 2px; + max-height: 16px; + text-decoration: none; +} +div.toolbar_vert a:hover { + border: 1px solid #202090; + background-color: #ceceed; + color: #000000; + text-decoration: none; +} +div.toolbar_vert a:active { + border: 1px solid #A0A0A0; + background-color: #E0E0E0; +} +div.toolbar_vert img { + margin: 0; + padding: 0; + display: inline; + border-width: 0px; +} +div.toolbar_vert a span { + position: relative; + top: -4px; +} +div.toolbar_vert li span { + padding-left: 2px; + padding-right: 5px; +} + +/* Tables */ +.tblholder { margin: 10px 0 0 0; padding: 0; border: 1px solid #AAAAAA; background-color: #E8E8E8; } + +/* The beautiful tables inside what may not obviously be mdg-comment divs */ +div.tblholder td.row1 { padding: 4px; background-color: #E0E0E0; } +div.tblholder td.row2 { padding: 4px; background-color: #F0F0F0; } +div.tblholder td.row3 { padding: 4px; background-color: #E8E8E8; } +div.tblholder th { padding: 4px; background-color: #7080A0; font-weight: bold; text-align: center; color: #FFFFFF; } +div.tblholder th.subhead { padding: 4px; background-color: #90A0B0; font-weight: bold; text-align: center; color: #FFFFFF; } +div.tblholder table { background-color: #FFFFFF; width: 100%; } + +/* Well, not Midget and not comments (usually), but that's what the class is called ;-). Basically an informational window or used as a wrapper for tables. */ +.mdg-comment, .mdg-infobox { margin-left: 1em; padding: 7px; border: 1px solid #AAAAAA; background-color: #E8E8E8; } + +/* JWS window theming */ +div.jswindow { border: 2px solid #7090B0; border-top: 5px solid #7090B0; padding: 0px; font-family: Trebuchet MS, tahoma, verdana, arial, sans-serif; font-size: 9pt; display: none; position: absolute; background-color: #FFFFFF; } +div.titlebar { background-color: #7090B0; color: #FFFFFF; font-family: Trebuchet MS, tahoma, verdana, arial, sans-serif; font-size: 9pt; padding-bottom: 4px; cursor: default; } +div.titlebar div.closebtn { width: 16px; height: 16px; border: 1px solid #B0D0F0; background-color: #90B0D0; display: block; } +div.titlebar div.closebtn:hover { width: 16px; height: 16px; border: 1px solid #FFFFFF; background-color: #B0D0F0; display: block; } +div.titlebar table, div.titlebar td { margin: 0; padding: 0; } +div.jswindow div.content { padding: 10px; margin: 0; background-color: #FFFFFF; } + +/* Search results */ +div.search-result h3 { font-size: 14pt; margin: 10px 0 0 0; } +div.search-result h3 a { color: blue !important; font-weight: normal; padding-bottom: 0; } +div.search-result p { margin: 10px 0 0 0 !important; font-family: arial, helvetica, sans-serif; font-size: 10pt; } +div.search-result span.search-result-info { color: green; } +div.search-result span.search-term, div.search-result span.title-search-term { background-color: #FFFFC0; font-weight: bold; } + +/* + * Search box + */ + +input.js-search-box { + font-size: 13px; + margin: 0; + padding: 1px !important; + background-image: url(../../../images/search-box-normal.gif); + height: 15px; + background-repeat: repeat-x; + border-width: 1px; + border-style: solid; + border-color: #6c6c6c; + color: #C0C0C0; +} + +input.js-search-box:focus { + background-image: url(../../../images/search-box-hilite.gif); + color: #666; +} + +div.js-search-submit { + display: block; + position: absolute; + width: 24px; + height: 19px; + font-size: 1px; + line-height: 19px; + clip: rect(0px, 24px, 19px, 0px); + overflow: hidden; + margin: 0; + padding: 0; + background: transparent url(../../../images/search-btn-normal.png) no-repeat !important; + background-repeat: no-repeat; + cursor: pointer; +} + +div.js-search-submit:hover { + background-image: url(../../../images/search-btn-hilite.png); +} + +input[type ^="text"].username, input[type ^="password"].password { + padding: 2px 2px 2px 27px; + width: 96px; + height: 15px; + border: 0px none #000; + font-size: 11px; +} +input[type ^="text"].username { + background-image: url(../../../images/login-username.png); +} +input[type ^="password"].password { + background-image: url(../../../images/login-password.png); +} + +/* + * jBox menu system + */ + +div.menu, div.menu_nojs { + background-color: #D0D0D0; + border: 1px solid #A0A0A0; + font-size: 9pt; +} +div.menu a, div.menu div.label { + padding: 2pt 5px; + text-decoration: none; + display: block; + float: left; + color: #404040; +} +div.menu_nojs a, div.menu_nojs div.label { + padding: 2pt 5px; + text-decoration: none; + display: block; + color: #404040; +} +div.menu div.label, div.menu_nojs div.label { + color: #101010; +} +div.menu span.sep, div.menu_nojs span.sep { + display: block; + float: left; + width: 5px; +} +div.menu div.multopts, div.menu_nojs div.multopts { + line-height: 17pt; +} +div.menu div.multopts a, div.menu div.multopts div.label, div.menu_nojs div.multopts a, div.menu_nojs div.multopts div.label { + float: none; + display: inline; +} +div.menu a:hover, div.menu_nojs a:hover { + color: #FFFFFF; + background-color: #808080; +} +div.menu input[type ^="text"], div.menu input[type ^="password"], div.menu_nojs input[type ^="text"], div.menu_nojs input[type ^="password"] { + border-width: 0; + font-size: 9pt; + padding: 4px 5px; + max-width: 70px; + background-color: #E0E0E0; +} +div.menu input[type ^="text"]:hover, div.menu input[type ^="password"]:hover, div.menu_nojs input[type ^="text"]:hover, div.menu_nojs input[type ^="password"]:hover { + background-color: #E8E8E8; +} +div.menu input[type ^="text"]:focus, div.menu input[type ^="password"]:focus, div.menu_nojs input[type ^="text"]:focus, div.menu_nojs input[type ^="password"]:focus { + background-color: #F0F0F0; +} +div.menu input[type ^="button"], div.menu input[type ^="submit"], div.menu_nojs input[type ^="button"], div.menu_nojs input[type ^="submit"] { + border-width: 0; + font-size: 9pt; + padding: 3px 5px; + max-width: 70px; +} +div.menu a.current, div.menu a.current:hover, div.menu_nojs a.current, div.menu_nojs a.current:hover { + color: #FFFFFF; + background-color: #505050; +} +div.menu ul { + display: none; + position: absolute; + padding: 0; + margin: 0; + background-color: #D0D0D0; + border: 1px solid #A0A0A0; + min-width: 120px; +} +div.menu_nojs ul { + display: block; + clear: both; +} +div.menu ul li, div.menu_nojs ul li { + list-style: none; +} +div.menu ul a, div.menu_nojs ul a { + float: none; + margin: 0; +} +span.menuclear { + font-size: 1px; + height: 0px; + width: 0px; + clear: left; + line-height: 0px; + display: block; +} + +/* + * Docking Boxes code (for the sidebar editor) + */ + +/* group container(s) */ +#sbedit { + margin: 0; + padding: 0; + /* position:relative; /* additional outer containers must also have position:relative */ +} +/* keyboard navigation tooltip */ +.dbx-tooltip { + display:block; + position:absolute; + margin:36px 0 0 125px; + width:185px; + border:1px solid #000; + background:#ffd; + color:#000; + font:normal normal normal 0.85em tahoma, arial, sans-serif; + padding:2px 4px 3px 5px; + text-align:left; + } +* html .dbx-tooltip { width:195px; } + +/* use CSS2 system colors in CSS2 browsers + but not safari, which doesn't support them */ +*[class="dbx-tooltip"]:lang(en) { + border-color:InfoText; + background:InfoBackground; + color:InfoText; + font:small-caption; + font-weight:normal; + } +/* additional clone styles */ +.dbx-clone { + opacity: 0.8; +} +.dbx-content ul { + margin: 0; padding: 0; + list-style: none; +} +.dbx-content li a, .dbx-content li a:hover { + text-decoration: none; color: #666; +} +.dbx-content2 { + margin: 0px 1px 0px 1px; +} + +/* Progress bars */ +div.progressbar { + padding: 2px; + background-color: #90A0B0; + width: 308px; +} +div.progressbar_inner { + min-width: 30px; + color: white; + background-color: #7080A0; + padding: 4px; +} +/* User notification - courtest of wikipedia.org (not sure if this is included with MediaWiki) */ +/* user notification thing */ + +.usermessage { + background-color: #ffce7b; + border: 1px solid #ffa500; + color: black; + margin: 10px 0 1em; + padding: .5em 1em; + vertical-align: middle; +} +.usermessage a:link, .usermessage a:active, .usermessage a:visited { + color: #CA7520; +} +.usermessage a:hover { + color: #AA5500 !important; +} +div.thumbnail { + display: table; + border: 1px solid #AAAAAA; + background-color: #F0F0F0; + padding: 4px; + margin-bottom: 10px; + padding-bottom: 0; +} +div.thumbnail-inner { + background-image: url(../../../images/thumbnail.png); + background-position: top right; + background-repeat: no-repeat; +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/dbx-key.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/dbx-key.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,63 @@ +function dbx_set_key() +{ + //initialise the docking boxes manager + var manager = new dbxManager('main'); //session ID [/-_a-zA-Z0-9/] + + //onstatechange fires when any group state changes + manager.onstatechange = function() + { + //copy the state string to a local var + var state = this.state; + + //remove group name and open/close state tokens + state = state.replace(/sbedit_(left|right)=/ig, '').replace(/[\-\+]/g, ''); + + //split into an array + state = state.split('&'); + + //output field + var field = document.getElementById('divOrder_Left'); + field.value = state[0]; + var field = document.getElementById('divOrder_Right'); + field.value = state[1]; + + //return value determines whether cookie is set + return false; + }; + + //create new docking boxes group + var sbedit_left = new dbxGroup( + 'sbedit_left', // container ID [/-_a-zA-Z0-9/] + 'vertical', // orientation ['vertical'|'horizontal'] + '7', // drag threshold ['n' pixels] + 'no', // restrict drag movement to container axis ['yes'|'no'] + '10', // animate re-ordering [frames per transition, or '0' for no effect] + 'no', // include open/close toggle buttons ['yes'|'no'] + 'open', // default state ['open'|'closed'] + 'open', // word for "open", as in "open this box" + 'close', // word for "close", as in "close this box" + 'click-down and drag to move this box', // sentence for "move this box" by mouse + 'click to %toggle% this box', // pattern-match sentence for "(open|close) this box" by mouse + 'use the arrow keys to move this box', // sentence for "move this box" by keyboard + ', or press the enter key to %toggle% it', // pattern-match sentence-fragment for "(open|close) this box" by keyboard + '%mytitle% [%dbxtitle%]' // pattern-match syntax for title-attribute conflicts + ); + + //create new docking boxes group + var sbedit_right = new dbxGroup( + 'sbedit_right', // container ID [/-_a-zA-Z0-9/] + 'vertical', // orientation ['vertical'|'horizontal'] + '7', // drag threshold ['n' pixels] + 'no', // restrict drag movement to container axis ['yes'|'no'] + '10', // animate re-ordering [frames per transition, or '0' for no effect] + 'no', // include open/close toggle buttons ['yes'|'no'] + 'open', // default state ['open'|'closed'] + 'open', // word for "open", as in "open this box" + 'close', // word for "close", as in "close this box" + 'click-down and drag to move this box', // sentence for "move this box" by mouse + 'click to %toggle% this box', // pattern-match sentence for "(open|close) this box" by mouse + 'use the arrow keys to move this box', // sentence for "move this box" by keyboard + ', or press the enter key to %toggle% it', // pattern-match sentence-fragment for "(open|close) this box" by keyboard + '%mytitle% [%dbxtitle%]' // pattern-match syntax for title-attribute conflicts + ); +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/dbx.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/dbx.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,34 @@ +/**************************************************************** + Docking Boxes core CSS: YOU MUST NOT CHANGE OR OVERRIDE THESE +*****************************************************************/ +.dbx-clone { + position:absolute; + visibility:hidden; + } +.dbx-clone, .dbx-clone .dbx-handle-cursor { + cursor:move !important; + } +.dbx-dummy { + display:block; + width:0; + height:0; + overflow:hidden; + } +.dbx-group, .dbx-box, .dbx-handle { + position:relative; + display:block; + } + + + +/**************************************************************** + avoid padding, margins or borders on dbx-box, + to reduce visual discrepancies between it and the clone. + overall, dbx-box is best left as visually unstyled as possible +*****************************************************************/ +.dbx-box { + margin:0; + padding:0; + border:none; + } + diff -r 902822492a68 -r fe660c52c48f includes/clientside/dbx.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/dbx.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,6 @@ +// DBX2.05 :: Docking Boxes (dbx) +// ***************************************************** +// DOM scripting by brothercake -- http://www.brothercake.com/ +// GNU Lesser General Public License -- http://www.gnu.org/licenses/lgpl.html +//****************************************************** +var dbx;function dbxManager(sid){dbx = this;if(!/^[-_a-z0-9]+$/i.test(sid)) { alert('Error from dbxManager:\n"' + sid + '" is an invalid session ID'); return; }this.supported = !(document.getElementsByTagName('*').length == 0 || (navigator.vendor == 'KDE' && typeof window.sidebar == 'undefined'));if(!this.supported) { return; }this.etype = typeof document.addEventListener != 'undefined' ? 'addEventListener' : typeof document.attachEvent != 'undefined' ? 'attachEvent' : 'none';this.eprefix = (this.etype == 'attachEvent' ? 'on' : '');if(typeof window.opera != 'undefined' && parseFloat(navigator.userAgent.toLowerCase().split(/opera[\/ ]/)[1].split(' ')[0], 10) < 7.5){this.etype = 'none';}if(this.etype == 'none') { this.supported = false; return; }this.running = 0;this.sid = sid;this.savedata = {};this.cookiestate = this.getCookieState();};dbxManager.prototype.setCookieState = function(){var now = new Date();now.setTime(now.getTime() + (365*24*60*60*1000));var str = '';for(j in this.savedata){if(typeof this.savedata[j]!='function'){str += j + '=' + this.savedata[j] + '&'}}this.state = str.replace(/^(.+)&$/, '$1');this.cookiestring = this.state.replace(/,/g, '|');this.cookiestring = this.cookiestring.replace(/=/g, ':');if(typeof this.onstatechange == 'undefined' || this.onstatechange()){document.cookie = 'dbx-' + this.sid + '='+ this.cookiestring+ '; expires=' + now.toGMTString()+ '; path=/';}};dbxManager.prototype.getCookieState = function(){this.cookiestate = null;if(document.cookie){if(document.cookie.indexOf('dbx-' + this.sid)!=-1){this.cookie = document.cookie.split('dbx-' + this.sid + '=')[1].split(';')[0].split('&');for(var i in this.cookie){if(typeof this.cookie[i]!='function'){this.cookie[i] = this.cookie[i].replace(/\|/g, ',');this.cookie[i]= this.cookie[i].replace(/:/g, '=');this.cookie[i] = this.cookie[i].split('=');this.cookie[i][1] = this.cookie[i][1].split(',');}}this.cookiestate = {};for(i in this.cookie){if(typeof this.cookie[i]!='function'){this.cookiestate[this.cookie[i][0]] = this.cookie[i][1];}}}}return this.cookiestate;};dbxManager.prototype.addDataMember = function(gid, order){this.savedata[gid] = order;};dbxManager.prototype.createElement = function(tag){return typeof document.createElementNS != 'undefined' ? document.createElementNS('http://www.w3.org/1999/xhtml', tag) : document.createElement(tag);};dbxManager.prototype.getTarget = function(e, pattern, node){if(typeof node != 'undefined'){var target = node;}else{target = typeof e.target != 'undefined' ? e.target : e.srcElement;}var regex = new RegExp(pattern, '');while(!regex.test(target.className)){target = target.parentNode;}return target;};function dbxGroup(gid, dir, thresh, fix, ani, togs, def, open, close, move, toggle, kmove, ktoggle, syntax){if(!/^[-_a-z0-9]+$/i.test(gid)) { alert('Error from dbxGroup:\n"' + gid + '" is an invalid container ID'); return; }this.container = document.getElementById(gid);if(this.container == null || !dbx.supported) { return; }var self = this;this.gid = gid;this.dragok = false;this.box = null;this.vertical = dir == 'vertical';this.threshold = parseInt(thresh, 10);this.restrict = fix == 'yes';this.resolution = parseInt(ani, 10);this.toggles = togs == 'yes';this.defopen = def != 'closed';this.vocab = {'open' : open,'close' : close,'move' : move,'toggle' : toggle,'kmove' : kmove,'ktoggle' : ktoggle,'syntax' : syntax};this.container.style.position = 'relative';this.container.style.display = 'block';if(typeof window.opera != 'undefined'){this.container.style.display = 'run-in';}this.boxes = [];this.buttons = [];this.order = [];this.eles = this.container.getElementsByTagName('*');for(var i=0; i 0)){var sibling = this.boxes[positions[i + (positive ? 1 : -1)][0]];if(this.resolution > 0){var visipos = { 'x' : parent.offsetLeft, 'y' : parent.offsetTop };var siblingpos = { 'x' : sibling.offsetLeft, 'y' : sibling.offsetTop };}var obj = { 'insert' : (positive ? sibling : parent), 'before' : (positive ? parent : sibling) };this.container.insertBefore(obj.insert, obj.before);if(this.resolution > 0){var animators ={'sibling' : new dbxAnimator(this, sibling, siblingpos, this.resolution, true, anchor),'parent' : new dbxAnimator(this, parent, visipos, this.resolution, true, anchor)};}else{anchor.focus();}break;}}}this.getBoxOrder();}};dbxGroup.prototype.compare = function(a, b){return a[1] - b[1];};dbxGroup.prototype.createTooltip = function(isopen, anchor){if(this.keydown){this.tooltip = this.container.appendChild(dbx.createElement('span'));this.tooltip.style.visibility = 'hidden';this.tooltip.className = 'dbx-tooltip';if(isopen != null){this.tooltip.appendChild(document.createTextNode(this.vocab.kmove + this.vocab.ktoggle.replace('%toggle%', isopen ? this.vocab.close : this.vocab.open)));}else{this.tooltip.appendChild(document.createTextNode(this.vocab.kmove));}var parent = dbx.getTarget(null, 'dbx\-box', anchor);this.tooltip.style.left = parent.offsetLeft + 'px';this.tooltip.style.top = parent.offsetTop + 'px';var tooltip = this.tooltip;window.setTimeout(function(){if(tooltip != null) { tooltip.style.visibility = 'visible'; }}, 500);}};dbxGroup.prototype.removeTooltip = function(){if(this.tooltip != null){this.tooltip.parentNode.removeChild(this.tooltip);this.tooltip = null;}};dbxGroup.prototype.mousedown = function(e, box){var node = typeof e.target != 'undefined' ? e.target : e.srcElement;if(node.nodeName == '#text') { node = node.parentNode; }if(!/dbx\-(toggle|box|group)/i.test(node.className)){while(!/dbx\-(handle|box|group)/i.test(node.className)){node = node.parentNode;}}if(/dbx\-handle/i.test(node.className)){this.removeTooltip();this.released = false;this.initial = { 'x' : e.clientX, 'y' : e.clientY };this.current = { 'x' : 0, 'y' : 0 };this.createCloneBox(box);if(typeof e.preventDefault != 'undefined' ) { e.preventDefault(); }if(typeof document.onselectstart != 'undefined'){document.onselectstart = function() { return false; }}}};dbxGroup.prototype.mousemove = function(e){if(this.dragok && this.box != null){this.positive = this.vertical ? (e.clientY > this.current.y ? true : false) : (e.clientX > this.current.x ? true : false);this.current = { 'x' : e.clientX, 'y' : e.clientY };var overall = { 'x' : this.current.x - this.initial.x, 'y' : this.current.y - this.initial.y };if(((overall.x >= 0 && overall.x <= this.threshold) || (overall.x <= 0 && overall.x >= 0 - this.threshold))&&((overall.y >= 0 && overall.y <= this.threshold) || (overall.y <= 0 && overall.y >= 0 - this.threshold))){this.current.x -= overall.x;this.current.y -= overall.y;}if(this.released || overall.x > this.threshold || overall.x < (0 - this.threshold) || overall.y > this.threshold || overall.y < (0 - this.threshold)){dbx.group = this.container;dbx.box = this.box;dbx.event = e;if(typeof dbx.onboxdrag == 'undefined' || dbx.onboxdrag()){this.released = true;if(!this.restrict || !this.vertical) { this.boxclone.style.left = (this.current.x - this.difference.x) + 'px'; }if(!this.restrict || this.vertical) { this.boxclone.style.top = (this.current.y - this.difference.y) + 'px'; }this.moveOriginalToPosition(this.current.x, this.current.y);if(typeof e.preventDefault != 'undefined' ) { e.preventDefault(); }}}}return true;};dbxGroup.prototype.mouseup = function(e){if(this.box != null){this.moveOriginalToPosition(e.clientX, e.clientY);this.removeCloneBox();this.getBoxOrder();if(typeof document.onselectstart != 'undefined'){document.onselectstart = function() { return true; }}}this.dragok = false;};dbxGroup.prototype.keypress = function(e, anchor){if(/^(3[7-9])|(40)$/.test(e.keyCode)){this.removeTooltip();if((this.vertical && /^(38|40)$/.test(e.keyCode)) || (!this.vertical && /^(37|39)$/.test(e.keyCode))){this.shiftBoxPosition(e, anchor, /^[3][78]$/.test(e.keyCode) ? false : true);if(typeof e.preventDefault != 'undefined') { e.preventDefault(); }else { return false; }typeof e.stopPropagation != 'undefined' ? e.stopPropagation() : e.cancelBubble = true;this.keydown = false;}}return true;};dbxGroup.prototype.getBoxOrder = function(){this.order = [];var len = this.eles.length;for(var j=0; j boxprops.xy && cloneprops.xy < boxprops.xy)||(!this.positive && cloneprops.xy < boxprops.xy && cloneprops.xy + cloneprops.wh > boxprops.xy)){if(this.boxes[i] == this.box) { return; }var sibling = this.box.nextSibling;while(sibling.className == null || !/dbx\-box/.test(sibling.className)){sibling = sibling.nextSibling;}if(this.boxes[i] == sibling) { return; }if(this.resolution > 0){if(this.box[this.vertical ? 'offsetTop' : 'offsetLeft'] < boxprops.xy){var visibox = this.boxes[i].previousSibling;while(visibox.className == null || !/dbx\-box/.test(visibox.className)){visibox = visibox.previousSibling;}}else{visibox = this.boxes[i];}var visipos = { 'x' : visibox.offsetLeft, 'y' : visibox.offsetTop };}var prepos = { 'x' : this.box.offsetLeft, 'y' : this.box.offsetTop };this.container.insertBefore(this.box, this.boxes[i]);this.initial.x += (this.box.offsetLeft - prepos.x);this.initial.y += (this.box.offsetTop - prepos.y);if(this.resolution > 0 && visibox != this.box){var animator = new dbxAnimator(this, visibox, visipos, this.resolution, false, null);}else{}break;}}};function dbxAnimator(caller, box, pos, res, kbd, anchor){this.caller = caller;this.box = box;this.timer = null;var before = pos[this.caller.vertical ? 'y' : 'x'];var after = this.box[this.caller.vertical ? 'offsetTop' : 'offsetLeft'];if(before != after){if(dbx.running > this.caller.boxes.length - 1) { return; }var clone = this.caller.createClone(this.box, 29999, arguments[2]);clone.style.visibility = 'visible';this.box.style.visibility = 'hidden';this.animateClone(clone,before,after > before ? after - before : 0 - (before - after),this.caller.vertical ? 'top' : 'left',res,kbd,anchor);}};dbxAnimator.prototype.animateClone = function(clone, current, change, dir, res, kbd, anchor){var self = this;var count = 0;dbx.running ++;this.timer = window.setInterval(function(){count ++;current += change / res;clone.style[dir] = current + 'px';if(count == res){window.clearTimeout(self.timer);self.timer = null;dbx.running --;self.caller.container.removeChild(clone);self.box.style.visibility = 'visible';if(kbd){if(anchor != null && anchor.parentNode.style.visibility != 'hidden'){anchor.focus();}else if(self.caller.toggles){var button = self.caller.buttons[parseInt(self.box.className.split('dbxid')[1],10)];if(button != null && typeof button.isactive != 'undefined'){button.focus();}}}}}, 20);};if(typeof window.attachEvent != 'undefined'){window.attachEvent('onunload', function(){var ev = ['mousedown', 'mousemove', 'mouseup', 'mouseout', 'click', 'keydown', 'keyup', 'focus', 'blur', 'selectstart', 'statechange', 'boxdrag', 'boxopen', 'boxclose'];var el = ev.length;var dl = document.all.length;for(var i=0; igetPacked($everything); + $data = Array( + 'md5' => md5 ( $everything ), + 'code' => $packed + ); + echo "// The code in this file was fetched from the static scripts and compressed (packed code cached)\n\n"; + echo $packed; + + $fh = @fopen($cache_file, 'w'); + if (!$fh) + die('// ERROR: Can\'t open cache file for writing'); + fwrite($fh, serialize ( $data ) ); + fclose($fh); + + exit; + } + + echo "// The code in this file was not compressed because packed-script caching is disabled\n\n"; + echo $everything; + +} +else +{ + echo "// The code in this file was not compressed because all script compression is disabled\n\n"; + echo $everything; +} +?> diff -r 902822492a68 -r fe660c52c48f includes/clientside/md5.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/md5.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,30 @@ +// Javascript implementation of the MD5 algorithm - used for encrypting password before sending them over the Internet +var hexcase = 0; var b64pad = ""; var chrsz = 8; +function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));} +function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));} +function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));} +function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); } +function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); } +function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); } +function md5_vm_test() { return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72"; } +function core_md5(x, len) { x[len >> 5] |= 0x80 << ((len) % 32); x[(((len + 64) >>> 9) << 4) + 14] = len; var a = 1732584193; var b = -271733879; var c = -1732584194; var d = 271733878; for(var i = 0; i < x.length; i += 16) { var olda = a; var oldb = b; var oldc = c; var oldd = d; a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); + a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);c = md5_ff(c, d, a, b, x[i+10], 17, -42063);b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);d = md5_ff(d, a, b, c, x[i+13], 12, -40341101); + c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); + a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); + c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); + a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); + c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); + a = safe_add(a, olda); b = safe_add(b, oldb); c = safe_add(c, oldc); d = safe_add(d, oldd); } return Array(a, b, c, d); } +function md5_cmn(q, a, b, x, s, t) { return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b); } +function md5_ff(a, b, c, d, x, s, t) { return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); } +function md5_gg(a, b, c, d, x, s, t) { return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); } +function md5_hh(a, b, c, d, x, s, t) { return md5_cmn(b ^ c ^ d, a, b, x, s, t); } +function md5_ii(a, b, c, d, x, s, t) { return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); } +function core_hmac_md5(key, data) { var bkey = str2binl(key); if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz); var ipad = Array(16), opad = Array(16); for(var i = 0; i < 16; i++) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5C5C5C5C; } var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz); return core_md5(opad.concat(hash), 512 + 128); } +function safe_add(x, y) {var lsw = (x & 0xFFFF) + (y & 0xFFFF);var msw = (x >> 16) + (y >> 16) + (lsw >> 16);return (msw << 16) | (lsw & 0xFFFF); } +function bit_rol(num, cnt) { return (num << cnt) | (num >>> (32 - cnt)); } +function str2binl(str) { var bin = Array(); var mask = (1 << chrsz) - 1; for(var i = 0; i < str.length * chrsz; i += chrsz) bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32); return bin; } +function binl2str(bin) { var str = ""; var mask = (1 << chrsz) - 1; for(var i = 0; i < bin.length * 32; i += chrsz) str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask); return str; } +function binl2hex(binarray) { var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; var str = ""; for(var i = 0; i < binarray.length * 4; i++) { str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF); } return str; } +function binl2b64(binarray) { var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var str = ""; for(var i = 0; i < binarray.length * 4; i += 3) { var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16) | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 ) | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF); for(var j = 0; j < 4; j++) { if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); } } return str; } + diff -r 902822492a68 -r fe660c52c48f includes/clientside/sbedit.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/sbedit.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,122 @@ +var disenable_currentBlock; +function ajaxDisenableBlock(id) +{ + disenable_currentBlock = document.getElementById('disabled_'+id); + ajaxGet(makeUrlNS('Special', 'EditSidebar', 'action=disenable&ajax=true&noheaders&id='+id), function() + { + if(ajax.readyState == 4) + { + if(ajax.responseText == 'GOOD') + { + if(disenable_currentBlock.style.display == 'none') + { + disenable_currentBlock.style.display = 'inline'; + } + else + { + disenable_currentBlock.style.display = 'none'; + } + } + else + { + document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText; + } + } + }); +} + +var delete_currentBlock; +function ajaxDeleteBlock(id, oElm) +{ + delete_currentBlock = { 0 : id, 1 : oElm }; + ajaxGet(makeUrlNS('Special', 'EditSidebar', 'action=delete&ajax=true&noheaders&id='+id), function() + { + if(ajax.readyState == 4) + { + if(ajax.responseText == 'GOOD') + { + e = delete_currentBlock[1]; + e = e.parentNode.parentNode; + e.parentNode.removeChild(e); + } + else + { + document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText; + } + } + }); +} + +var blockEdit_current; +function ajaxEditBlock(id, oElm) +{ + blockEdit_current = { 0 : id, 1 : oElm }; + ajaxGet(makeUrlNS('Special', 'EditSidebar', 'action=getsource&noheaders&id='+id), function() + { + if(ajax.readyState == 4) + { + id = blockEdit_current[0]; + oElm = blockEdit_current[1]; + var thediv = document.createElement('div'); + //if(!oElm.id) oElm.id = 'autoEditButton_'+Math.floor(Math.random() * 100000); + oElm = oElm.parentNode; + o = fetch_offset(oElm); + d = fetch_dimensions(oElm); + top = o['top'] + d['h'] + 'px'; + left = o['left'] + 'px'; + thediv.style.top = top; + thediv.style.left = left; + thediv.style.position = 'absolute'; + thediv.className = 'mdg-comment'; + thediv.style.margin = '0'; + if(ajax.responseText == 'HOUSTON_WE_HAVE_A_PLUGIN') + { + thediv.innerHTML = '

This block cannot be edited.

This is a plugin block, and cannot be edited.

close

'; + } + else + { + ta = document.createElement('textarea'); + ta.rows = '15'; + ta.cols = '50'; + ta.innerHTML = ajax.responseText; + thediv.appendChild(ta); + b = document.createElement('br'); + thediv.appendChild(b); + thediv.innerHTML += 'save | cancel'; + } + body = document.getElementsByTagName('body'); + body = body[0]; + body.appendChild(thediv); + } + }); +} + +var blockSave_current; +function ajaxSaveBlock(oElm, id) +{ + taContent = escape(oElm.previousSibling.previousSibling.value); + taContent = taContent.replace(unescape('%0A'), '%0A'); + taContent = taContent.replace('+', '%2B'); + blockSave_current = { 0 : id, 1 : oElm }; + ajaxPost(makeUrlNS('Special', 'EditSidebar', 'noheaders&action=save&id='+id), 'content='+taContent, function() + { + if(ajax.readyState == 4) + { + id = blockSave_current[0]; + oElm = blockSave_current[1]; + eval(ajax.responseText); + if(status == 'GOOD') + { + parent = document.getElementById('disabled_'+id).parentNode.parentNode; + oElm.parentNode.parentNode.removeChild(oElm.parentNode); + content = content.replace('%a', unescape('%0A')); + parent.firstChild.nextSibling.nextSibling.nextSibling.innerHTML = content; // $content is set in ajax.responseText + } + else + { + alert(status); + } + } + }); +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/.acl.js.marks --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/.acl.js.marks Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +!t;10617;10617 diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/acl.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/acl.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,696 @@ +// Javascript routines for the ACL editor + +var aclManagerID = 'enano_aclmanager_' + Math.floor(Math.random() * 1000000); +var aclPermList = false; +var aclDataCache = false; + +function ajaxOpenACLManager(page_id, namespace) +{ + if(IE) + return true; + if(!page_id || !namespace) + { + var data = strToPageID(title); + var page_id = data[0]; + var namespace = data[1]; + } + var params = { + 'mode' : 'listgroups', + 'page_id' : page_id, + 'namespace' : namespace + }; + params = toJSONString(params); + params = ajaxEscape(params); + ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function() { + if(ajax.readyState == 4) + { + __aclBuildWizardWindow(); + groups = parseJSON(ajax.responseText); + aclDataCache = groups; + __aclBuildSelector(groups); + } + }); + return false; +} + +function ajaxACLSwitchToSelector() +{ + params = { + 'mode' : 'listgroups' + }; + if ( aclDataCache.page_id && aclDataCache.namespace ) + { + params.page_id = aclDataCache.page_id; + params.namespace = aclDataCache.namespace; + } + params = toJSONString(params); + params = ajaxEscape(params); + ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function() { + if(ajax.readyState == 4) + { + document.getElementById(aclManagerID+'_main').innerHTML = ''; + document.getElementById(aclManagerID + '_back').style.display = 'none'; + document.getElementById(aclManagerID + '_next').value = 'Next >'; + groups = parseJSON(ajax.responseText); + aclDataCache = groups; + thispage = strToPageID(title); + groups.page_id = thispage[0]; + groups.namespace = thispage[1]; + __aclBuildSelector(groups); + } + }); +} + +function __aclBuildSelector(groups) +{ + thispage = strToPageID(title); + do_scopesel = ( thispage[0] == groups.page_id && thispage[1] == groups.namespace ); + + seed = Math.floor(Math.random() * 1000000); + + main = document.getElementById(aclManagerID + '_main'); + main.style.padding = '10px'; + + selector = document.createElement('div'); + + grpsel = __aclBuildGroupsHTML(groups); + grpsel.name = 'group_id'; + + span = document.createElement('div'); + span.id = "enACL_grpbox_"+seed+""; + + // Build the selector + grpb = document.createElement('input'); + grpb.type = 'radio'; + grpb.name = 'target_type'; + grpb.value = 1; // ACL_TYPE_GROUP + grpb.checked = 'checked'; + grpb.className = seed; + grpb.onclick = function() { seed = this.className; document.getElementById('enACL_grpbox_'+seed).style.display = 'block'; document.getElementById('enACL_usrbox_'+seed).style.display = 'none'; }; + lbl = document.createElement('label'); + lbl.appendChild(grpb); + lbl.appendChild(document.createTextNode('A usergroup')); + lbl.style.display = 'block'; + span.appendChild(grpsel); + + usrb = document.createElement('input'); + usrb.type = 'radio'; + usrb.name = 'target_type'; + usrb.value = 2; // ACL_TYPE_USER + usrb.className = seed; + usrb.onclick = function() { seed = this.className; document.getElementById('enACL_grpbox_'+seed).style.display = 'none'; document.getElementById('enACL_usrbox_'+seed).style.display = 'block'; }; + lbl2 = document.createElement('label'); + lbl2.appendChild(usrb); + lbl2.appendChild(document.createTextNode('A specific user')); + lbl2.style.display = 'block'; + + usrsel = document.createElement('input'); + usrsel.type = 'text'; + usrsel.name = 'username'; + usrsel.onkeyup = function() { ajaxUserNameComplete(this); }; + usrsel.id = 'userfield_' + aclManagerID; + try { + usrsel.setAttribute("autocomplete","off"); + } catch(e) {}; + + span2 = document.createElement('div'); + span2.id = "enACL_usrbox_"+seed+""; + span2.style.display = 'none'; + span2.appendChild(usrsel); + + // Scope selector + if(do_scopesel) + { + scopediv1 = document.createElement('div'); + scopediv2 = document.createElement('div'); + scopeRadioPage = document.createElement('input'); + scopeRadioPage.type = 'radio'; + scopeRadioPage.name = 'scope'; + scopeRadioPage.value = 'page'; + scopeRadioPage.checked = 'checked'; + scopeRadioGlobal = document.createElement('input'); + scopeRadioGlobal.type = 'radio'; + scopeRadioGlobal.name = 'scope'; + scopeRadioGlobal.value = 'global'; + lblPage = document.createElement('label'); + lblPage.style.display = 'block'; + lblPage.appendChild(scopeRadioPage); + lblPage.appendChild(document.createTextNode('Only this page')); + lblGlobal = document.createElement('label'); + lblGlobal.style.display = 'block'; + lblGlobal.appendChild(scopeRadioGlobal); + lblGlobal.appendChild(document.createTextNode('The entire website')); + scopediv1.appendChild(lblPage); + scopediv2.appendChild(lblGlobal); + + scopedesc = document.createElement('p'); + scopedesc.appendChild(document.createTextNode('What should this access rule control?')); + } + + // Styles + span.style.marginLeft = '13px'; + span.style.padding = '5px 0'; + span2.style.marginLeft = '13px'; + span2.style.padding = '5px 0'; + + selector.appendChild(lbl); + selector.appendChild(span); + + selector.appendChild(lbl2); + selector.appendChild(span2); + + container = document.createElement('div'); + container.style.margin = 'auto'; + container.style.width = '360px'; + container.style.paddingTop = '70px'; + + head = document.createElement('h2'); + head.appendChild(document.createTextNode('Manage page access')); + + desc = document.createElement('p'); + desc.appendChild(document.createTextNode('Please select who should be affected by this access rule.')); + + container.appendChild(head); + container.appendChild(desc); + container.appendChild(selector); + + if(do_scopesel) + { + container.appendChild(scopedesc); + container.appendChild(scopediv1); + container.appendChild(scopediv2); + } + + main.appendChild(container); + + var mode = document.createElement('input'); + mode.name = 'mode'; + mode.type = 'hidden'; + mode.id = aclManagerID + '_mode'; + mode.value = 'seltarget'; + + var theform = document.getElementById(aclManagerID + '_formobj_id'); + if ( !theform.mode ) + { + theform.appendChild(mode); + } + else + { + theform.removeChild(theform.mode); + theform.appendChild(mode); + } +} + +var aclDebugWin = false; + +function aclDebug(text) +{ + if(!aclDebugWin) + aclDebugWin = pseudoWindowOpen("data:text/html;plain,debug win

Debug window

", "aclDebugWin"); + setTimeout(function() { + aclDebugWin.pre = aclDebugWin.document.createElement('pre'); + aclDebugWin.pre.appendChild(aclDebugWin.document.createTextNode(text)); + aclDebugWin.b = aclDebugWin.document.getElementsByTagName('body')[0]; + aclDebugWin.b.appendChild(aclDebugWin.pre);}, 1000); +} + +var pseudoWindows = new Object(); + +function pseudoWindowOpen(url, id) +{ + if(pseudoWindows[id]) + { + document.getElementById('pseudowin_ifr_'+id).src = url; + } + else + { + win = document.createElement('iframe'); + win.style.position='fixed'; + win.style.width = '640px'; + win.style.height = '480px'; + win.style.top = '0px'; + win.style.left = '0px'; + win.style.zIndex = getHighestZ() + 1; + win.style.backgroundColor = '#FFFFFF'; + win.name = 'pseudo_ifr_'+id; + win.id = 'pseudowindow_ifr_'+id; + win.src = url; + body = document.getElementsByTagName('body')[0]; + body.appendChild(win); + } + win_obj = eval("( pseudo_ifr_"+id+" )"); + return win_obj; +} + +function __aclJSONSubmitAjaxHandler(params) +{ + params = toJSONString(params); + params = ajaxEscape(params); + ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function() { + if(ajax.readyState == 4) + { + try { + data = parseJSON(ajax.responseText); + } catch(e) { + aclDebug(e+"\n\nResponse:\n"+ajax.responseText); + } + aclDataCache = data; + switch(data.mode) + { + case 'seltarget': + + // Build the ACL edit form + // try { + act_desc = ( data.type == 'new' ) ? 'Create access rule' : 'Editing permissions'; + target_type_t = ( data.target_type == 1 ) ? 'group' : 'user'; + target_name_t = data.target_name; + var scope_type = ( data.page_id == false && data.namespace == false ) ? 'this entire site' : 'this page'; + html = '

'+act_desc+'

This panel allows you to edit what the '+target_type_t+' "'+target_name_t+'" can do on ' + scope_type + '. Unless you set a permission to "Deny", these permissions may be overridden by other rules.

'; + parser = new templateParser(data.template.acl_field_begin); + html += parser.run(); + + cls = 'row2'; + for(var i in data.acl_types) + { + if(typeof(data.acl_types[i]) == 'number') + { + cls = ( cls == 'row1' ) ? 'row2' : 'row1'; + p = new templateParser(data.template.acl_field_item); + vars = new Object(); + vars['FIELD_DESC'] = data.acl_descs[i]; + vars['FIELD_DENY_CHECKED'] = ''; + vars['FIELD_DISALLOW_CHECKED'] = ''; + vars['FIELD_WIKIMODE_CHECKED'] = ''; + vars['FIELD_ALLOW_CHECKED'] = ''; + vars['FIELD_NAME'] = i; + switch(data.current_perms[i]) + { + case 1: + vars['FIELD_DENY_CHECKED'] = 'checked="checked"'; + break; + case 2: + default: + vars['FIELD_DISALLOW_CHECKED'] = 'checked="checked"'; + break; + case 3: + vars['FIELD_WIKIMODE_CHECKED'] = 'checked="checked"'; + break; + case 4: + vars['FIELD_ALLOW_CHECKED'] = 'checked="checked"'; + break; + } + vars['ROW_CLASS'] = cls; + p.assign_vars(vars); + html += p.run(); + } + } + + var parser = new templateParser(data.template.acl_field_end); + html += parser.run(); + + if(data.type == 'edit') + html += '

Delete this rule

'; + + var main = document.getElementById(aclManagerID + '_main'); + main.innerHTML = html; + + var form = document.getElementById(aclManagerID + '_formobj_id'); + + var modeobj = form_fetch_field(form, 'mode'); + if ( modeobj ) + modeobj.value = 'save_' + data.type; + else + alert('modeobj is invalid: '+modeobj); + + aclPermList = array_keys(data.acl_types); + + document.getElementById(aclManagerID + '_back').style.display = 'inline'; + document.getElementById(aclManagerID + '_next').value = 'Save Changes'; + + // } catch(e) { alert(e); aclDebug(ajax.responseText); } + + break; + case 'success': + var note = document.createElement('div'); + note.className = 'info-box'; + note.style.marginLeft = '0'; + var b = document.createElement('b'); + b.appendChild(document.createTextNode('Permissions updated')); + note.appendChild(b); + note.appendChild(document.createElement('br')); + note.appendChild(document.createTextNode('The permissions for '+data.target_name+' on this page have been updated successfully.')); + note.appendChild(document.createElement('br')); + var a = document.createElement('a'); + a.href = 'javascript:void(0);'; + a.onclick = function() { this.parentNode.parentNode.removeChild(this.parentNode); return false; }; + a.appendChild(document.createTextNode('[ dismiss :')); + note.appendChild(a); + var a2 = document.createElement('a'); + a2.href = 'javascript:void(0);'; + a2.onclick = function() { killACLManager(); return false; }; + a2.appendChild(document.createTextNode(': close manager ]')); + note.appendChild(a2); + document.getElementById(aclManagerID + '_main').insertBefore(note, document.getElementById(aclManagerID + '_main').firstChild); + if(!document.getElementById(aclManagerID+'_deletelnk')) + document.getElementById(aclManagerID + '_main').innerHTML += '

Delete this rule

'; + //fadeInfoBoxes(); + break; + case 'delete': + + params = { + 'mode' : 'listgroups' + }; + params = toJSONString(params); + params = ajaxEscape(params); + ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function() { + if(ajax.readyState == 4) + { + document.getElementById(aclManagerID+'_main').innerHTML = ''; + document.getElementById(aclManagerID + '_back').style.display = 'none'; + document.getElementById(aclManagerID + '_next').value = 'Next >'; + var thispage = strToPageID(title); + groups.page_id = thispage[0]; + groups.namespace = thispage[1]; + __aclBuildSelector(groups); + + note = document.createElement('div'); + note.className = 'info-box'; + note.style.marginLeft = '0'; + b = document.createElement('b'); + b.appendChild(document.createTextNode('Entry deleted')); + note.appendChild(b); + note.appendChild(document.createElement('br')); + note.appendChild(document.createTextNode('The access rules for '+aclDataCache.target_name+' on this page have been deleted.')); + note.appendChild(document.createElement('br')); + a = document.createElement('a'); + a.href = '#'; + a.onclick = function() { this.parentNode.parentNode.removeChild(this.parentNode); return false; }; + a.appendChild(document.createTextNode('[ dismiss :')); + note.appendChild(a); + a = document.createElement('a'); + a.href = '#'; + a.onclick = function() { killACLManager(); return false; }; + a.appendChild(document.createTextNode(': close manager ]')); + note.appendChild(a); + document.getElementById(aclManagerID + '_main').insertBefore(note, document.getElementById(aclManagerID + '_main').firstChild); + //fadeInfoBoxes(); + + } + }); + + break; + case 'error': + alert("Server side processing error:\n"+data.error); + break; + case 'debug': + aclDebug(data.text); + break; + default: + alert("Invalid JSON response from server\nMode: "+data.mode+"\nJSON string: "+ajax.responseText); + break; + } + } + }); +} + +function __aclBuildGroupsHTML(groups) +{ + groups = groups.groups; + select = document.createElement('select'); + for(var i in groups) + { + if(typeof(groups[i]['name']) == 'string' && i != 'toJSONString') + { + o = document.createElement('option'); + o.value = groups[i]['id']; + t = document.createTextNode(groups[i]['name']); + o.appendChild(t); + select.appendChild(o); + } + } + return select; +} + +function __aclBuildWizardWindow() +{ + darken(); + box = document.createElement('div'); + box.style.width = '640px' + box.style.height = '440px'; + box.style.position = 'fixed'; + width = getWidth(); + height = getHeight(); + box.style.left = ( width / 2 - 320 ) + 'px'; + box.style.top = ( height / 2 - 250 ) + 'px'; + box.style.backgroundColor = 'white'; + box.style.zIndex = getHighestZ() + 1; + box.id = aclManagerID; + box.style.opacity = '0'; + box.style.filter = 'alpha(opacity=0)'; + box.style.display = 'none'; + + mainwin = document.createElement('div'); + mainwin.id = aclManagerID + '_main'; + mainwin.style.clip = 'rect(0px,640px,440px,0px)'; + mainwin.style.overflow = 'auto'; + mainwin.style.width = '620px'; + mainwin.style.height = '420px'; + + panel = document.createElement('div'); + panel.style.width = '620px'; + panel.style.padding = '10px'; + panel.style.lineHeight = '40px'; + panel.style.textAlign = 'right'; + panel.style.position = 'fixed'; + panel.style.left = ( width / 2 - 320 ) + 'px'; + panel.style.top = ( height / 2 + 190 ) + 'px'; + panel.style.backgroundColor = '#D0D0D0'; + panel.style.opacity = '0'; + panel.style.filter = 'alpha(opacity=0)'; + panel.id = aclManagerID + '_panel'; + + form = document.createElement('form'); + form.method = 'post'; + form.action = 'javascript:void(0)'; + form.onsubmit = function() { if(this.username && !submitAuthorized) return false; __aclSubmitManager(this); return false; }; + form.name = aclManagerID + '_formobj'; + form.id = aclManagerID + '_formobj_id'; + + back = document.createElement('input'); + back.type = 'button'; + back.value = '< Back'; + back.style.fontWeight = 'normal'; + back.onclick = function() { ajaxACLSwitchToSelector(); return false; }; + back.style.display = 'none'; + back.id = aclManagerID + '_back'; + + saver = document.createElement('input'); + saver.type = 'submit'; + saver.value = 'Next >'; + saver.style.fontWeight = 'bold'; + saver.id = aclManagerID + '_next'; + + closer = document.createElement('input'); + closer.type = 'button'; + closer.value = 'Cancel Changes'; + closer.onclick = function() { if(!confirm('Do you really want to close the ACL manager?')) return false; killACLManager(); return false; } + + spacer1 = document.createTextNode(' '); + spacer2 = document.createTextNode(' '); + + panel.appendChild(back); + panel.appendChild(spacer1); + panel.appendChild(saver); + panel.appendChild(spacer2); + panel.appendChild(closer); + form.appendChild(mainwin); + form.appendChild(panel); + box.appendChild(form); + + body = document.getElementsByTagName('body')[0]; + body.appendChild(box); + setTimeout("document.getElementById('"+aclManagerID+"').style.display = 'block'; opacity('"+aclManagerID+"', 0, 100, 500); opacity('"+aclManagerID + '_panel'+"', 0, 100, 500);", 1000); +} + +function killACLManager() +{ + el = document.getElementById(aclManagerID); + if(el) + { + el.parentNode.removeChild(el); + enlighten(); + } +} + +function __aclSubmitManager(form) +{ + var thefrm = document.forms[form.name]; + var modeobj = form_fetch_field(thefrm, 'mode'); + if ( typeof(modeobj) == 'object' ) + { + var mode = (thefrm.mode.value) ? thefrm.mode.value : 'cant_get'; + } + else + { + var mode = ''; + } + switch(mode) + { + case 'cant_get': + alert('BUG: can\'t get the state value from the form field.'); + break; + case 'seltarget': + var target_type = parseInt(getRadioState(thefrm, 'target_type')); + if(isNaN(target_type)) + { + alert('Please select a target type.'); + return false; + } + target_id = ( target_type == 1 ) ? parseInt(thefrm.group_id.value) : thefrm.username.value; + + obj = { 'mode' : mode, 'target_type' : target_type, 'target_id' : target_id }; + + thispage = strToPageID(title); + do_scopesel = ( thispage[0] == aclDataCache.page_id && thispage[1] == aclDataCache.namespace ); + + if(do_scopesel) + { + scope = getRadioState(thefrm, 'scope'); + if(scope == 'page') + { + pageid = strToPageID(title); + obj['page_id'] = pageid[0]; + obj['namespace'] = pageid[1]; + } + else if(scope == 'global') + { + obj['page_id'] = false; + obj['namespace'] = false; + } + else + { + alert('Invalid scope'); + return false; + } + } + else + { + obj['page_id'] = aclDataCache.page_id; + obj['namespace'] = aclDataCache.namespace; + } + if(target_id == '') + { + alert('Please enter a username.'); + return false; + } + __aclJSONSubmitAjaxHandler(obj); + break; + case 'save_edit': + case 'save_new': + var form = document.forms[aclManagerID + '_formobj']; + selections = new Object(); + for(var i in aclPermList) + { + if(i != 'toJSONString' && i != aclPermList.length-1) + { + selections[aclPermList[i]] = getRadioState(form, aclPermList[i]); + if(!selections[aclPermList[i]]) + { + alert("Invalid return from getRadioState: "+i+": "+selections[i]+" ("+typeof(selections[i])+")"); + return false; + } + } + } + obj = new Object(); + obj['perms'] = selections; + obj['mode'] = mode; + obj['target_type'] = aclDataCache.target_type; + obj['target_id'] = aclDataCache.target_id; + obj['target_name'] = aclDataCache.target_name; + obj['page_id'] = aclDataCache.page_id; + obj['namespace'] = aclDataCache.namespace; + __aclJSONSubmitAjaxHandler(obj); + break; + default: + alert("JSON form submit: invalid mode string "+mode+", stopping execution"); + return false; + break; + } +} + +function getRadioState(form, name) +{ + inputs = form.getElementsByTagName('input'); + radios = new Array(); + for(var i in inputs) + { + if(inputs[i]) if(inputs[i].type == 'radio') + radios.push(inputs[i]); + } + for(var i in radios) + { + if(radios[i].checked && radios[i].name == name) + return radios[i].value; + } + return false; +} + +function __aclSetAllRadios(val) +{ + val = val+''; + form = document.forms[aclManagerID + '_formobj']; + if (!form) + return false; + inputs = form.getElementsByTagName('input'); + radios = new Array(); + for(var i in inputs) + { + if(inputs[i].type == 'radio') + radios.push(inputs[i]); + } + for(var i in radios) + { + if(radios[i].value == val) + radios[i].checked = true; + else + radios[i].checked = false; + } +} + +function __aclDeleteRule() +{ + if(!aclDataCache) return false; + if(aclDataCache.mode != 'seltarget') return false; + parms = { + 'target_type' : aclDataCache.target_type, + 'target_id' : aclDataCache.target_id, + 'target_name' : aclDataCache.target_name, + 'page_id' : aclDataCache.page_id, + 'namespace' : aclDataCache.namespace, + 'mode' : 'delete' + }; + __aclJSONSubmitAjaxHandler(parms); +} + +function array_keys(obj) +{ + keys = new Array(); + for(var i in obj) + keys.push(i); + return keys; +} + +function form_fetch_field(form, name) +{ + var fields = form.getElementsByTagName('input'); + if ( fields.length < 1 ) + return false; + for ( var i = 0; i < fields.length; i++ ) + { + var field = fields[i]; + if ( field.name == name ) + return field; + } + return false; +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/admin-menu.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/admin-menu.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,209 @@ +/* + * Enano - an open source wiki-like CMS + * Copyright (C) 2006-2007 Dan Fuhry + * + * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * Some code found in this script is not licensed under the GNU General Public License; however, it is believed that the license terms shown + * below are GPL-compatible. If you believe that this is not the case, please drop a note to support@enano.homelinux.org. + */ + +/* + * Title: Tigra Tree + * Description: See the demo at url + * URL: http://www.softcomplex.com/products/tigra_tree_menu/ + * Version: 1.1 + * Date: 11-12-2002 (mm-dd-yyyy) + * Notes: This script is free. Visit official site for further details. + * + * There is no license fee or royalty fee to be paid at any time for using the Tigra Tree Menu v1.x + * You may include the source code or modified source code within your own projects for either personal + * or commercial use but excluding the restrictions outlined below. The following restrictions apply to + * all parts of the component, including all source code, samples and documentation. + * + * - Header block of script file (tree.js) CAN NOT be modified or removed. + * - The above items CAN NOT be sold as are, either individually or together. + * - The above items CAN NOT be modified and then sold as a library component, either individually or together. + */ + +var ck = readCookie('admin_menu_state'); +if(ck) +{ + var ck = parseInt(ck); +} +else +{ + ck = 0; +} +ck = ( isNaN(ck) ) ? 0 : ck; + +function tree (a_items, a_template) { + + this.a_tpl = a_template; + this.a_config = a_items; + this.o_root = this; + this.a_index = []; + this.o_selected = null; + this.n_depth = -1; + + var o_icone = new Image(), + o_iconl = new Image(); + o_icone.src = a_template['icon_e']; + o_iconl.src = a_template['icon_l']; + a_template['im_e'] = o_icone; + a_template['im_l'] = o_iconl; + for (var i = 0; i < 64; i++) + if (a_template['icon_' + i]) { + var o_icon = new Image(); + a_template['im_' + i] = o_icon; + o_icon.src = a_template['icon_' + i]; + } + + this.toggle = function (n_id,co) { var o_item = this.a_index[n_id]; o_item.open(o_item.b_opened,co); }; + this.open = function (n_id,co) { var o_item = this.a_index[n_id]; o_item.open(false,co); }; + this.select = function (n_id) { return this.a_index[n_id].select(); }; + this.mout = function (n_id) { this.a_index[n_id].upstatus(true) }; + this.mover = function (n_id) { this.a_index[n_id].upstatus() }; + + this.a_children = []; + for (var i = 0; i < a_items.length; i++) + { + new tree_item(this, i); + } + + this.n_id = trees.length; + trees[this.n_id] = this; + + for (var i = 0; i < this.a_children.length; i++) { + document.write(this.a_children[i].init()); + this.a_children[i].open(false, true); + } +} +function tree_item (o_parent, n_order) { + + this.n_depth = o_parent.n_depth + 1; + this.a_config = o_parent.a_config[n_order + (this.n_depth ? 2 : 0)]; + if (!this.a_config) return; + + this.o_root = o_parent.o_root; + this.o_parent = o_parent; + this.n_order = n_order; + this.b_opened = !this.n_depth; + + this.n_id = this.o_root.a_index.length; + this.o_root.a_index[this.n_id] = this; + o_parent.a_children[n_order] = this; + + this.a_children = []; + for (var i = 0; i < this.a_config.length - 2; i++) + { + new tree_item(this, i); + } + + this.get_icon = item_get_icon; + this.open = item_open; + this.select = item_select; + this.init = item_init; + this.upstatus = item_upstatus; + this.is_last = function () { return this.n_order == this.o_parent.a_children.length - 1 }; + + // CODE MODIFICATION + // added: + // Do we need to open the branch? + n = Math.pow(2, this.n_id); + var disp = ( ck & n ) ? true : false; + s = ( disp ) ? 'open' : 'closed'; + //if(s=='open') alert(this.n_id + ': ' + s); + if(disp) setTimeout('trees['+trees.length+'].open('+this.n_id+', true);', 10); + // END MODIFICATIONS +} + +function item_open (b_close, nocookie) { + //alert('item_open('+this.n_id+');'); + var o_idiv = get_element('i_div' + this.o_root.n_id + '_' + this.n_id); + if (!o_idiv) return; + + if (!o_idiv.innerHTML) { + var a_children = []; + for (var i = 0; i < this.a_children.length; i++) + { + a_children[i]= this.a_children[i].init(); + } + o_idiv.innerHTML = a_children.join(''); + } + o_idiv.style.display = (b_close ? 'none' : 'block'); + + // CODE MODIFICATION + // added: + if(!nocookie) + { + // The idea here is to use a bitwise field. Nice 'n simple, right? Object of the game is to assemble + // a binary number that depicts the open/closed state of the entire menu in one cookie. + n = Math.pow(2, this.n_id); + ck = ( b_close ) ? ck-n : ck+n; + //alert('open(): doing the cookie routine for id '+this.n_id+"\nResult for bitwise op: "+ck); + createCookie('admin_menu_state', ck, 365); + } else { + //alert('open(): NOT doing the cookie routine for id '+this.n_id); + } + // END MODIFICATIONS + + this.b_opened = !b_close; + var o_jicon = document.images['j_img' + this.o_root.n_id + '_' + this.n_id], + o_iicon = document.images['i_img' + this.o_root.n_id + '_' + this.n_id]; + if (o_jicon) o_jicon.src = this.get_icon(true); + if (o_iicon) o_iicon.src = this.get_icon(); + this.upstatus(); +} + +function item_select (b_deselect) { + if (!b_deselect) { + var o_olditem = this.o_root.o_selected; + this.o_root.o_selected = this; + if (o_olditem) o_olditem.select(true); + } + var o_iicon = document.images['i_img' + this.o_root.n_id + '_' + this.n_id]; + if (o_iicon) o_iicon.src = this.get_icon(); + get_element('i_txt' + this.o_root.n_id + '_' + this.n_id).style.fontWeight = b_deselect ? 'normal' : 'bold'; + + this.upstatus(); + return Boolean(this.a_config[1]); +} + +function item_upstatus (b_clear) { + window.setTimeout('window.status="' + (b_clear ? '' : this.a_config[0] + (this.a_config[1] ? ' ('+ this.a_config[1] + ')' : '')) + '"', 10); +} + +function item_init () { + var a_offset = [], + o_current_item = this.o_parent; + for (var i = this.n_depth; i > 1; i--) { + a_offset[i] = ''; + o_current_item = o_current_item.o_parent; + } + return '
' + (this.n_depth ? a_offset.join('') + (this.a_children.length + ? '' + : '') : '') + // CODE MODIFICATION + // removed: + // + '' + this.a_config[0] + '
' + (this.a_children.length ? '' : ''); + // added: + + '' + this.a_config[0] + '' + (this.a_children.length ? '' : ''); + // END MODIFICATIONS + alert('i_div' + this.o_root.n_id + '_' + this.n_id); +} + +function item_get_icon (b_junction) { + return this.o_root.a_tpl['icon_' + ((this.n_depth ? 0 : 32) + (this.a_children.length ? 16 : 0) + (this.a_children.length && this.b_opened ? 8 : 0) + (!b_junction && this.o_root.o_selected == this ? 4 : 0) + (b_junction ? 2 : 0) + (b_junction && this.is_last() ? 1 : 0))]; +} + +var trees = []; +get_element = document.all ? + function (s_id) { return document.all[s_id] } : + function (s_id) { return document.getElementById(s_id) }; + + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/ajax.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/ajax.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,571 @@ +/* + * AJAX applets + */ + +function ajaxGet(uri, f) { + if (window.XMLHttpRequest) { + ajax = new XMLHttpRequest(); + } else { + if (window.ActiveXObject) { + ajax = new ActiveXObject("Microsoft.XMLHTTP"); + } else { + alert('Enano client-side runtime error: No AJAX support, unable to continue'); + return; + } + } + ajax.onreadystatechange = f; + ajax.open('GET', uri, true); + ajax.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" ); + ajax.send(null); +} + +function ajaxPost(uri, parms, f) { + if (window.XMLHttpRequest) { + ajax = new XMLHttpRequest(); + } else { + if (window.ActiveXObject) { + ajax = new ActiveXObject("Microsoft.XMLHTTP"); + } else { + alert('Enano client-side runtime error: No AJAX support, unable to continue'); + return; + } + } + ajax.onreadystatechange = f; + ajax.open('POST', uri, true); + ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + ajax.setRequestHeader("Content-length", parms.length); + ajax.setRequestHeader("Connection", "close"); + ajax.send(parms); +} + +function ajaxEscape(text) +{ + text = escape(text); + text = text.replace(/\+/g, '%2B', text); + return text; +} + +// Page editor + +function ajaxEditor() { + setAjaxLoading(); + ajaxGet(stdAjaxPrefix+'&_mode=getsource', function() { + if(ajax.readyState == 4) { + unsetAjaxLoading(); + if(edit_open) { + c=confirm('Do you really want to revert your changes?'); + if(!c) return; + } + edit_open = true; + selectButtonMajor('article'); + selectButtonMinor('edit'); + if(in_array('ajaxEditArea', grippied_textareas)) + { + // Allow the textarea grippifier to re-create the resizer control on the textarea + grippied_textareas.pop(in_array('ajaxEditArea', grippied_textareas)); + } + disableUnload('If you do, any changes that you have made to this page will be lost.'); + var switcher = ( readCookie('enano_editor_mode') == 'tinymce' ) ? + 'wikitext editor | graphical editor' : + 'wikitext editor | graphical editor' ; + document.getElementById('ajaxEditContainer').innerHTML = '\ +
\ + ' + switcher + '
\ +
\ +
\ + Edit summary:
\ +
\ + save changes | preview changes | revert changes | discard changes | formatting help\ +
\ + '+editNotice+'\ +
'; + // initTextareas(); + if(readCookie('enano_editor_mode') == 'tinymce') + { + $('ajaxEditArea').switchToMCE(); + } + } + }); +} + +function setEditorMCE() +{ + $('ajaxEditArea').switchToMCE(); + createCookie('enano_editor_mode', 'tinymce', 365); + $('switcher').object.innerHTML = 'wikitext editor | graphical editor'; +} + +function setEditorText() +{ + $('ajaxEditArea').destroyMCE(); + createCookie('enano_editor_mode', 'text', 365); + $('switcher').object.innerHTML = 'wikitext editor | graphical editor'; +} + +function ajaxViewSource() { + setAjaxLoading(); + ajaxGet(stdAjaxPrefix+'&_mode=getsource', function() { + if(ajax.readyState == 4) { + unsetAjaxLoading(); + if(edit_open) { + c=confirm('Do you really want to revert your changes?'); + if(!c) return; + } + edit_open = true; + selectButtonMajor('article'); + selectButtonMinor('edit'); + if(in_array('ajaxEditArea', grippied_textareas)) + { + // Allow the textarea grippifier to re-create the resizer control on the textarea + grippied_textareas.pop(in_array('ajaxEditArea', grippied_textareas)); + } + document.getElementById('ajaxEditContainer').innerHTML = '\ +
\ +
\ + close viewer\ +
'; + initTextareas(); + } + }); +} + +function ajaxShowPreview() +{ + goBusy('Loading preview...'); + var text = ajaxEscape($('ajaxEditArea').getContent()); + if(document.mdgAjaxEditor.minor.checked) minor='&minor'; + else minor=''; + ajaxPost(stdAjaxPrefix+'&_mode=preview', 'summary='+document.getElementById('ajaxEditSummary').value+minor+'&text='+text, function() { + if(ajax.readyState == 4) { + unBusy(); + edit_open = false; + document.getElementById('mdgPreviewContainer').innerHTML = ajax.responseText; + } + }); +} + +function ajaxSavePage() { + goBusy('Saving page...'); + var text = ajaxEscape($('ajaxEditArea').getContent()); + if(document.mdgAjaxEditor.minor.checked) minor='&minor'; + else minor=''; + ajaxPost(stdAjaxPrefix+'&_mode=savepage', 'summary='+document.getElementById('ajaxEditSummary').value+minor+'&text='+text, function() { + if(ajax.readyState == 4) { + unBusy(); + edit_open = false; + document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText; + enableUnload(); + unselectAllButtonsMinor(); + } + }); +} + +function ajaxDiscard() { + c = confirm('Do you really want to discard your changes?'); + if(!c) return; + ajaxReset(); +} + +function ajaxReset() { + enableUnload(); + setAjaxLoading(); + ajaxGet(stdAjaxPrefix+'&_mode=getpage&noheaders', function() { + if(ajax.readyState == 4) { + unsetAjaxLoading(); + edit_open = false; + document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText; + selectButtonMajor('article'); + unselectAllButtonsMinor(); + } + }); +} + +// Miscellaneous AJAX applets + +function ajaxProtect(l) { + if(shift) { + r = 'NO_REASON'; + } else { + r = prompt('Reason for (un)protecting:'); + if(!r || r=='') return; + } + setAjaxLoading(); + document.getElementById('protbtn_0').style.textDecoration = 'none'; + document.getElementById('protbtn_1').style.textDecoration = 'none'; + document.getElementById('protbtn_2').style.textDecoration = 'none'; + document.getElementById('protbtn_'+l).style.textDecoration = 'underline'; + ajaxPost(stdAjaxPrefix+'&_mode=protect', 'reason='+escape(r)+'&level='+l, function() { + if(ajax.readyState == 4) { + unsetAjaxLoading(); + if(ajax.responseText != 'good') + alert(ajax.responseText); + } + }); +} + +function ajaxRename() { + r = prompt('What title should this page be renamed to?\nNote: This does not and will never change the URL of this page, that must be done from the admin panel.'); + if(!r || r=='') return; + setAjaxLoading(); + ajaxPost(stdAjaxPrefix+'&_mode=rename', 'newtitle='+escape(r), function() { + if(ajax.readyState == 4) { + unsetAjaxLoading(); + alert(ajax.responseText); + } + }); +} + +function ajaxMakePage() { + setAjaxLoading(); + ajaxPost(ENANO_SPECIAL_CREATEPAGE, ENANO_CREATEPAGE_PARAMS, function() { + if(ajax.readyState == 4) { + unsetAjaxLoading(); + window.location.reload(); + } + }); +} + +function ajaxDeletePage() { + c = confirm('You are about to DESTROY this page. Do you REALLY want to do this?'); + if(!c) return; + c = confirm('You\'re ABSOLUTELY sure???'); + if(!c) return; + setAjaxLoading(); + ajaxGet(stdAjaxPrefix+'&_mode=deletepage', function() { + if(ajax.readyState == 4) { + unsetAjaxLoading(); + alert(ajax.responseText); + window.location.reload(); + } + }); +} + +function ajaxDelVote() { + c = confirm('Are you sure that you want to vote that this page be deleted?'); + if(!c) return; + setAjaxLoading(); + ajaxGet(stdAjaxPrefix+'&_mode=delvote', function() { + if(ajax.readyState == 4) { + unsetAjaxLoading(); + alert(ajax.responseText); + } + }); +} + +function ajaxResetDelVotes() { + c = confirm('This will reset the number of votes against this page to zero. Do you really want to do this?'); + if(!c) return; + setAjaxLoading(); + ajaxGet(stdAjaxPrefix+'&_mode=resetdelvotes', function() { + if(ajax.readyState == 4) { + unsetAjaxLoading(); + alert(ajax.responseText); + item = document.getElementById('mdgDeleteVoteNoticeBox'); + if(item) + { + opacity('mdgDeleteVoteNoticeBox', 100, 0, 1000); + setTimeout("document.getElementById('mdgDeleteVoteNoticeBox').style.display = 'none';", 1000); + } + } + }); +} + +function ajaxSetWikiMode(val) { + setAjaxLoading(); + document.getElementById('wikibtn_0').style.textDecoration = 'none'; + document.getElementById('wikibtn_1').style.textDecoration = 'none'; + document.getElementById('wikibtn_2').style.textDecoration = 'none'; + document.getElementById('wikibtn_'+val).style.textDecoration = 'underline'; + ajaxGet(stdAjaxPrefix+'&_mode=setwikimode&mode='+val, function() { + if(ajax.readyState == 4) { + unsetAjaxLoading(); + if(ajax.responseText!='GOOD') + { + alert(ajax.responseText); + } + } + }); +} + +// Editing/saving category information +// This was not easy to write, I hope enjoy it, and dang I swear I'm gonna +// find someone to work on just the Javascript part of Enano... + +function ajaxCatEdit() { + setAjaxLoading(); + ajaxGet(stdAjaxPrefix+'&_mode=catedit', function() { + if(ajax.readyState == 4) { + unsetAjaxLoading(); + edit_open = false; + eval(ajax.responseText); + } + }); +} + +function ajaxCatSave() +{ + if(!catlist) + { + alert('Var catlist has no properties'); + return; + } + query=''; + for(i=0;i= timelist.length ) break; + arrDiff2Buttons[i].style.display = 'none'; + } +} + +function selectDiff1Button(obj) +{ + var this_time = obj.id.substr(6); + var index = parseInt(in_array(this_time, timelist)); + for ( var i = 0; i < timelist.length - 1; i++ ) + { + if ( i < timelist.length - 1 ) + { + var state = ( i < index ) ? 'inline' : 'none'; + var id = 'diff2_' + timelist[i]; + document.getElementById(id).style.display = state; + + // alert("Debug:\nIndex: "+index+"\nState: "+state+"\ni: "+i); + } + } +} + +function selectDiff2Button(obj) +{ + var this_time = obj.id.substr(6); + var index = parseInt(in_array(this_time, timelist)); + for ( var i = 1; i < timelist.length; i++ ) + { + if ( i < timelist.length - 1 ) + { + var state = ( i > index ) ? 'inline' : 'none'; + var id = 'diff1_' + timelist[i]; + document.getElementById(id).style.display = state; + + // alert("Debug:\nIndex: "+index+"\nState: "+state+"\ni: "+i); + } + } +} + +function ajaxHistDiff() +{ + var id1=false; + var id2=false; + for ( i = 0; i < arrDiff1Buttons.length; i++ ) + { + k = i + ''; + kpp = i + 1; + kpp = kpp + ''; + if(arrDiff1Buttons[k].checked) id1 = arrDiff1Buttons[k].id.substr(6); + if(arrDiff2Buttons[k].checked) id2 = arrDiff2Buttons[k].id.substr(6); + } + if(!id1 || !id2) { alert('BUG: Couldn\'t get checked radiobutton state'); return; } + setAjaxLoading(); + ajaxGet(stdAjaxPrefix+'&_mode=pagediff&diff1='+id1+'&diff2='+id2, function() + { + if(ajax.readyState==4) + { + unsetAjaxLoading(); + document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText; + } + }); +} + +// Change the user's preferred style/theme + +function ajaxChangeStyle() +{ + var win = document.getElementById("cn2"); + win.innerHTML = ' \ +
\ +

Select a theme...

\ + \ +
\ +

\ + \ + \ + \ +
\ + '; + ajaxGetStyles(ENANO_CURRENT_THEME); + jws.openWin('root2', 340, 300); +} + +function ajaxGetStyles(id) { + setAjaxLoading(); + ajaxGet(stdAjaxPrefix+'&_mode=getstyles&id='+id, function() { + if(ajax.readyState == 4) { + unsetAjaxLoading(); + eval(ajax.responseText); + html = '

And a style...

'; + document.getElementById('styleSelector').innerHTML = html; + document.getElementById('styleSubmitter').style.display = 'inline'; + } + }); +} + +function ajaxSwapCSS() { + setAjaxLoading(); + if(_css) { + document.getElementById('mdgCss').href = main_css; + _css = false; + } else { + document.getElementById('mdgCss').href = print_css; + _css = true; + } + unsetAjaxLoading(); + menuOff(); +} + +function ajaxSetPassword() +{ + pass = hex_sha1(document.getElementById('mdgPassSetField').value); + setAjaxLoading(); + ajaxPost(stdAjaxPrefix+'&_mode=setpass', 'password='+pass, function() + { + unsetAjaxLoading(); + if(ajax.readyState==4) + { + alert(ajax.responseText); + } + } + ); +} + +function ajaxWikiEditHelp() +{ + jws.openWin('root3', 640, 480); + setAjaxLoading(); + ajaxGet(stdAjaxPrefix+'&_mode=wikihelp', function() { + if(ajax.readyState==4) + { + unsetAjaxLoading(); + document.getElementById('cn3').innerHTML = ajax.responseText; + } + }); +} + +function ajaxStartLogin() +{ + ajaxPromptAdminAuth(function(k) { + window.location.reload(); + }, 2); +} + +function ajaxAdminPage() +{ + if ( auth_level < USER_LEVEL_ADMIN ) + { + ajaxPromptAdminAuth(function(k) { + ENANO_SID = k; + auth_level = USER_LEVEL_ADMIN; + var loc = String(window.location + ''); + window.location = append_sid(loc); + var loc = makeUrlNS('Special', 'Administration', 'module=' + namespace_list['Admin'] + 'PageManager&source=ajax&page_id=' + ajaxEscape(title)); + if ( (ENANO_SID + ' ').length > 1 ) + window.location = loc; + }, 9); + return false; + } + var loc = makeUrlNS('Special', 'Administration', 'module=' + namespace_list['Admin'] + 'PageManager&source=ajax&page_id=' + ajaxEscape(title)); + window.location = loc; +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/autocomplete.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/autocomplete.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,308 @@ +/* + * Auto-completing page/username fields + */ + +// The ultimate Javascript app: AJAX auto-completion, which responds to up/down arrow keys, the enter key, and the escape key +// The idea was pilfered mercilessly from vBulletin, but uses about 8 +// bytes of vB code. All the rest was coded by me, Mr. Javascript Newbie... + +// ...in about 8 hours. +// You folks better like this stuff. + +function nameCompleteEventHandler(e) +{ + if(!e) e = window.event; + switch(e.keyCode) + { + case 38: // up + unSelectMove('up'); + break; + case 40: // down + unSelectMove('down'); + break; + case 27: // escape + case 9: // tab + destroyUsernameDropdowns(); + break; + case 13: // enter + unSelect(); + break; + default: return false; break; + } + return true; +} + +function unSelectMove(dir) +{ + if(submitAuthorized) return false; + var thediv = document.getElementById(unObjDivCurrentId); + thetable = thediv.firstChild; + cel = thetable.firstChild.firstChild; + d = true; + index = false; + changed = false; + // Object of the game: extract the username, determine its index in the userlist array, and then color the menu items and set unObjCurrentSelection + while(d) // Set to false if an exception occurs or if we arrive at our destination + { + //* + if(!cel) d=false; + celbak = cel; + cel = cel.nextSibling; + if(!cel) d=false; + try { + if(cel.firstChild.nextSibling) html = cel.firstChild.nextSibling.innerHTML; + else html = cel.firstChild.innerHTML; + cel.firstChild.className = 'row1'; + if(cel.firstChild.nextSibling) cel.firstChild.nextSibling.className = 'row1'; + thename = html.substr(7, html.length-15); + // FINALLY! we have extracted the username + // Now get its position in the userlist array + if(thename == unObjCurrentSelection) + { + index = parseInt(in_array(thename, userlist)); + } + if(typeof(index) == 'number') + { + if(dir=='down') + n = index+1; + else if(dir == 'up') + n = index - 1; + + // Try to trap moving the selection up or down beyond the top of bottom + if(n > userlist.length-1 || n < 0) + { + cel.firstChild.className = 'row2'; + if(cel.firstChild.nextSibling) cel.firstChild.nextSibling.className = 'row2'; + return; + } + + if(dir=='down') no=cel.nextSibling; + else if(dir=='up') no=cel.previousSibling; + no.firstChild.className = 'row2'; + if(no.firstChild.nextSibling) no.firstChild.nextSibling.className = 'row2'; + if(no.firstChild.id) + { + scroll = getScrollOffset() + getHeight(); + elemht = getElementHeight(no.firstChild.id); + elemoff = fetch_offset(no.firstChild); + whereto = elemoff['top'] + elemht; + if(whereto > scroll) + { + window.location.hash = '#'+no.firstChild.id; + unObj.focus(); + } + } + cel=cel.nextSibling; + unObjCurrentSelection = userlist[n]; + index = false; + changed = true; + return; + } + } catch(e) { } + //*/ d = false; + } +} + +function unSelect() +{ + if(!unObj || submitAuthorized) return false; + if ( unObjCurrentSelection ) + unObj.value = unObjCurrentSelection; + destroyUsernameDropdowns(); +} + +function in_array(needle, haystack) +{ + for(var i in haystack) + { + if(haystack[i] == needle) return i; + } + return false; +} + +function ajaxUserNameComplete(o) +{ + if(!o) {destroyUsernameDropdowns(); return;} + if(!o.value) {destroyUsernameDropdowns(); return;} + if(o.value.length < 3) {destroyUsernameDropdowns(); return;} + //if(IE) return; // This control doesn't work in IE. Yes, it's true! document.createElement doesn't work. + if(!o.id) + { + o.id = 'usernametextboxobj_' + Math.floor(Math.random() * 10000000); + } + unObj = o; + o.setAttribute("autocomplete","off"); + o.onkeyup = function(e, o) { o=unObj; if(!nameCompleteEventHandler(e)) ajaxUserNameComplete(o); } + val = escape(o.value).replace('+', '%2B'); + ajaxGet(stdAjaxPrefix+'&_mode=fillusername&name='+val, function() + { + if(ajax.readyState==4) + { + // Determine the appropriate left/top positions, then create a div to use for the drop-down list + // The trick here is to be able to make the div dynamically destroy itself depending on how far the user's mouse is from it + destroyUsernameDropdowns(); + off = fetch_offset(unObj); + dim = fetch_dimensions(unObj); + left = off['left']; + i1 = off['top']; + i2 = dim['h']; + var top = 0; + top = i1 + i2; + var thediv = document.createElement('div'); + thediv.className = 'tblholder'; + thediv.style.marginTop = '0px'; + thediv.style.position = 'absolute'; + thediv.style.top = top + 'px'; + thediv.style.left = left + 'px'; + thediv.style.zIndex = getHighestZ() + 2; + id = 'usernamehoverobj_' + Math.floor(Math.random() * 10000000); + unObjDivCurrentId = id; + thediv.id = id; + unObj.onblur = function() { destroyUsernameDropdowns(); } + + eval(ajax.responseText); + if(errorstring) + { + html = ''+errorstring+''; + } + else + { + html = ''; + cls = 'row2'; + unObjCurrentSelection = userlist[0]; + for(i=0;i'; + if(cls=='row2') cls='row1'; + } + html = html + '
Username matches
'+userlist[i]+'
'; + } + + thediv.innerHTML = html; + var body = document.getElementsByTagName('body'); + body = body[0]; + unSelectMenuOn = true; + submitAuthorized = false; + body.appendChild(thediv); + } + }); +} + +function ajaxPageNameComplete(o) +{ + if(!o) {destroyUsernameDropdowns(); return;} + if(!o.value) {destroyUsernameDropdowns(); return;} + if(o.value.length < 3) {destroyUsernameDropdowns(); return;} + if(IE) return; // This control doesn't work in IE. Yes, it's true! document.createElement doesn't work. + if(!o.id) + { + o.id = 'usernametextboxobj_' + Math.floor(Math.random() * 10000000); + } + o.setAttribute("autocomplete","off"); + unObj = o; + o.onkeyup = function(e, o) { o=unObj; if(!nameCompleteEventHandler(e)) ajaxPageNameComplete(o); } + val = escape(o.value).replace('+', '%2B'); + ajaxGet(stdAjaxPrefix+'&_mode=fillpagename&name='+val, function() + { + if(!ajax) return; + if(ajax.readyState==4) + { + // Determine the appropriate left/top positions, then create a div to use for the drop-down list + // The trick here is to be able to make the div dynamically destroy itself depending on how far the user's mouse is from it + destroyUsernameDropdowns(); + off = fetch_offset(unObj); + dim = fetch_dimensions(unObj); + left = off['left']; + top = off['top'] + dim['h']; + var thediv = document.createElement('div'); + thediv.className = 'tblholder'; + thediv.style.marginTop = '0px'; + thediv.style.position = 'absolute'; + thediv.style.top = top + 'px'; + thediv.style.left = left + 'px'; + thediv.style.zIndex = getHighestZ() + 2; + id = 'usernamehoverobj_' + Math.floor(Math.random() * 10000000); + unObjDivCurrentId = id; + thediv.id = id; + + eval(ajax.responseText); + if(errorstring) + { + html = ''+errorstring+''; + } + else + { + html = ''; + cls = 'row2'; + unObjCurrentSelection = userlist[0]; + for(i=0;i'; + if(cls=='row2') cls='row1'; + } + html = html + '
Page name matches
Page titlePage ID
'+namelist[i]+''+userlist[i]+'
'; + } + + thediv.innerHTML = html; + var body = document.getElementsByTagName('body'); + body = body[0]; + unSelectMenuOn = true; + submitAuthorized = false; + body.appendChild(thediv); + + unObj.onblur = function() { CheckDestroyUsernameDropdowns(thediv.id); }; + } + }); +} + +function CheckDestroyUsernameDropdowns(id) +{ + elem = document.getElementById(id); + if(!elem) return; + if(queryOnObj(elem, 100)) + { + destroyUsernameDropdowns(); + } +} + +function destroyUsernameDropdowns() +{ + var divs = document.getElementsByTagName('div'); + var prefix = 'usernamehoverobj_'; + for(i=0;i= base64Str.length) return END_OF_INPUT; + var c = base64Str.charCodeAt(base64Count) & 0xff; + base64Count++; + return c; +} +function encodeBase64(str){ + setBase64Str(str); + var result = ''; + var inBuffer = new Array(3); + var lineCount = 0; + var done = false; + while (!done && (inBuffer[0] = readBase64()) != END_OF_INPUT){ + inBuffer[1] = readBase64(); + inBuffer[2] = readBase64(); + result += (base64Chars[ inBuffer[0] >> 2 ]); + if (inBuffer[1] != END_OF_INPUT){ + result += (base64Chars [(( inBuffer[0] << 4 ) & 0x30) | (inBuffer[1] >> 4) ]); + if (inBuffer[2] != END_OF_INPUT){ + result += (base64Chars [((inBuffer[1] << 2) & 0x3c) | (inBuffer[2] >> 6) ]); + result += (base64Chars [inBuffer[2] & 0x3F]); + } else { + result += (base64Chars [((inBuffer[1] << 2) & 0x3c)]); + result += ('='); + done = true; + } + } else { + result += (base64Chars [(( inBuffer[0] << 4 ) & 0x30)]); + result += ('='); + result += ('='); + done = true; + } + lineCount += 4; + if (lineCount >= 76){ + result += ('\n'); + lineCount = 0; + } + } + return result; +} +function readReverseBase64(){ + if (!base64Str) return END_OF_INPUT; + while (true){ + if (base64Count >= base64Str.length) return END_OF_INPUT; + var nextCharacter = base64Str.charAt(base64Count); + base64Count++; + if (reverseBase64Chars[nextCharacter]){ + return reverseBase64Chars[nextCharacter]; + } + if (nextCharacter == 'A') return 0; + } + return END_OF_INPUT; +} + +function ntos(n){ + n=n.toString(16); + if (n.length == 1) n="0"+n; + n="%"+n; + return unescape(n); +} + +function decodeBase64(str){ + setBase64Str(str); + var result = ""; + var inBuffer = new Array(4); + var done = false; + while (!done && (inBuffer[0] = readReverseBase64()) != END_OF_INPUT + && (inBuffer[1] = readReverseBase64()) != END_OF_INPUT){ + inBuffer[2] = readReverseBase64(); + inBuffer[3] = readReverseBase64(); + result += ntos((((inBuffer[0] << 2) & 0xff)| inBuffer[1] >> 4)); + if (inBuffer[2] != END_OF_INPUT){ + result += ntos((((inBuffer[1] << 4) & 0xff)| inBuffer[2] >> 2)); + if (inBuffer[3] != END_OF_INPUT){ + result += ntos((((inBuffer[2] << 6) & 0xff) | inBuffer[3])); + } else { + done = true; + } + } else { + done = true; + } + } + return result; +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/comments.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/comments.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,569 @@ +// Comments + +var comment_template = false; + +function ajaxComments(parms) { + setAjaxLoading(); + var pid = strToPageID(title); + if(!parms) + { + var parms = { + 'mode' : 'fetch' + }; + } + parms.page_id = pid[0]; + parms.namespace = pid[1]; + if(comment_template) + parms.have_template = true; + parms = ajaxEscape(toJSONString(parms)); + ajaxPost(stdAjaxPrefix+'&_mode=comments', 'data=' + parms, function() { + if(ajax.readyState == 4) { + unsetAjaxLoading(); + selectButtonMajor('discussion'); + unselectAllButtonsMinor(); + // IE compatibility - doing ajax.responseText.substr() doesn't work + var rsptxt = ajax.responseText + ''; + if ( rsptxt.substr(0, 1) != '{' ) + { + document.getElementById('ajaxEditContainer').innerHTML = '

Comment system Javascript runtime: invalid JSON response from server, response text:

' + ajax.responseText + '
'; + return false; + } + var response = parseJSON(ajax.responseText); + switch(response.mode) + { + case 'fetch': + document.getElementById('ajaxEditContainer').innerHTML = 'Rendering response...'; + if(response.template) + comment_template = response.template; + setAjaxLoading(); + renderComments(response); + unsetAjaxLoading(); + break; + case 'redraw': + redrawComment(response); + break; + case 'annihilate': + annihiliateComment(response.id); + break; + case 'materialize': + alert('Your comment has been posted. If it does not appear right away, it is probably awaiting approval.'); + hideCommentForm(); + materializeComment(response); + break; + case 'error': + alert(response.error); + break; + default: + alert(ajax.responseText); + break; + } + } + }); +} + +function renderComments(data) +{ + + var html = ''; + + // Header + + html += '

Article Comments

'; + + var ns = ( strToPageID(title)[1]=='Article' ) ? 'article' : ( strToPageID(title)[1].toLowerCase() ) + ' page'; + + // Counters + if ( data.auth_mod_comments ) + { + var cnt = ( data.auth_mod_comments ) ? data.count_total : data.count_appr; + if ( cnt == 0 ) cnt = 'no'; + var s = ( cnt == 1 ) ? '' : 's'; + var is = ( cnt == 1 ) ? 'is' : 'are'; + html += "

There "+is+" " + cnt + " comment"+s+" on this "+ns+"."; + if ( data.count_unappr > 0 ) + { + html += ' ' + data.count_unappr + ' of those are unapproved.'; + } + html += '

'; + } + else + { + var cnt = data.count_appr; + if ( cnt == 0 ) cnt = 'no'; + var s = ( cnt == 1 ) ? '' : 's'; + var is = ( cnt == 1 ) ? 'is' : 'are'; + html += "

There "+is+" " + cnt + " comment"+s+" on this "+ns+"."; + if ( data.count_unappr > 0 ) + { + var s = ( data.count_unappr == 1 ) ? '' : 's'; + var is = ( data.count_unappr == 1 ) ? 'is' : 'are'; + html += ' However, there '+is+' '+data.count_unappr+' additional comment'+s+' awaiting approval.'; + } + html += '

'; + } + + // Comment display + + if ( data.count_total > 0 ) + { + var parser = new templateParser(comment_template); + for ( var i = 0; i < data.comments.length; i++ ) + { + var tplvars = new Object(); + + if ( data.comments[i].approved != '1' && !data.auth_mod_comments ) + continue; + + tplvars.ID = i; + tplvars.DATETIME = data.comments[i].time; + tplvars.SUBJECT = data.comments[i].subject; + tplvars.DATA = data.comments[i].comment_data; + tplvars.SIGNATURE = data.comments[i].signature; + + if ( data.comments[i].approved != '1' ) + tplvars.SUBJECT += ' (Unapproved)'; + + // Name + tplvars.NAME = data.comments[i].name; + if ( data.comments[i].user_id > 1 ) + tplvars.NAME = '' + data.comments[i].name + ''; + + // User level + tplvars.USER_LEVEL = 'Guest'; + if ( data.comments[i].user_level >= data.user_level.member ) tplvars.USER_LEVEL = 'Member'; + if ( data.comments[i].user_level >= data.user_level.mod ) tplvars.USER_LEVEL = 'Moderator'; + if ( data.comments[i].user_level >= data.user_level.admin ) tplvars.USER_LEVEL = 'Administrator'; + + // Send PM link + tplvars.SEND_PM_LINK=(data.comments[i].user_id>1)?'Send private message
':''; + + // Add buddy link + tplvars.ADD_BUDDY_LINK=(data.comments[i].user_id>1)?'Add to buddy list
':''; + + // Edit link + tplvars.EDIT_LINK='edit'; + + // Delete link + tplvars.DELETE_LINK='delete'; + + // Moderation: (Un)approve link + var appr = ( data.comments[i].approved == 1 ) ? 'Unapprove' : 'Approve'; + tplvars.MOD_APPROVE_LINK=''+appr+''; + + // Moderation: Delete post link + tplvars.MOD_DELETE_LINK='Delete'; + + var tplbool = new Object(); + + tplbool.signature = ( data.comments[i].signature == '' ) ? false : true; + tplbool.can_edit = ( data.auth_edit_comments && ( ( data.comments[i].user_id == data.user_id && data.logged_in ) || data.auth_mod_comments ) ); + tplbool.auth_mod = data.auth_mod_comments; + + parser.assign_vars(tplvars); + parser.assign_bool(tplbool); + + html += '
' + parser.run() + '
'; + } + } + + // Posting form + + html += '

Got something to say?

'; + html += '

If you have comments or suggestions on this article, you can shout it out here.'; + if ( data.approval_needed ) + html+=' Before your post will be visible to the public, a moderator will have to approve it.'; + html += ' Leave a comment...

'; + html += ''; + + document.getElementById('ajaxEditContainer').innerHTML = html; + + for ( i = 0; i < data.comments.length; i++ ) + { + document.getElementById('comment_source_'+i).value = data.comments[i].comment_source; + } + +} + +function displayCommentForm() +{ + document.getElementById('leave_comment_button').style.display = 'none'; + document.getElementById('comment_form').style.display = 'block'; +} + +function hideCommentForm() +{ + document.getElementById('leave_comment_button').style.display = 'inline'; + document.getElementById('comment_form').style.display = 'none'; +} + +function editComment(id, link) +{ + var ctr = document.getElementById('subject_'+id); + var subj = trim(ctr.firstChild.nodeValue); // If there's a span in there that says 'unapproved', this eliminates it + ctr.innerHTML = ''; + var ipt = document.createElement('input'); + ipt.id = 'subject_edit_'+id; + ipt.value = subj; + ctr.appendChild(ipt); + + var src = document.getElementById('comment_source_'+id).value; + var cmt = document.getElementById('comment_'+id); + cmt.innerHTML = ''; + var ta = document.createElement('textarea'); + ta.rows = '10'; + ta.cols = '40'; + ta.value = src; + ta.id = 'comment_edit_'+id; + cmt.appendChild(ta); + + link.style.fontWeight = 'bold'; + link.innerHTML = 'save'; + link.onclick = function() { var id = this.id.substr(this.id.indexOf('_')+1); saveComment(id, this); return false; }; +} + +function saveComment(id, link) +{ + var data = document.getElementById('comment_edit_'+id).value; + var subj = document.getElementById('subject_edit_'+id).value; + var div = document.getElementById('comment_holder_'+id); + var real_id = div.getElementsByTagName('input')[0]['value']; + var req = { + 'mode' : 'edit', + 'id' : real_id, + 'local_id' : id, + 'data' : data, + 'subj' : subj + }; + link.style.fontWeight = 'normal'; + link.innerHTML = 'edit'; + link.onclick = function() { var id = this.id.substr(this.id.indexOf('_')+1); editComment(id, this); return false; }; + ajaxComments(req); +} + +function deleteComment(id) +{ + //var c = confirm('Do you really want to delete this comment?'); + //if(!c); + // return false; + var div = document.getElementById('comment_holder_'+id); + var real_id = div.getElementsByTagName('input')[0]['value']; + var req = { + 'mode' : 'delete', + 'id' : real_id, + 'local_id' : id + }; + ajaxComments(req); +} + +function submitComment() +{ + var name = document.getElementById('commentform_name').value; + var subj = document.getElementById('commentform_subject').value; + var text = document.getElementById('commentform_message').value; + if ( document.getElementById('commentform_captcha') ) + { + var captcha_code = document.getElementById('commentform_captcha').value; + var captcha_id = document.getElementById('commentform_captcha_id').value; + } + else + { + var captcha_code = ''; + var captcha_id = ''; + } + var req = { + 'mode' : 'submit', + 'name' : name, + 'subj' : subj, + 'text' : text, + 'captcha_code' : captcha_code, + 'captcha_id' : captcha_id + }; + ajaxComments(req); +} + +function redrawComment(data) +{ + if ( data.subj ) + { + document.getElementById('subject_' + data.id).innerHTML = data.subj; + } + if ( data.approved && data.approved != '1' ) + { + document.getElementById('subject_' + data.id).innerHTML += ' (Unapproved)'; + } + if ( data.approved ) + { + var appr = ( data.approved == '1' ) ? 'Unapprove' : 'Approve'; + document.getElementById('comment_approve_'+data.id).innerHTML = appr; + var p = document.getElementById('comment_status'); + var count = p.firstChild.nodeValue.split(' ')[2]; + if ( p.firstChild.nextSibling ) + { + var span = p.firstChild.nextSibling; + var is = ( data.approved == '1' ) ? -1 : 1; + var n_unapp = parseInt(span.firstChild.nodeValue.split(' ')[0]) + is; + n_unapp = n_unapp + ''; + } + else + { + var span = document.createElement('span'); + p.innerHTML += ' '; + span.innerHTML = ' '; + span.style.color = '#D84308'; + var n_unapp = '1'; + p.appendChild(span); + } + span.innerHTML = n_unapp + ' of those are unapproved.'; + if ( n_unapp == '0' ) + p.removeChild(span); + } + if ( data.text ) + { + document.getElementById('comment_' + data.id).innerHTML = data.text; + } + if ( data.src ) + { + document.getElementById('comment_source_' + data.id).value = data.src; + } +} + +function approveComment(id) +{ + var div = document.getElementById('comment_holder_'+id); + var real_id = div.getElementsByTagName('input')[0]['value']; + var req = { + 'mode' : 'approve', + 'id' : real_id, + 'local_id' : id + }; + ajaxComments(req); +} + +// Does the actual DOM object removal +function annihiliateComment(id) // Did I spell that right? +{ + // Approved? + var p = document.getElementById('comment_status'); + + if(document.getElementById('comment_approve_'+id)) + { + var appr = document.getElementById('comment_approve_'+id).firstChild.nodeValue; + if ( p.firstChild.nextSibling && appr == 'Approve' ) + { + var span = p.firstChild.nextSibling; + var t = span.firstChild.nodeValue; + var n_unapp = ( parseInt(t.split(' ')[0]) ) - 1; + if ( n_unapp == 0 ) + p.removeChild(span); + else + span.firstChild.nodeValue = n_unapp + t.substr(t.indexOf(' ')); + } + } + + var div = document.getElementById('comment_holder_'+id); + div.parentNode.removeChild(div); + var t = p.firstChild.nodeValue.split(' '); + t[2] = ( parseInt(t[2]) - 1 ) + ''; + delete(t.toJSONString); + if ( t[2] == '1' ) + { + t[1] = 'is'; + t[3] = 'comment'; + } + else + { + t[1] = 'are'; + t[3] = 'comments'; + } + t = implode(' ', t); + p.firstChild.nodeValue = t; +} + +function materializeComment(data) +{ + // Intelligently get an ID + + var i = 0; + var brother; + while ( true ) + { + var x = document.getElementById('comment_holder_'+i); + if(!x) + break; + brother = x; + i++; + } + + var parser = new templateParser(comment_template); + var tplvars = new Object(); + + if ( data.approved != '1' && !data.auth_mod_comments ) + return false; + + tplvars.ID = i; + tplvars.DATETIME = data.time; + tplvars.SUBJECT = data.subject; + tplvars.DATA = data.comment_data; + tplvars.SIGNATURE = data.signature; + + tplvars.NAME = data.name; + if ( data.user_id > 1 ) + tplvars.NAME = '' + data.name + ''; + + if ( data.approved != '1' ) + tplvars.SUBJECT += ' (Unapproved)'; + + // User level + tplvars.USER_LEVEL = 'Guest'; + if ( data.user_level >= data.user_level_list.member ) tplvars.USER_LEVEL = 'Member'; + if ( data.user_level >= data.user_level_list.mod ) tplvars.USER_LEVEL = 'Moderator'; + if ( data.user_level >= data.user_level_list.admin ) tplvars.USER_LEVEL = 'Administrator'; + + // Send PM link + tplvars.SEND_PM_LINK=(data.user_id>1)?'Send private message
':''; + + // Add buddy link + tplvars.ADD_BUDDY_LINK=(data.user_id>1)?'Add to buddy list
':''; + + // Edit link + tplvars.EDIT_LINK='edit'; + + // Delete link + tplvars.DELETE_LINK='delete'; + + // Moderation: (Un)approve link + var appr = ( data.approved == 1 ) ? 'Unapprove' : 'Approve'; + tplvars.MOD_APPROVE_LINK=''+appr+''; + + // Moderation: Delete post link + tplvars.MOD_DELETE_LINK='Delete'; + + var tplbool = new Object(); + + tplbool.signature = ( data.signature == '' ) ? false : true; + tplbool.can_edit = ( data.auth_edit_comments && ( ( data.user_id == data.user_id && data.logged_in ) || data.auth_mod_comments ) ); + tplbool.auth_mod = data.auth_mod_comments; + + parser.assign_vars(tplvars); + parser.assign_bool(tplbool); + + var div = document.createElement('div'); + div.id = 'comment_holder_'+i; + + div.innerHTML = '' + parser.run(); + + if ( brother ) + { + brother.parentNode.insertBefore(div, brother.nextSibling); + } + else + { + // No comments in ajaxEditContainer, insert it after the header + var aec = document.getElementById("ajaxEditContainer"); + aec.insertBefore(div, aec.firstChild.nextSibling.nextSibling); + } + + document.getElementById('comment_source_'+i).value = data.comment_source; + + var p = document.getElementById('comment_status'); + var t = p.firstChild.nodeValue.split(' '); + var n = ( isNaN(parseInt(t[2])) ) ? 0 : parseInt(t[2]); + t[2] = ( n + 1 ) + ''; + delete(t.toJSONString); + if ( t[2] == '1' ) + { + t[1] = 'is'; + t[3] = 'comment'; + } + else + { + t[1] = 'are'; + t[3] = 'comments'; + } + t = implode(' ', t); + p.firstChild.nodeValue = t; + + if(document.getElementById('comment_approve_'+i)) + { + var appr = document.getElementById('comment_approve_'+i).firstChild.nodeValue; + if ( p.firstChild.nextSibling && appr == 'Approve' ) + { + var span = p.firstChild.nextSibling; + var t = span.firstChild.nodeValue; + var n_unapp = ( parseInt(t.split(' ')[0]) ) - 1; + if ( n_unapp == 0 ) + p.removeChild(span); + else + span.firstChild.nodeValue = n_unapp + t.substr(t.indexOf(' ')); + } + else if ( appr == 'Approve' && !p.firstChild.nextSibling ) + { + var span = document.createElement('span'); + p.innerHTML += ' '; + span.innerHTML = '1 of those are unapproved.'; + span.style.color = '#D84308'; + var n_unapp = '1'; + p.appendChild(span); + } + } + +} + +function htmlspecialchars(text) +{ + text = text.replace(//g, '>'); + return text; +} + +// Equivalent to PHP trim() function +function trim(text) +{ + text = text.replace(/^([\s]+)/, ''); + text = text.replace(/([\s]+)$/, ''); + return text; +} + +// Equivalent to PHP implode() function +function implode(chr, arr) +{ + if ( typeof ( arr.toJSONString ) == 'function' ) + delete(arr.toJSONString); + + var ret = ''; + var c = 0; + for ( var i in arr ) + { + if(i=='toJSONString')continue; + if ( c > 0 ) + ret += chr; + ret += arr[i]; + c++; + } + return ret; +} + +function nl2br(text) +{ + var regex = new RegExp(unescape('%0A'), 'g'); + return text.replace(regex, '
' + unescape('%0A')); +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/dropdown.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/dropdown.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,508 @@ +/* + * The jBox menu system. Written by Dan Fuhry and licensed under the GPL. + */ + +// Cache of DOM and event objects, used in setTimeout() calls due to scope rules +var jBoxObjCache = new Object(); + +// Cache of "correct" heights for unordered list objects used in submenus. Helps the animation routine know what height it's aiming for. +var jBoxMenuHeights = new Object(); + +// Blocks animations from running if there's already an animation going on the same object +var jBoxSlideBlocker = new Object(); + +// Speed at which the menus should slide open. 1 to 100 or -1 to disable. +// Setting this higher than 100 will cause an infinite loop in the browser. +// Default is 80 - anything higher than 90 means exponential speed increase +var slide_speed = 80; + +// Inertia value to start with +// Default is 0 +var inertia_base = 0; + +// Value added to inertia_base on each frame - generally 1 to 3 is good, with 1 being slowest animation +// Default is 1 +var inertia_inc = 1; + +// Opacity that menus should fade to. 1 to 100 or -1 to disable fades. This only works if the slide effect is also enabled. +// Default is 100 +var jBox_opacity = 100; + +// Adds the jBox CSS to the HTML header. Called on window onload. +function jBoxInit() +{ + setTimeout('jBoxBatchSetup();', 200); +} + +// Initializes each menu. +function jBoxBatchSetup() +{ + var menus = document.getElementsByClassName('div', 'menu_nojs'); + if ( menus.length > 0 ) + { + for ( var i in menus ) + { + if ( typeof(menus[i]) != 'object') + continue; // toJSONString() compatibility + jBoxSetup(menus[i]); + } + } +} + +// Initializes a div with a jBox menu in it. +function jBoxSetup(obj) +{ + $(obj).addClass('menu'); + removeTextNodes(obj); + for ( var i in obj.childNodes ) + { + /* normally this would be done in about 2 lines of code, but javascript is so picky..... */ + if ( obj.childNodes[i] ) + { + if ( obj.childNodes[i].tagName ) + { + if ( obj.childNodes[i].tagName.toLowerCase() == 'a' ) + { + if ( obj.childNodes[i].nextSibling.tagName ) + { + if ( obj.childNodes[i].nextSibling.tagName.toLowerCase() == 'ul' || ( obj.childNodes[i].nextSibling.tagName.toLowerCase() == 'div' && obj.childNodes[i].nextSibling.className == 'submenu' ) ) + { + // Calculate height + var ul = obj.childNodes[i].nextSibling; + domObjChangeOpac(0, ul); + ul.style.display = 'block'; + var dim = fetch_dimensions(ul); + if ( !ul.id ) + ul.id = 'jBoxmenuobj_' + Math.floor(Math.random() * 10000000); + jBoxMenuHeights[ul.id] = parseInt(dim['h']) - 2; // subtract 2px for border width + ul.style.display = 'none'; + domObjChangeOpac(100, ul); + + // Setup events + obj.childNodes[i].onmouseover = function() { jBoxOverHandler(this); }; + obj.childNodes[i].onmouseout = function(e) { jBoxOutHandler(this, e); }; + obj.childNodes[i].nextSibling.onmouseout = function(e) { jBoxOutHandler(this, e); }; + } + } + } + } + } + } +} + +// Called when user hovers mouse over a submenu +function jBoxOverHandler(obj) +{ + // Random ID used to track the object to perform on + var seed = Math.floor(Math.random() * 1000000); + jBoxObjCache[seed] = obj; + + // Sleep for a (little more than a tenth of a) second to see if the user really wants the menu to expand + setTimeout('if(isOverObj(jBoxObjCache['+seed+'], false, false)) jBoxOverHandlerBin(jBoxObjCache['+seed+']);', 150); +} + +// Displays a menu. +function jBoxOverHandlerBin(obj) +{ + var others = obj.parentNode.getElementsByTagName('ul'); + for ( var i in others ) + { + if(typeof(others[i]) == 'object') + { + others[i].style.display = 'none'; + others[i].previousSibling.className = ''; + } + } + var others = obj.parentNode.getElementsByTagName('div'); + for ( var i in others ) + { + if(typeof(others[i]) == 'object') + { + if ( others[i].className == 'submenu' ) + { + others[i].style.display = 'none'; + others[i].previousSibling.className = ''; + } + } + } + if(obj.nextSibling.tagName.toLowerCase() == 'ul' || ( obj.nextSibling.tagName.toLowerCase() == 'div' && obj.nextSibling.className == 'submenu' )) + { + obj.className = 'liteselected'; + var ul = obj.nextSibling; + var dim = fetch_dimensions(obj); + var off = fetch_offset(obj); + var dimh = parseInt(dim['h']); + var offtop = parseInt(off['top']); + var top = dimh + offtop; + left = off['left']; + ul.style.left = left + 'px'; + ul.style.top = top + 'px'; + domObjChangeOpac(0, ul); + ul.style.clip = 'rect(auto,auto,auto,auto)'; + ul.style.overflow = 'visible'; + ul.style.display = 'block'; + slideOut(ul); + } +} + +function jBoxOutHandler(obj, event) +{ + var seed = Math.floor(Math.random() * 1000000); + var seed2 = Math.floor(Math.random() * 1000000); + jBoxObjCache[seed] = obj; + jBoxObjCache[seed2] = event; + setTimeout('jBoxOutHandlerBin(jBoxObjCache['+seed+'], jBoxObjCache['+seed2+']);', 750); +} + +function jBoxOutHandlerBin(obj, event) +{ + var caller = obj.tagName.toLowerCase(); + if(caller == 'a') + { + a = obj; + ul = obj.nextSibling; + } + else if(caller == 'ul' || caller == 'div') + { + a = obj.previousSibling; + ul = obj; + } + else + { + return false; + } + + if (!isOverObj(a, false, event) && !isOverObj(ul, true, event)) + { + a.className = ''; + + slideIn(ul); + + } + + return true; +} + +// Slide an element downwards until it is at full height. +// First parameter should be a DOM object with style.display = block and opacity = 0. + +var sliderobj = new Object(); + +function slideOut(obj) +{ + if ( jBoxSlideBlocker[obj.id] ) + return false; + + jBoxSlideBlocker[obj.id] = true; + + if ( slide_speed == -1 ) + { + obj.style.display = 'block'; + return false; + } + + var currentheight = 0; + var targetheight = jBoxMenuHeights[obj.id]; + var inertiabase = inertia_base; + var inertiainc = inertia_inc; + slideStep(obj, 0); + domObjChangeOpac(100, obj); + obj.style.overflow = 'hidden'; + + // Don't edit past here + var timercnt = 0; + + var seed = Math.floor(Math.random() * 1000000); + sliderobj[seed] = obj; + + var framecnt = 0; + + while(true) + { + framecnt++; + timercnt += ( 100 - slide_speed ); + inertiabase += inertiainc; + currentheight += inertiabase; + if ( currentheight > targetheight ) + currentheight = targetheight; + setTimeout('slideStep(sliderobj['+seed+'], '+currentheight+', '+targetheight+');', timercnt); + if ( currentheight >= targetheight ) + break; + } + timercnt = timercnt + ( 100 - slide_speed ); + setTimeout('jBoxSlideBlocker[sliderobj['+seed+'].id] = false;', timercnt); + var opacstep = jBox_opacity / framecnt; + var opac = 0; + var timerstep = 0; + domObjChangeOpac(0, obj); + while(true) + { + timerstep += ( 100 - slide_speed ); + opac += opacstep; + setTimeout('domObjChangeOpac('+opac+', sliderobj['+seed+']);', timerstep); + if ( opac >= jBox_opacity ) + break; + } +} + +function slideIn(obj) +{ + if ( obj.style.display != 'block' ) + return false; + + if ( jBoxSlideBlocker[obj.id] ) + return false; + + jBoxSlideBlocker[obj.id] = true; + + var targetheight = 0; + var dim = fetch_dimensions(obj); + var currentheight = jBoxMenuHeights[obj.id]; + var origheight = currentheight; + var inertiabase = inertia_base; + var inertiainc = inertia_inc; + domObjChangeOpac(100, obj); + obj.style.overflow = 'hidden'; + + // Don't edit past here + var timercnt = 0; + + var seed = Math.floor(Math.random() * 1000000); + sliderobj[seed] = obj; + + var framecnt = 0; + + for(var j = 0;j<100;j++) // while(true) + { + framecnt++; + timercnt = timercnt + ( 100 - slide_speed ); + inertiabase = inertiabase + inertiainc; + currentheight = currentheight - inertiabase; + if ( currentheight < targetheight ) + currentheight = targetheight; + setTimeout('slideStep(sliderobj['+seed+'], '+currentheight+');', timercnt); + if ( currentheight <= targetheight ) + break; + } + timercnt += ( 100 - slide_speed ); + setTimeout('sliderobj['+seed+'].style.display="none";sliderobj['+seed+'].style.height="'+origheight+'px";jBoxSlideBlocker[sliderobj['+seed+'].id] = false;', timercnt); + + var opacstep = jBox_opacity / framecnt; + var opac = jBox_opacity; + var timerstep = 0; + domObjChangeOpac(100, obj); + while(true) + { + timerstep += ( 100 - slide_speed ); + opac -= opacstep; + setTimeout('domObjChangeOpac('+opac+', sliderobj['+seed+']);', timerstep); + if ( opac <= 0 ) + break; + } + +} + +function slideStep(obj, height, maxheight) +{ + obj.style.height = height + 'px'; + //obj.style.clip = 'rect(3px,auto,'+maxheight+'px,auto)'; + obj.style.overflow = 'hidden'; + //obj.style.clip = 'rect('+height+'px,0px,'+maxheight+'px,auto);'; +} + +function isOverObj(obj, bias, event) +{ + var fieldUL = new Object(); + var dim = fetch_dimensions(obj); + var off = fetch_offset(obj); + fieldUL['top'] = off['top']; + fieldUL['left'] = off['left']; + fieldUL['right'] = off['left'] + dim['w']; + fieldUL['bottom'] = off['top'] + dim['h']; + + //document.getElementById('debug').innerHTML = '




Mouse: x: '+mouseX+', y:' + mouseY + '
'; // + document.getElementById('debug').innerHTML; + + if(bias) + { + if ( ( mouseX < fieldUL['left'] + 2 || mouseX > fieldUL['right'] - 5 ) || + ( mouseY < fieldUL['top'] - 2 || mouseY > fieldUL['bottom'] - 2 ) ) + { + return false; + } + } + else + { + if ( ( mouseX < fieldUL['left'] || mouseX > fieldUL['right'] ) || + ( mouseY < fieldUL['top'] || mouseY > fieldUL['bottom'] ) ) + return false; + } + + return true; + + /* + var tgt = event.target; + if ( !tgt ) + return false; + do { + if ( tgt == obj ) + return true; + tgt = tgt.parentNode; + } while (tgt.tagName.toLowerCase() != 'body' ); + return false; + */ +} + +function jBoxGarbageCollection(e) +{ + setMousePos(e); + var menus = document.getElementsByClassName('div', 'menu'); + if ( menus.length > 0 ) + { + for ( var i in menus ) + { + if ( typeof(menus[i]) != 'object') + continue; // toJSONString() compatibility + var uls = menus[i].getElementsByTagName('ul'); + if ( uls.length > 0 ) + { + for ( var j = 0; j < uls.length; j++ ) + { + if ( !isOverObj(uls[j], false, e) ) + { + uls[j].previousSibling.className = ''; + //uls[j].style.display = 'none'; + slideIn(uls[j]); + } + } + } + var uls = getElementsByClassName(menus[i], 'divs', 'submenu'); + if ( uls.length > 0 ) + { + for ( var j = 0; j < uls.length; j++ ) + { + if ( !isOverObj(uls[j], false, e) ) + { + uls[j].previousSibling.className = ''; + //uls[j].style.display = 'none'; + slideIn(uls[j]); + } + } + } + } + } +} + +document.onclick = jBoxGarbageCollection; + +function removeTextNodes(obj) +{ + if(obj) + { + if(typeof(obj.tagName) != 'string') + { + if ( obj.nodeType == 3 && obj.data.match(/^([\s]*)$/ig) ) + { + obj.parentNode.removeChild(obj); + return; + } + } + if(obj.firstChild) + { + for(var i in obj.childNodes) + { + removeTextNodes(obj.childNodes[i]); + } + } + } +} + +var getElementsByClassName = function(parent, type, cls) { + if(!type) + type = '*'; + ret = new Array(); + el = parent.getElementsByTagName(type); + for ( var i in el ) + { + if ( typeof(el[i]) != 'object') + continue; // toJSONString() compatibility + if(el[i].className) + { + if(el[i].className.indexOf(' ') > 0) + { + classes = el[i].className.split(' '); + } + else + { + classes = new Array(); + classes.push(el[i].className); + } + if ( in_array(cls, classes) ) + ret.push(el[i]); + } + } + return ret; +} + +document.getElementsByClassName = function(type, cls) { + return getElementsByClassName(document, type, cls); +} + +function setMousePos(event) +{ + if(IE) + { + if(!event) + { + event = window.event; + } + clX = event.clientX; + sL = document.body.scrollLeft; + mouseX = clX + sL; + mouseY = event.clientY + document.body.scrollTop; + return; + } + if( typeof(event.clientX) == 'number' ) + { + mouseX = event.clientX; + mouseY = event.clientY; + return; + } + else if( typeof(event.layerX) == 'number' ) + { + mouseX = event.layerX; + mouseY = event.layerY; + return; + } + else if( typeof(event.offsetX) == 'number' ) + { + mouseX = event.offsetX; + mouseY = event.offsetY; + return; + } + else if( typeof(event.screenX) == 'number' ) + { + mouseX = event.screenX; + mouseY = event.screenY; + return; + } + else if( typeof(event.x) == 'number' ) + { + mouseX = event.x; + mouseY = event.y; + return; + } +} + +document.onmousemove = function(e) +{ + setMousePos(e); +}; + +function domObjChangeOpac(opacity, id) { + var object = id.style; + object.opacity = (opacity / 100); + object.MozOpacity = (opacity / 100); + object.KhtmlOpacity = (opacity / 100); + object.filter = "alpha(opacity=" + opacity + ")"; +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/dynano.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/dynano.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,129 @@ +// The "Dynano" Javascript framework. Similar in syntax to JQuery but only has what Enano needs. + +var $ = function(id) +{ + return new DNobj(id); +} +var $dynano = $; +function DNobj(id) +{ + this.object = ( typeof(id) == 'object' ) ? id : document.getElementById(id); + this.height = __DNObjGetHeight(this.object); + this.width = __DNObjGetWidth(this.object); + + if ( this.object.tagName == 'TEXTAREA' && typeof(tinyMCE) == 'object' ) + { + this.object.dnIsMCE = 'no'; + this.switchToMCE = DN_switchToMCE; + this.destroyMCE = DN_destroyMCE; + this.getContent = DN_mceFetchContent; + } +} +function __DNObjGetHeight(o) { + return o.offsetHeight; +} + +function __DNObjGetWidth(o) { + return o.offsetWidth; +} + +function addClass(obj, clsname) +{ + var cnt = obj.className; + var space = ( (cnt + '').length > 0 ) ? ' ' : ''; + var cls = cnt + space + clsname; + obj.className = cls; +} + +function rmClass(obj, clsname) +{ + var cnt = obj.className; + if ( cnt == clsname ) + { + obj.className = ''; + } + else + { + cnt = cnt.replace(clsname, ''); + cnt = trim(cnt); + obj.className = cnt; + } +} + +function hasClass(obj, clsname) +{ + var cnt = obj.className; + if ( !cnt ) + return false; + if ( cnt == clsname ) + return true; + cnt = cnt.split(' '); + + for ( var i in cnt ) + if ( cnt[i] == clsname ) + return true; + + return false; +} +function __DNObjGetLeft(obj) { + var left_offset = obj.offsetLeft; + while ((obj = obj.offsetParent) != null) { + left_offset += obj.offsetLeft; + } + return left_offset; +} + +function __DNObjGetTop(obj) { + var left_offset = obj.offsetTop; + while ((obj = obj.offsetParent) != null) { + left_offset += obj.offsetTop; + } + return left_offset; +} + +function DN_switchToMCE() +{ + //if ( this.object.dn_is_mce ) + // return this; + if ( !this.object.id ) + this.object.id = 'textarea_' + Math.floor(Math.random() * 1000000); + if ( !this.object.name ) + this.object.name = 'textarea_' + Math.floor(Math.random() * 1000000); + tinyMCE.addMCEControl(this.object, this.object.name, document); + this.object.dnIsMCE = 'yes'; + return this; +} + +function DN_destroyMCE() +{ + //if ( !this.object.dn_is_mce ) + // return this; + if ( this.object.name ) + tinyMCE.removeMCEControl(this.object.name); + this.object.dnIsMCE = 'no'; + return this; +} + +function DN_mceFetchContent() +{ + if ( this.object.name ) + { + var text = this.object.value; + if ( tinyMCE.getInstanceById(this.object.name) ) + text = tinyMCE.getContent(this.object.name); + return text; + } + else + { + return this.object.value; + } +} + +DNobj.prototype.addClass = function(clsname) { addClass(this.object, clsname); return this; }; +DNobj.prototype.rmClass = function(clsname) { rmClass( this.object, clsname); return this; }; +DNobj.prototype.hasClass = function(clsname) { return hasClass(this.object, clsname); }; +DNobj.prototype.Height = function() { return __DNObjGetHeight(this.object); } +DNobj.prototype.Width = function() { return __DNObjGetWidth( this.object); } +DNobj.prototype.Left = function() { /* return this.object.offsetLeft; */ return __DNObjGetLeft(this.object); } +DNobj.prototype.Top = function() { /* return this.object.offsetTop; */ return __DNObjGetTop( this.object); } + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/editor.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/editor.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,22 @@ +// Javascript routines for the page editor + +function initTinyMCE(e) +{ + if ( typeof(tinyMCE) == 'object' ) + { + tinyMCE.init({ + mode : "exact", + elements : '', + plugins : 'table', + theme_advanced_resize_horizontal : false, + theme_advanced_resizing : true, + theme_advanced_toolbar_location : "top", + theme_advanced_toolbar_align : "left", + theme_advanced_buttons1_add : "fontselect,fontsizeselect", + theme_advanced_buttons3_add_before : "tablecontrols,separator", + theme_advanced_statusbar_location : 'bottom' + }); + } +} +addOnloadHook(initTinyMCE); + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/enano-lib-basic.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/enano-lib-basic.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,328 @@ +/* + * Enano - an open source wiki-like CMS + * Copyright (C) 2006-2007 Dan Fuhry + * Javascript client library + * + * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * For more information about Enano, please visit http://www.enanocms.org/. + * All of the code in these script files may be used freely so long as the above license block is displayed and your + * modified code is distributed under the GPL. See the page Special:About_Enano on this website for more information. + */ + +if(typeof title != 'string') +{ + alert('Uh-oh! The required dynamic (PHP-generated) Javascript variables don\'t seem to be available. Javascript is going to be seriously broken.'); +} + +// Run-time variables + +var detect = navigator.userAgent.toLowerCase(); +var IE; + +// dummy tinyMCE object +var tinyMCE = new Object(); + +// Detect whether the user is running the Evil One or not... + +function checkIt(string) { + place = detect.indexOf(string) + 1; + thestring = string; + return place; +} +if (checkIt('msie')) IE = true; +else IE = false; + +var cmt_open; +var list; +var edit_open = false; +var catlist = new Array(); +var arrDiff1Buttons = new Array(); +var arrDiff2Buttons = new Array(); +var arrTimeIdList = new Array(); +var list; +var unObj; +var unSelectMenuOn = false; +var unObjDivCurrentId = false; +var unObjCurrentSelection = false; +var userlist = new Array(); +var submitAuthorized = true; +var rDnsObj; +var rDnsBannerObj; +var ns4 = document.layers; +var op5 = (navigator.userAgent.indexOf("Opera 5")!=-1) ||(navigator.userAgent.indexOf("Opera/5")!=-1); +var op6 = (navigator.userAgent.indexOf("Opera 6")!=-1) ||(navigator.userAgent.indexOf("Opera/6")!=-1); +var agt=navigator.userAgent.toLowerCase(); +var mac = (agt.indexOf("mac")!=-1); +var ie = (agt.indexOf("msie") != -1); +var mac_ie = mac && ie; +var mouseX = 0; +var mouseY = 0; +var menuheight; +var inertiabase = 1; +var inertiainc = 1; +var slideintervalinc = 20; +var inertiabaseoriginal = inertiabase; +var heightnow; +var targetheight; +var block; +var slideinterval; +var divheights = new Array(); +var __menutimeout = false; +var startmouseX = false; +var startmouseY = false; +var startScroll = false; +var is_dragging = false; +var current_ta = false; +var startwidth = false; +var startheight = false; +var do_width = false; + +// You have an NSIS coder in your midst... +var MB_OK = 1; +var MB_OKCANCEL = 2; +var MB_YESNO = 4; +var MB_YESNOCANCEL = 8; +var MB_ABORTRETRYIGNORE = 16; +var MB_ICONINFORMATION = 32; +var MB_ICONEXCLAMATION = 64; +var MB_ICONSTOP = 128; +var MB_ICONQUESTION = 256; +var MB_ICONLOCK = 512; + +// Syntax: +// messagebox(MB_OK|MB_ICONINFORMATION, 'Title', 'Text'); +// :-D + +var main_css = document.getElementById('mdgCss').href; +if(main_css.indexOf('?') > -1) { + sep = '&'; +} else sep = '?'; +var _css = false; +var print_css = main_css + sep + 'printable'; + +var shift; + +function makeUrl(page, query, html_friendly) +{ + url = contentPath+page; + if(url.indexOf('?') > 0) sep = '&'; + else sep = '?'; + if(query) + { + url = url + sep + query; + } + if(html_friendly) + { + url = url.replace('&', '&'); + url = url.replace('<', '<'); + url = url.replace('>', '>'); + } + return url; +} + +function makeUrlNS(namespace, page, query, html_friendly) +{ + var url = contentPath+namespace_list[namespace]+(page.replace(/ /g, '_')); + if(url.indexOf('?') > 0) sep = '&'; + else sep = '?'; + if(query) + { + url = url + sep + query; + } + if(html_friendly) + { + url = url.replace('&', '&'); + url = url.replace('<', '<'); + url = url.replace('>', '>'); + } + return append_sid(url); +} + +function strToPageID(string) +{ + // Convert Special:UploadFile to ['UploadFile', 'Special'], but convert 'Image:Enano.png' to ['Enano.png', 'File'] + for(var i in namespace_list) + if(namespace_list[i] != '') + if(namespace_list[i] == string.substr(0, namespace_list[i].length)) + return [string.substr(namespace_list[i].length), i]; + return [string, 'Article']; +} + +function append_sid(url) +{ + sep = ( url.indexOf('?') > 0 ) ? '&' : '?'; + if(ENANO_SID.length > 10) + { + url = url + sep + 'auth=' + ENANO_SID; + sep = '&'; + } + if ( pagepass.length > 0 ) + { + url = url + sep + 'pagepass=' + pagepass; + } + return url; +} + +var stdAjaxPrefix = append_sid(scriptPath+'/ajax.php?title='+title); + +// Code for parsing JSON strings - full source code in json.js +if(!Object.prototype.toJSONString){Array.prototype.toJSONString=function(){var a=['['],b,i,l=this.length,v;function p(s){if(b){a.push(',');} +a.push(s);b=true;} +for(i=0;i= 1000 ) + break; + var _f = onload_hooks[_oLc]; + if ( typeof(_f) == 'function' ) + { + _f(e); + } + } +} + +addOnloadHook(function() { + if ( $_REQUEST['do'] ) + { + var act = $_REQUEST['do']; + switch(act) + { + case 'comments': + ajaxComments(); + break; + case 'edit': + ajaxEditor(); + break; + case 'login': + ajaxStartLogin(); + break; + case 'history': + ajaxHistory(); + break; + case 'catedit': + ajaxCatEdit(); + break; + } + } +}); + + +//*/ diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/faders.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/faders.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,393 @@ +// Message box system + +function darken(nofade) +{ + if(IE) + nofade = true; + if(document.getElementById('specialLayer_darkener')) + { + document.getElementById('specialLayer_darkener').style.display = 'block'; + if(nofade) + { + document.getElementById('specialLayer_darkener').style.opacity = '0.7'; + document.getElementById('specialLayer_darkener').style.filter = 'alpha(opacity=70)'; + } + else + { + opacity('specialLayer_darkener', 0, 70, 1000); + } + } else { + w = getWidth(); + h = getHeight(); + var thediv = document.createElement('div'); + if(IE) + thediv.style.position = 'absolute'; + else + thediv.style.position = 'fixed'; + thediv.style.top = '0px'; + thediv.style.left = '0px'; + thediv.style.opacity = '0'; + thediv.style.filter = 'alpha(opacity=0)'; + thediv.style.backgroundColor = '#000000'; + thediv.style.width = '100%'; + thediv.style.height = '100%'; + thediv.zIndex = getHighestZ() + 5; + thediv.id = 'specialLayer_darkener'; + if(nofade) + { + thediv.style.opacity = '0.7'; + thediv.style.filter = 'alpha(opacity=70)'; + body = document.getElementsByTagName('body'); + body = body[0]; + body.appendChild(thediv); + } else { + body = document.getElementsByTagName('body'); + body = body[0]; + body.appendChild(thediv); + opacity('specialLayer_darkener', 0, 70, 1000); + } + } +} + +function enlighten(nofade) +{ + if(IE) + nofade = true; + if(document.getElementById('specialLayer_darkener')) + { + if(nofade) + { + document.getElementById('specialLayer_darkener').style.display = 'none'; + } + opacity('specialLayer_darkener', 70, 0, 1000); + setTimeout("document.getElementById('specialLayer_darkener').style.display = 'none';", 1000); + } +} + +/** + * The ultimate message box framework for Javascript + * Syntax is (almost) identical to the MessageBox command in NSIS + * @param int type - a bitfield consisting of the MB_* constants + * @param string title - the blue text at the top of the window + * @param string text - HTML for the body of the message box + * Properties: + * onclick - an array of functions to be called on button click events + * NOTE: key names are to be strings, and they must be the value of the input, CaSe-SeNsItIvE + * onbeforeclick - same as onclick but called before the messagebox div is destroyed + * Example: + * var my_message = new messagebox(MB_OK|MB_ICONSTOP, 'Error logging in', 'The username and/or password is incorrect. Please check the username and retype your password'); + * my_message.onclick['OK'] = function() { + * document.getElementById('password').value = ''; + * }; + * Deps: + * Modern browser that supports DOM + * darken() and enlighten() (above) + * opacity() - required for darken() and enlighten() + * MB_* constants are defined in enano-lib-basic.js + */ + +var mb_current_obj; + +function messagebox(type, title, message) +{ + var y = getScrollOffset(); + if(document.getElementById('messageBox')) return; + darken(true); + var master_div = document.createElement('div'); + var mydiv = document.createElement('div'); + mydiv.style.width = '400px'; + mydiv.style.height = '200px'; + w = getWidth(); + h = getHeight(); + //master_div.style.left = (w / 2) - 200+'px'; + //master_div.style.top = (h / 2) + y - 120+'px'; + master_div.style.top = '-10000px'; + master_div.style.position = ( IE ) ? 'absolute' : 'fixed'; + z = getHighestZ(); // document.getElementById('specialLayer_darkener').style.zIndex; + mydiv.style.zIndex = parseInt(z) + 1; + mydiv.style.backgroundColor = '#FFFFFF'; + mydiv.style.padding = '10px'; + mydiv.style.marginBottom = '1px'; + mydiv.id = 'messageBox'; + mydiv.style.overflow = 'auto'; + + var buttondiv = document.createElement('div'); + buttondiv.style.width = '400px'; + w = getWidth(); + h = getHeight(); + // buttondiv.style.left = (w / 2) - 200+'px'; + // buttondiv.style.top = (h / 2) + y + 101+'px'; + // buttondiv.style.position = ( IE ) ? 'absolute' : 'fixed'; + z = getHighestZ(); // document.getElementById('specialLayer_darkener').style.zIndex; + buttondiv.style.zIndex = parseInt(z) + 1; + buttondiv.style.backgroundColor = '#C0C0C0'; + buttondiv.style.padding = '10px'; + buttondiv.style.textAlign = 'right'; + buttondiv.style.verticalAlign = 'middle'; + buttondiv.id = 'messageBoxButtons'; + + this.clickHandler = function() { messagebox_click(this, mb_current_obj); }; + + if(type & MB_ICONINFORMATION || type & MB_ICONSTOP || type & MB_ICONQUESTION || type & MB_ICONEXCLAMATION || type & MB_ICONLOCK) + { + mydiv.style.paddingLeft = '50px'; + mydiv.style.width = '360px'; + mydiv.style.backgroundRepeat = 'no-repeat'; + } + + if(type & MB_ICONINFORMATION) + { + mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/info.png\')'; + } + + if(type & MB_ICONQUESTION) + { + mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/question.png\')'; + } + + if(type & MB_ICONSTOP) + { + mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/error.png\')'; + } + + if(type & MB_ICONEXCLAMATION) + { + mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/warning.png\')'; + } + + if(type & MB_ICONLOCK) + { + mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/lock.png\')'; + } + + if(type & MB_OK) + { + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'OK'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + } + + if(type & MB_OKCANCEL) + { + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'OK'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'Cancel'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + } + + if(type & MB_YESNO) + { + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'Yes'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'No'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + } + + if(type & MB_YESNOCANCEL) + { + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'Yes'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'No'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'Cancel'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + } + + heading = document.createElement('h2'); + heading.innerHTML = title; + heading.style.color = '#50A0D0'; + heading.style.fontFamily = 'trebuchet ms, verdana, arial, helvetica, sans-serif'; + heading.style.fontSize = '12pt'; + heading.style.fontWeight = 'lighter'; + heading.style.textTransform = 'lowercase'; + heading.style.marginTop = '0'; + mydiv.appendChild(heading); + + var text = document.createElement('div'); + text.innerHTML = String(message); + this.text_area = text; + mydiv.appendChild(text); + + this.updateContent = function(text) + { + this.text_area.innerHTML = text; + }; + + //domObjChangeOpac(0, mydiv); + //domObjChangeOpac(0, master_div); + + body = document.getElementsByTagName('body'); + body = body[0]; + master_div.appendChild(mydiv); + master_div.appendChild(buttondiv); + + body.appendChild(master_div); + + setTimeout('mb_runFlyIn();', 100); + + this.onclick = new Array(); + this.onbeforeclick = new Array(); + mb_current_obj = this; +} + +function mb_runFlyIn() +{ + var mydiv = document.getElementById('messageBox'); + var maindiv = mydiv.parentNode; + fly_in_top(maindiv, true, false); +} + +function messagebox_click(obj, mb) +{ + val = obj.value; + if(typeof mb.onbeforeclick[val] == 'function') + { + var o = mb.onbeforeclick[val]; + var resp = o(); + if ( resp ) + return false; + o = false; + } + + var mydiv = document.getElementById('messageBox'); + var maindiv = mydiv.parentNode; + var to = fly_out_top(maindiv, true, false); + + setTimeout("var mbdiv = document.getElementById('messageBox'); mbdiv.parentNode.removeChild(mbdiv.nextSibling); mbdiv.parentNode.removeChild(mbdiv); enlighten(true);", to); + if(typeof mb.onclick[val] == 'function') + { + o = mb.onclick[val]; + o(); + o = false; + } +} + +function testMessageBox() +{ + mb = new messagebox(MB_OKCANCEL|MB_ICONINFORMATION, 'Javascripted dynamic message boxes', 'This is soooooo coool, now if only document.createElement() worked in IE!
this is some more text




this is some more text




this is some more text




this is some more text




this is some more text




this is some more text




this is some more text




this is some more text'); + mb.onclick['OK'] = function() + { + alert('You clicked OK!'); + } + mb.onbeforeclick['Cancel'] = function() + { + alert('You clicked Cancel!'); + } +} + +// Function to fade classes info-box, warning-box, error-box, etc. + +function fadeInfoBoxes() +{ + var divs = new Array(); + d = document.getElementsByTagName('div'); + j = 0; + for(var i in d) + { + if ( !d[i].tagName ) + continue; + if(d[i].className=='info-box' || d[i].className=='error-box' || d[i].className=='warning-box' || d[i].className=='question-box') + { + divs[j] = d[i]; + j++; + } + } + if(divs.length < 1) return; + for(i in divs) + { + if(!divs[i].id) divs[i].id = 'autofade_'+Math.floor(Math.random() * 100000); + switch(divs[i].className) + { + case 'info-box': + default: + from = '#3333FF'; + break; + case 'error-box': + from = '#FF3333'; + break; + case 'warning-box': + from = '#FFFF33'; + break; + case 'question-box': + from = '#33FF33'; + break; + } + Fat.fade_element(divs[i].id,30,2000,from,Fat.get_bgcolor(divs[i].id)); + } +} + +// Alpha fades + +function opacity(id, opacStart, opacEnd, millisec) { + //speed for each frame + var speed = Math.round(millisec / 100); + var timer = 0; + + //determine the direction for the blending, if start and end are the same nothing happens + if(opacStart > opacEnd) { + for(i = opacStart; i >= opacEnd; i--) { + setTimeout("changeOpac(" + i + ",'" + id + "')",(timer * speed)); + timer++; + } + } else if(opacStart < opacEnd) { + for(i = opacStart; i <= opacEnd; i++) + { + setTimeout("changeOpac(" + i + ",'" + id + "')",(timer * speed)); + timer++; + } + } +} + +//change the opacity for different browsers +function changeOpac(opacity, id) { + var object = document.getElementById(id).style; + object.opacity = (opacity / 100); + object.MozOpacity = (opacity / 100); + object.KhtmlOpacity = (opacity / 100); + object.filter = "alpha(opacity=" + opacity + ")"; +} + +function mb_logout() +{ + var mb = new messagebox(MB_YESNO|MB_ICONQUESTION, 'Are you sure you want to log out?', 'If you log out, you will no longer be able to access your user preferences, your private messages, or certain areas of this site until you log in again.'); + mb.onclick['Yes'] = function() + { + window.location = makeUrlNS('Special', 'Logout/' + title); + } +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/faders.js~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/faders.js~ Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,393 @@ +// Message box system + +function darken(nofade) +{ + if(IE) + nofade = true; + if(document.getElementById('specialLayer_darkener')) + { + document.getElementById('specialLayer_darkener').style.display = 'block'; + if(nofade) + { + document.getElementById('specialLayer_darkener').style.opacity = '0.7'; + document.getElementById('specialLayer_darkener').style.filter = 'alpha(opacity=70)'; + } + else + { + opacity('specialLayer_darkener', 0, 70, 1000); + } + } else { + w = getWidth(); + h = getHeight(); + var thediv = document.createElement('div'); + if(IE) + thediv.style.position = 'absolute'; + else + thediv.style.position = 'fixed'; + thediv.style.top = '0px'; + thediv.style.left = '0px'; + thediv.style.opacity = '0'; + thediv.style.filter = 'alpha(opacity=0)'; + thediv.style.backgroundColor = '#000000'; + thediv.style.width = '100%'; + thediv.style.height = '100%'; + thediv.zIndex = getHighestZ() + 5; + thediv.id = 'specialLayer_darkener'; + if(nofade) + { + thediv.style.opacity = '0.7'; + thediv.style.filter = 'alpha(opacity=70)'; + body = document.getElementsByTagName('body'); + body = body[0]; + body.appendChild(thediv); + } else { + body = document.getElementsByTagName('body'); + body = body[0]; + body.appendChild(thediv); + opacity('specialLayer_darkener', 0, 70, 1000); + } + } +} + +function enlighten(nofade) +{ + if(IE) + nofade = true; + if(document.getElementById('specialLayer_darkener')) + { + if(nofade) + { + document.getElementById('specialLayer_darkener').style.display = 'none'; + } + opacity('specialLayer_darkener', 70, 0, 1000); + setTimeout("document.getElementById('specialLayer_darkener').style.display = 'none';", 1000); + } +} + +/** + * The ultimate message box framework for Javascript + * Syntax is (almost) identical to the MessageBox command in NSIS + * @param int type - a bitfield consisting of the MB_* constants + * @param string title - the blue text at the top of the window + * @param string text - HTML for the body of the message box + * Properties: + * onclick - an array of functions to be called on button click events + * NOTE: key names are to be strings, and they must be the value of the input, CaSe-SeNsItIvE + * onbeforeclick - same as onclick but called before the messagebox div is destroyed + * Example: + * var my_message = new messagebox(MB_OK|MB_ICONSTOP, 'Error logging in', 'The username and/or password is incorrect. Please check the username and retype your password'); + * my_message.onclick['OK'] = function() { + * document.getElementById('password').value = ''; + * }; + * Deps: + * Modern browser that supports DOM + * darken() and enlighten() (above) + * opacity() - required for darken() and enlighten() + * MB_* constants are defined in enano-lib-basic.js + */ + +var mb_current_obj; + +function messagebox(type, title, message) +{ + var y = getScrollOffset(); + if(document.getElementById('messageBox')) return; + darken(true); + var master_div = document.createElement('div'); + var mydiv = document.createElement('div'); + mydiv.style.width = '400px'; + mydiv.style.height = '200px'; + w = getWidth(); + h = getHeight(); + //master_div.style.left = (w / 2) - 200+'px'; + //master_div.style.top = (h / 2) + y - 120+'px'; + master_div.style.top = '-10000px'; + master_div.style.position = ( IE ) ? 'absolute' : 'fixed'; + z = getHighestZ(); // document.getElementById('specialLayer_darkener').style.zIndex; + mydiv.style.zIndex = parseInt(z) + 1; + mydiv.style.backgroundColor = '#FFFFFF'; + mydiv.style.padding = '10px'; + mydiv.style.marginBottom = '1px'; + mydiv.id = 'messageBox'; + mydiv.style.overflow = 'auto'; + + var buttondiv = document.createElement('div'); + buttondiv.style.width = '400px'; + w = getWidth(); + h = getHeight(); + // buttondiv.style.left = (w / 2) - 200+'px'; + // buttondiv.style.top = (h / 2) + y + 101+'px'; + // buttondiv.style.position = ( IE ) ? 'absolute' : 'fixed'; + z = getHighestZ(); // document.getElementById('specialLayer_darkener').style.zIndex; + buttondiv.style.zIndex = parseInt(z) + 1; + buttondiv.style.backgroundColor = '#C0C0C0'; + buttondiv.style.padding = '10px'; + buttondiv.style.textAlign = 'right'; + buttondiv.style.verticalAlign = 'middle'; + buttondiv.id = 'messageBoxButtons'; + + this.clickHandler = function() { messagebox_click(this, mb_current_obj); }; + + if(type & MB_ICONINFORMATION || type & MB_ICONSTOP || type & MB_ICONQUESTION || type & MB_ICONEXCLAMATION || type & MB_ICONLOCK) + { + mydiv.style.paddingLeft = '50px'; + mydiv.style.width = '360px'; + mydiv.style.backgroundRepeat = 'no-repeat'; + } + + if(type & MB_ICONINFORMATION) + { + mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/info.png\')'; + } + + if(type & MB_ICONQUESTION) + { + mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/question.png\')'; + } + + if(type & MB_ICONSTOP) + { + mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/error.png\')'; + } + + if(type & MB_ICONEXCLAMATION) + { + mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/warning.png\')'; + } + + if(type & MB_ICONLOCK) + { + mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/lock.png\')'; + } + + if(type & MB_OK) + { + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'OK'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + } + + if(type & MB_OKCANCEL) + { + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'OK'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'Cancel'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + } + + if(type & MB_YESNO) + { + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'Yes'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'No'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + } + + if(type & MB_YESNOCANCEL) + { + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'Yes'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'No'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + + btn = document.createElement('input'); + btn.type = 'button'; + btn.value = 'Cancel'; + btn.onclick = this.clickHandler; + btn.style.margin = '0 3px'; + buttondiv.appendChild(btn); + } + + heading = document.createElement('h2'); + heading.innerHTML = title; + heading.style.color = '#50A0D0'; + heading.style.fontFamily = 'trebuchet ms, verdana, arial, helvetica, sans-serif'; + heading.style.fontSize = '12pt'; + heading.style.fontWeight = 'lighter'; + heading.style.textTransform = 'lowercase'; + heading.style.marginTop = '0'; + mydiv.appendChild(heading); + + var text = document.createElement('div'); + text.innerHTML = String(message); + this.text_area = text; + mydiv.appendChild(text); + + this.updateContent = function(text) + { + this.text_area.innerHTML = text; + }; + + //domObjChangeOpac(0, mydiv); + //domObjChangeOpac(0, master_div); + + body = document.getElementsByTagName('body'); + body = body[0]; + master_div.appendChild(mydiv); + master_div.appendChild(buttondiv); + + body.appendChild(master_div); + + setTimeout('mb_runFlyIn();', 100); + + this.onclick = new Array(); + this.onbeforeclick = new Array(); + mb_current_obj = this; +} + +function mb_runFlyIn() +{ + var mydiv = document.getElementById('messageBox'); + var maindiv = mydiv.parentNode; + fly_in_top(maindiv, true, false); +} + +function messagebox_click(obj, mb) +{ + val = obj.value; + if(typeof mb.onbeforeclick[val] == 'function') + { + var o = mb.onbeforeclick[val]; + var resp = o(); + if ( resp ) + return false; + o = false; + } + + var mydiv = document.getElementById('messageBox'); + var maindiv = mydiv.parentNode; + var to = fly_out_top(maindiv, true, false); + + setTimeout("var mbdiv = document.getElementById('messageBox'); mbdiv.parentNode.removeChild(mbdiv.nextSibling); mbdiv.parentNode.removeChild(mbdiv); enlighten(true);", to); + if(typeof mb.onclick[val] == 'function') + { + o = mb.onclick[val]; + o(); + o = false; + } +} + +function testMessageBox() +{ + mb = new messagebox(MB_OKCANCEL|MB_ICONINFORMATION, 'Javascripted dynamic message boxes', 'This is soooooo coool, now if only document.createElement() worked in IE!
this is some more text




this is some more text




this is some more text




this is some more text




this is some more text




this is some more text




this is some more text




this is some more text'); + mb.onclick['OK'] = function() + { + alert('You clicked OK!'); + } + mb.onbeforeclick['Cancel'] = function() + { + alert('You clicked Cancel!'); + } +} + +// Function to fade classes info-box, warning-box, error-box, etc. + +function fadeInfoBoxes() +{ + var divs = new Array(); + d = document.getElementsByTagName('div'); + j = 0; + for(var i in d) + { + if ( !d[i].tagName ) + continue; + if(d[i].className=='info-box' || d[i].className=='error-box' || d[i].className=='warning-box' || d[i].className=='question-box') + { + divs[j] = d[i]; + j++; + } + } + if(divs.length < 1) return; + for(i in divs) + { + if(!divs[i].id) divs[i].id = 'autofade_'+Math.floor(Math.random() * 100000); + switch(divs[i].className) + { + case 'info-box': + default: + from = '#3333FF'; + break; + case 'error-box': + from = '#FF3333'; + break; + case 'warning-box': + from = '#FFFF33'; + break; + case 'question-box': + from = '#33FF33'; + break; + } + Fat.fade_element(divs[i].id,30,2000,from,Fat.get_bgcolor(divs[i].id)); + } +} + +// Alpha fades + +function opacity(id, opacStart, opacEnd, millisec) { + //speed for each frame + var speed = Math.round(millisec / 100); + var timer = 0; + + //determine the direction for the blending, if start and end are the same nothing happens + if(opacStart > opacEnd) { + for(i = opacStart; i >= opacEnd; i--) { + setTimeout("changeOpac(" + i + ",'" + id + "')",(timer * speed)); + timer++; + } + } else if(opacStart < opacEnd) { + for(i = opacStart; i <= opacEnd; i++) + { + setTimeout("changeOpac(" + i + ",'" + id + "')",(timer * speed)); + timer++; + } + } +} + +//change the opacity for different browsers +function changeOpac(opacity, id) { + var object = document.getElementById(id).style; + object.opacity = (opacity / 100); + object.MozOpacity = (opacity / 100); + object.KhtmlOpacity = (opacity / 100); + object.filter = "alpha(opacity=" + opacity + ")"; +} + +function mb_logout() +{ + var mb = new messagebox(MB_YESNO|MB_ICONQUESTION, 'Are you sure you want to log out?', 'If you log out, you will no longer be able to access your user preferences, certain areas of this site, and this awesome logout confirmation screen until you login again.

OK, not funny. I\'ll remove the bad humor in Banshee.'); + mb.onclick['Yes'] = function() + { + window.location = makeUrlNS('Special', 'Logout/' + title); + } +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/fat.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/fat.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,93 @@ +// @name The Fade Anything Technique +// @namespace http://www.axentric.com/aside/fat/ +// @version 1.0-RC1 +// @author Adam Michela + +var Fat = { + make_hex : function (r,g,b) + { + r = r.toString(16); if (r.length == 1) r = '0' + r; + g = g.toString(16); if (g.length == 1) g = '0' + g; + b = b.toString(16); if (b.length == 1) b = '0' + b; + return "#" + r + g + b; + }, + fade_all : function () + { + var a = document.getElementsByTagName("*"); + for (var i = 0; i < a.length; i++) + { + var o = a[i]; + var r = /fade-?(\w{3,6})?/.exec(o.className); + if (r) + { + if (!r[1]) r[1] = ""; + if (!o.id) o.id = 'autofat_'+Math.floor(Math.random() * 100000); + if (o.id) Fat.fade_element(o.id,null,null,"#"+r[1]); + } + } + }, + fade_element : function (id, fps, duration, from, to) + { + if (!fps) fps = 30; + if (!duration) duration = 3000; + if (!from || from=="#") from = "#FFFF33"; + if (!to) to = this.get_bgcolor(id); + + var frames = Math.round(fps * (duration / 1000)); + var interval = duration / frames; + var delay = interval; + var frame = 0; + + if (from.length < 7) from += from.substr(1,3); + if (to.length < 7) to += to.substr(1,3); + + var rf = parseInt(from.substr(1,2),16); + var gf = parseInt(from.substr(3,2),16); + var bf = parseInt(from.substr(5,2),16); + var rt = parseInt(to.substr(1,2),16); + var gt = parseInt(to.substr(3,2),16); + var bt = parseInt(to.substr(5,2),16); + + var r,g,b,h; + while (frame < frames) + { + r = Math.floor(rf * ((frames-frame)/frames) + rt * (frame/frames)); + g = Math.floor(gf * ((frames-frame)/frames) + gt * (frame/frames)); + b = Math.floor(bf * ((frames-frame)/frames) + bt * (frame/frames)); + h = this.make_hex(r,g,b); + + setTimeout("Fat.set_bgcolor('"+id+"','"+h+"')", delay); + + frame++; + delay = interval * frame; + } + setTimeout("Fat.set_bgcolor('"+id+"','"+to+"')", delay); + }, + set_bgcolor : function (id, c) + { + var o = document.getElementById(id); + if(!o) return; + o.style.backgroundColor = c; + }, + get_bgcolor : function (id) + { + var o = document.getElementById(id); + while(o) + { + var c; + if (window.getComputedStyle) c = window.getComputedStyle(o,null).getPropertyValue("background-color"); + if (o.currentStyle) c = o.currentStyle.backgroundColor; + if ((c != "" && c != "transparent") || o.tagName == "BODY") { break; } + o = o.parentNode; + } + if (c == undefined || c == "" || c == "transparent") c = "#FFFFFF"; + var rgb = c.match(/rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/); + if (rgb) c = this.make_hex(parseInt(rgb[1]),parseInt(rgb[2]),parseInt(rgb[3])); + return c; + } +} + +window.onload = function () + { + Fat.fade_all(); + } \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/flyin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/flyin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,195 @@ +// Make an HTML element fly in from the top or bottom. +// Includes inertia! + +// vB, don't even try. It's GPL like the rest of Enano. I know you're jealous. >:) + +var fly_in_cache = new Object(); +var FI_TOP = 1; +var FI_BOTTOM = 2; +var FI_IN = 1; +var FI_OUT = 2; +var FI_UP = 1; +var FI_DOWN = 2; + +// Placeholder functions, to make organization a little easier :-) + +function fly_in_top(element, nofade, height_taken_care_of) +{ + return fly_core(element, nofade, FI_TOP, FI_IN, height_taken_care_of); +} + +function fly_in_bottom(element, nofade, height_taken_care_of) +{ + return fly_core(element, nofade, FI_BOTTOM, FI_IN, height_taken_care_of); +} + +function fly_out_top(element, nofade, height_taken_care_of) +{ + return fly_core(element, nofade, FI_TOP, FI_OUT, height_taken_care_of); +} + +function fly_out_bottom(element, nofade, height_taken_care_of) +{ + return fly_core(element, nofade, FI_BOTTOM, FI_OUT, height_taken_care_of); +} + +function fly_core(element, nofade, origin, direction, height_taken_care_of) +{ + if ( !element || typeof(element) != 'object' ) + return false; + // target dimensions + var top, left; + // initial dimensions + var topi, lefti; + // current dimensions + var topc, leftc; + // screen dimensions + var w = getWidth(); + var h = getHeight(); + var y = parseInt ( getScrollOffset() ); + // temp vars + var dim, off, diff, dist, ratio, opac_factor; + // setup element + element.style.position = 'absolute'; + + dim = [ $(element).Height(), $(element).Width() ]; + off = [ $(element).Top(), $(element).Left() ]; + + if ( height_taken_care_of ) + { + top = off[0]; + left = off[1]; + } + else + { + top = Math.round(( h / 2 ) - ( dim[0] / 2 )) + y; // - ( h / 4 )); + left = Math.round(( w / 2 ) - ( dim[1] / 2 )); + } + + // you can change this around to get it to fly in from corners or be on the left/right side + lefti = left; + + // calculate first frame Y position + if ( origin == FI_TOP && direction == FI_IN ) + { + topi = 0 - dim[0] + y; + } + else if ( origin == FI_TOP && direction == FI_OUT ) + { + topi = top; + top = 0 - dim[0] + y; + } + else if ( origin == FI_BOTTOM && direction == FI_IN ) + { + topi = h + y; + } + else if ( origin == FI_BOTTOM && direction == FI_OUT ) + { + topi = top; + top = h + y; + } + + var abs_dir = ( ( origin == FI_TOP && direction == FI_IN ) || ( origin == FI_BOTTOM && direction == FI_OUT ) ) ? FI_DOWN : FI_UP; + + /* + * Framestepper parameters + */ + + // starting value for inertia + var inertiabase = 1; + // increment for inertia, or 0 to disable inertia effects + var inertiainc = 1; + // when the progress reaches this %, deceleration is activated + var divider = 0.666667; + // multiplier for deceleration, setting this above 2 can cause some weird slowdown effects + var decelerate = 2; // 1 / divider; // reciprocal of the divider + + /* + * Timer parameters + */ + + // how long animation start is delayed, you want this at 0 + var timer = 0; + // frame ttl + var timestep = 12; + // sanity check + var frames = 0; + + // cache element so it can be changed from within setTimeout() + var rand_seed = Math.floor(Math.random() * 1000000); + fly_in_cache[rand_seed] = element; + + // set element left pos, you can comment this out to preserve left position + element.style.left = left + 'px'; + element.style.top = topi + 'px'; + + if ( nofade ) + { + domObjChangeOpac(100, element); + } + + // total distance to be traveled + dist = abs(top - topi); + + // animation loop + + while ( true ) + { + // used for a sanity check + frames++; + + // time until this frame should be executed + timer += timestep; + + // math stuff + // how far we are along in animation... + diff = abs(top - topi); + // ...in % + ratio = abs( 1 - ( diff / dist ) ); + // decelerate if we're more than 2/3 of the way there + if ( ratio < divider ) + inertiabase += inertiainc; + else + inertiabase -= ( inertiainc * decelerate ); + + // if the deceleration factor is anywhere above 1 then technically that can cause an infinite loop + // so leave this in there unless decelerate is set to 1 + if ( inertiabase < 1 ) + inertiabase = 1; + + // uncomment to disable inertia + // inertiabase = 3; + + // figure out frame Y position + topi = ( abs_dir == FI_UP ) ? topi - inertiabase : topi + inertiabase; + if ( ( abs_dir == FI_DOWN && topi > top ) || ( abs_dir == FI_UP && top > topi ) ) + topi = top; + + // tell the browser to do it + setTimeout('var o = fly_in_cache['+rand_seed+']; o.style.top=\''+topi+'px\';', timer); + if ( !nofade ) + { + // handle fade + opac_factor = ratio * 100; + if ( direction == FI_OUT ) + opac_factor = 100 - opac_factor; + setTimeout('var o = fly_in_cache['+rand_seed+']; domObjChangeOpac('+opac_factor+', o);', timer); + } + + // if we're done or if our sanity check failed then break out of the loop + if ( ( abs_dir == FI_DOWN && topi >= top ) || ( abs_dir == FI_UP && top >= topi ) || frames > 1000 ) + break; + } + + //timer += timestep; + setTimeout('delete(fly_in_cache['+rand_seed+']);', timer); + return timer; +} + +function abs(i) +{ + if ( isNaN(i) ) + return i; + return ( i < 0 ) ? ( 0 - i ) : i; +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/grippy.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/grippy.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,91 @@ +// Resizable textareas (fun!) + +function taStartDrag() +{ + obj = this; + current_ta = obj.previousSibling; + startmouseX = mouseX; + startmouseY = mouseY; + startScroll = getScrollOffset(); + is_dragging = true; + startwidth = getElementWidth(current_ta.id); + startheight = getElementHeight(current_ta.id); + var body = document.getElementsByTagName('body'); + body = body[0]; + body.style.cursor = 's-resize'; +} +function taInDrag() +{ + if(!is_dragging) return; + cw = startwidth; + ch = startheight; + mx = mouseX; + my = mouseY + getScrollOffset() - startScroll; + ch = -6 + ch + ( my - startmouseY ); + current_ta.style.height = ch+'px'; + if(do_width) + { + current_ta.style.width = mx+'px'; + current_ta.nextSibling.style.width = mx+'px'; + } +} +function taCloseDrag() +{ + is_dragging = false; + current_ta = false; + body = document.getElementsByTagName('body'); + body = body[0]; + body.style.cursor = 'default'; +} + +var grippied_textareas = new Array(); + +function initTextareas() +{ + var textareas = document.getElementsByTagName('textarea'); + for (i = 0;i < textareas.length;i++) + { + if(!textareas[i].id) + textareas[i].id = 'autoTextArea_'+Math.floor(Math.random()*100000); + cta = textareas[i]; + var divchk = ( in_array(cta.id, grippied_textareas) ) ? false : true; + if(divchk) + { + grippied_textareas.push(cta.id); + makeGrippy(cta); + } + } +} + +function makeGrippy(cta) +{ + var thediv = document.createElement('div'); + thediv.style.backgroundColor = '#ceceed'; + thediv.style.backgroundImage = 'url('+scriptPath+'/images/grippy.gif)'; + thediv.style.backgroundPosition = 'bottom right'; + thediv.style.backgroundRepeat = 'no-repeat'; + thediv.style.width = getElementWidth(cta.id)+'px'; + thediv.style.cursor = 's-resize'; + thediv.style.className = 'ThisIsATextareaGrippy'; + thediv.id = 'autoGrippy_'+Math.floor(Math.random()*100000); + thediv.style.height = '12px'; + thediv.onmousedown = taStartDrag; + thediv.style.border = '1px solid #0000A0'; + if(cta.style.marginBottom) + { + thediv.style.marginBottom = cta.style.marginBottom; + cta.style.marginBottom = '0'; + } + if(cta.style.marginLeft) + { + thediv.style.marginLeft = cta.style.marginLeft; + } + if(cta.style.marginRight) + { + thediv.style.marginRight = cta.style.marginRight; + } + document.onmouseup = taCloseDrag; + if(cta.nextSibling) cta.parentNode.insertBefore(thediv, cta.nextSibling); + else cta.parentNode.appendChild(thediv); +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/json.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/json.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,172 @@ +/* + json.js + 2007-03-20 + + All of the code contained within this file is released into + the public domain. Optionally, you may distribute this code + under the terms of the GNU General Public License as well + (public domain licensing allows this). + +*/ + +function toJSONString(input) +{ + var m = { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }; + var t = typeof(input); + switch(t) + { + case 'string': + if (/["\\\x00-\x1f]/.test(input)) + { + return '"' + input.replace(/([\x00-\x1f\\"])/g, function(a, b) + { + var c = m[b]; + if (c) { + return c; + } + c = b.charCodeAt(); + return '\\u00' + + Math.floor(c / 16).toString(16) + + (c % 16).toString(16); + }) + '"'; + } + return '"' + input + '"'; + break; + case 'array': + var a = ['['], + b, + i, + l = input.length, + v; + + function p(s) { + + if (b) { + a.push(','); + } + a.push(s); + b = true; + } + + for (i = 0; i < l; i += 1) { + v = input[i]; + switch (typeof v) { + case 'object': + if (v) { + p(toJSONString(v)); + } else { + p("null"); + } + break; + case 'array': + case 'string': + case 'number': + case 'boolean': + p(toJSONString(v)); + } + } + + a.push(']'); + return a.join(''); + break; + case 'date': + function f(n) + { + return n < 10 ? '0' + n : n; + } + return '"' + input.getFullYear() + '-' + + f(input.getMonth() + 1) + '-' + + f(input.getDate()) + 'T' + + f(input.getHours()) + ':' + + f(input.getMinutes()) + ':' + + f(input.getSeconds()) + '"'; + + case 'boolean': + return String(input); + break; + case 'number': + return isFinite(input) ? String(input) : "null"; + break; + case 'object': + var a = ['{'], + b, + k, + v; + + function p(s) + { + if (b) + { + a.push(','); + } + a.push(toJSONString(k), ':', s); + b = true; + } + + for (k in input) + { + if (input.hasOwnProperty(k)) + { + v = input[k]; + switch (typeof v) { + + case 'object': + if (v) { + p(toJSONString(v)); + } else { + p("null"); + } + break; + case 'string': + case 'number': + case 'boolean': + p(toJSONString(v)); + break; + } + } + } + + a.push('}'); + return a.join(''); + break; + } +} + +function parseJSON(string, filter) +{ + try { + if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/. + test(string)) + { + + var j = eval('(' + string + ')'); + if (typeof filter === 'function') { + + function walk(k, v) { + if (v && typeof v === 'object') { + for (var i in v) { + if (v.hasOwnProperty(i)) { + v[i] = walk(i, v[i]); + } + } + } + return filter(k, v); + } + + j = walk('', j); + } + return j; + } + } catch (e) { + + } + throw new SyntaxError("parseJSON"); +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/loader.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/loader.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,46 @@ +// Some final stuff - loader routines, etc. + +var __tmpEnanoStartup9843275; + +function enanoStartup(e) { + if ( !e ) + { + // Delay initting sliders until images are loaded + if ( typeof(window.onload) == 'function' ) + __tmpEnanoStartup9843275 = window.onload; + else + __tmpEnanoStartup9843275 = function(){}; + window.onload = function(e){__tmpEnanoStartup9843275(e);initSliders();}; + } + else + { + initSliders(); + } +} + +function mdgInnerLoader(e) +{ + jws.startup(); + enanoStartup(e); + if(window.location.hash == '#comments') ajaxComments(); + window.onkeydown=isKeyPressed; + window.onkeyup=function(e) { isKeyPressed(e); }; + Fat.fade_all(); + fadeInfoBoxes(); + //initTextareas(); + buildSearchBoxes(); + jBoxInit(); + if(typeof (dbx_set_key) == 'function') + { + dbx_set_key(); + } + runOnloadHooks(e); +} +if(window.onload) var ld = window.onload; +else var ld = function() {return;}; +function enano_init(e) { + ld(e); + mdgInnerLoader(e); +} +window.onload = enano_init; + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/md5.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/md5.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,51 @@ +// Javascript implementation of the and SHA1 hash algorithms - both written by Paul Johnston, licensed under the BSD license + +// MD5 +var hexcase = 0; var b64pad = ""; var chrsz = 8; +function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));} +function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));} +function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));} +function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); } +function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); } +function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); } +function md5_vm_test() { return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72"; } +function core_md5(x, len) { x[len >> 5] |= 0x80 << ((len) % 32); x[(((len + 64) >>> 9) << 4) + 14] = len; var a = 1732584193; var b = -271733879; var c = -1732584194; var d = 271733878; for(var i = 0; i < x.length; i += 16) { var olda = a; var oldb = b; var oldc = c; var oldd = d; a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); + a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);c = md5_ff(c, d, a, b, x[i+10], 17, -42063);b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);d = md5_ff(d, a, b, c, x[i+13], 12, -40341101); + c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); + a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); + c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); + a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); + c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); + a = safe_add(a, olda); b = safe_add(b, oldb); c = safe_add(c, oldc); d = safe_add(d, oldd); } return Array(a, b, c, d); } +function md5_cmn(q, a, b, x, s, t) { return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b); } +function md5_ff(a, b, c, d, x, s, t) { return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); } +function md5_gg(a, b, c, d, x, s, t) { return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); } +function md5_hh(a, b, c, d, x, s, t) { return md5_cmn(b ^ c ^ d, a, b, x, s, t); } +function md5_ii(a, b, c, d, x, s, t) { return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); } +function core_hmac_md5(key, data) { var bkey = str2binl(key); if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz); var ipad = Array(16), opad = Array(16); for(var i = 0; i < 16; i++) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5C5C5C5C; } var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz); return core_md5(opad.concat(hash), 512 + 128); } +function safe_add(x, y) {var lsw = (x & 0xFFFF) + (y & 0xFFFF);var msw = (x >> 16) + (y >> 16) + (lsw >> 16);return (msw << 16) | (lsw & 0xFFFF); } +function bit_rol(num, cnt) { return (num << cnt) | (num >>> (32 - cnt)); } +function str2binl(str) { var bin = Array(); var mask = (1 << chrsz) - 1; for(var i = 0; i < str.length * chrsz; i += chrsz)bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32); return bin;} +function binl2str(bin) { var str = ""; var mask = (1 << chrsz) - 1; for(var i = 0; i < bin.length * 32; i += chrsz) str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask); return str; } +function binl2hex(binarray) { var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; var str = ""; for(var i = 0; i < binarray.length * 4; i++) { str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF); } return str; } +function binl2b64(binarray) { var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var str = ""; for(var i = 0; i < binarray.length * 4; i += 3) { var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16) | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 ) | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF); for(var j = 0; j < 4; j++) { if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); } } return str; } + +// SHA1 +function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));} +function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));} +function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));} +function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));} +function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));} +function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));} +function sha1_vm_test() { return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; } +function core_sha1(x, len) { x[len >> 5] |= 0x80 << (24 - len % 32); x[((len + 64 >> 9) << 4) + 15] = len; var w = Array(80); var a = 1732584193; var b = -271733879; var c = -1732584194; var d = 271733878; var e = -1009589776; for(var i = 0; i < x.length; i += 16) { var olda = a; var oldb = b; var oldc = c; var oldd = d; var olde = e; for(var j = 0; j < 80; j++) { if(j < 16) w[j] = x[i + j]; else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), sha1_kt(j))); e = d; d = c; c = rol(b, 30); b = a; a = t; } a = safe_add(a, olda); b = safe_add(b, oldb); c = safe_add(c, oldc); d = safe_add(d, oldd); e = safe_add(e, olde); } return Array(a, b, c, d, e);} +function sha1_ft(t, b, c, d){ if(t < 20) return (b & c) | ((~b) & d); if(t < 40) return b ^ c ^ d; if(t < 60) return (b & c) | (b & d) | (c & d); return b ^ c ^ d;} +function sha1_kt(t){ return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : (t < 60) ? -1894007588 : -899497514;} +function core_hmac_sha1(key, data){ var bkey = str2binb(key); if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); var ipad = Array(16), opad = Array(16); for(var i = 0; i < 16; i++) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5C5C5C5C; } var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); return core_sha1(opad.concat(hash), 512 + 160);} +function safe_add(x, y){ var lsw = (x & 0xFFFF) + (y & 0xFFFF); var msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xFFFF);} +function rol(num, cnt){ return (num << cnt) | (num >>> (32 - cnt));} +function str2binb(str){ var bin = Array(); var mask = (1 << chrsz) - 1; for(var i = 0; i < str.length * chrsz; i += chrsz) bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32); return bin;} +function binb2str(bin){ var str = ""; var mask = (1 << chrsz) - 1; for(var i = 0; i < bin.length * 32; i += chrsz) str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask); return str;} +function binb2hex(binarray){ var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; var str = ""; for(var i = 0; i < binarray.length * 4; i++) { str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); } return str;} +function binb2b64(binarray){ var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var str = ""; for(var i = 0; i < binarray.length * 4; i += 3) { var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); for(var j = 0; j < 4; j++) { if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); } } return str;} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/misc.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/misc.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,575 @@ +// Some additional DHTML functions + +function fetch_offset(obj) { + var left_offset = obj.offsetLeft; + var top_offset = obj.offsetTop; + while ((obj = obj.offsetParent) != null) { + left_offset += obj.offsetLeft; + top_offset += obj.offsetTop; + } + return { 'left' : left_offset, 'top' : top_offset }; +} + +function fetch_dimensions(o) { + var w = o.offsetWidth; + var h = o.offsetHeight; + return { 'w' : w, 'h' : h }; +} + +function findParentForm(o) +{ + // Not implemented - someone please let me know how to do this, what I need to do is + // find the first parent
tag above param 'o', not sure how to do it with DOM +} + +function ajaxReverseDNS(o, text) +{ + if(text) var ipaddr = text; + else var ipaddr = o.innerHTML; + rDnsObj = o; + rDnsBannerObj = bannerOn('Retrieving reverse DNS info...'); + ajaxGet(stdAjaxPrefix+'&_mode=rdns&ip='+ipaddr, function() { + if(ajax.readyState == 4) + { + off = fetch_offset(rDnsObj); + dim = fetch_dimensions(rDnsObj); + right = off['left'] + dim['w']; + top = off['top'] + dim['h']; + var thediv = document.createElement('div'); + thediv.className = 'info-box'; + thediv.style.margin = '0'; + thediv.style.position = 'absolute'; + thediv.style.top = top + 'px'; + thediv.style.display = 'none'; + thediv.style.zIndex = getHighestZ() + 2; + thediv.id = 'mdgDynamic_rDnsInfoDiv_'+Math.floor(Math.random() * 1000000); + thediv.innerHTML = 'Reverse DNS:
'+ajax.responseText+' Close'; + var body = document.getElementsByTagName('body'); + body = body[0]; + bannerOff(rDnsBannerObj); + body.appendChild(thediv); + thediv.style.display = 'block'; + left = fetch_dimensions(thediv); + thediv.style.display = 'none'; + left = right - left['w']; + thediv.style.left = left + 'px'; + thediv.style.display = 'block'; + fadeInfoBoxes(); + } + }); +} + +function bannerOn(text) +{ + darken(true); + var thediv = document.createElement('div'); + thediv.className = 'mdg-comment'; + thediv.style.padding = '0'; + thediv.style.marginLeft = '0'; + thediv.style.position = 'absolute'; + thediv.style.display = 'none'; + thediv.style.padding = '4px'; + thediv.style.fontSize = '14pt'; + thediv.id = 'mdgDynamic_bannerDiv_'+Math.floor(Math.random() * 1000000); + thediv.innerHTML = text; + + var body = document.getElementsByTagName('body'); + body = body[0]; + body.appendChild(thediv); + body.style.cursor = 'wait'; + + thediv.style.display = 'block'; + dim = fetch_dimensions(thediv); + thediv.style.display = 'none'; + bdim = { 'w' : getWidth(), 'h' : getHeight() }; + so = getScrollOffset(); + + left = (bdim['w'] / 2) - ( dim['w'] / 2 ); + top = (bdim['h'] / 2) - ( dim['h'] / 2 ) + so; + + thediv.style.top = top + 'px'; + thediv.style.left = left + 'px'; + + thediv.style.display = 'block'; + + return thediv.id; +} + +function bannerOff(id) +{ + e = document.getElementById(id); + if(!e) return; + e.innerHTML = ''; + e.style.display = 'none'; + var body = document.getElementsByTagName('body'); + body = body[0]; + body.style.cursor = 'default'; + enlighten(true); +} + +function disableUnload(message) +{ + if(typeof message != 'string') message = 'You may want to save your changes first.'; + var body = document.getElementsByTagName('body'); + body = body[0]; + body.onbeforeunload='return unescape(\''+escape(message)+'\')'; +} + +function enableUnload() +{ + var body = document.getElementsByTagName('body'); + body = body[0]; + body.onbeforeunload = null; +} + +/** + * Gets the highest z-index of all divs in the document + * @return integer + */ +function getHighestZ() +{ + z = 0; + var divs = document.getElementsByTagName('div'); + for(var i = 0; i < divs.length; i++) + { + if(divs[i].style.zIndex > z) z = divs[i].style.zIndex; + } + return z; +} + +function isKeyPressed(event) +{ + if (event.shiftKey==1) + { + shift = true; + } + else + { + shift = false; + } +} + +function moveDiv(div, newparent) +{ + var backup = div; + var oldparent = div.parentNode; + oldparent.removeChild(div); + newparent.appendChild(backup); +} + +function readCookie(name) {var nameEQ = name + "=";var ca = document.cookie.split(';');for(var i=0;i < ca.length;i++){var c = ca[i];while (c.charAt(0)==' ') c = c.substring(1,c.length);if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);}return null;} +function createCookie(name,value,days){if (days){var date = new Date();date.setTime(date.getTime()+(days*24*60*60*1000));var expires = "; expires="+date.toGMTString();}else var expires = "";document.cookie = name+"="+value+expires+"; path=/";} +function eraseCookie(name) {createCookie(name,"",-1);} + +var busyBannerID; +function goBusy(msg) +{ + if(!msg) msg = 'Please wait...'; + body = document.getElementsByTagName('body'); + body = body[0]; + body.style.cursor = 'wait'; + busyBannerID = bannerOn(msg); +} + +function unBusy() +{ + body = document.getElementsByTagName('body'); + body = body[0]; + body.style.cursor = 'default'; + bannerOff(busyBannerID); +} + +function setAjaxLoading() +{ + if ( document.getElementById('ajaxloadicon') ) + { + document.getElementById('ajaxloadicon').src=scriptPath + '/images/loading.gif'; + } +} + +function unsetAjaxLoading() +{ + if ( document.getElementById('ajaxloadicon') ) + { + document.getElementById('ajaxloadicon').src=scriptPath + '/images/spacer.gif'; + } +} + +/* + * Search boxes + */ + +function buildSearchBoxes() +{ + var divs = document.getElementsByTagName('*'); + var boxes = new Array(); + for ( var i = 0; i < divs.length; i++ ) + { + if ( divs[i].className) + { + if ( divs[i].className.substr(0, 9) == 'searchbox' ) + { + boxes.push(divs[i]); + } + } + } + for ( var i = 0; i < boxes.length; i++ ) + { + if ( boxes[i].className.match(/^searchbox\[([0-9]+)px\]$/) ) + { + var width = boxes[i].className.match(/^searchbox\[([0-9]+)px\]$/); + width = parseInt(width[1]); + } + else + { + var width = 120; + } + createSearchBox(boxes[i], width); + } +} + +function createSearchBox(parent, width) +{ + if ( typeof(parent) != 'object') + { + alert('BUG: createSearchBox(): parent is not an object'); + return false; + } + //parent.style.padding = '0px'; + //parent.style.textAlign = 'center'; + parent.style.width = width + 'px'; + var submit = document.createElement('div'); + submit.onclick = function() { searchFormSubmit(this); }; + submit.className = 'js-search-submit'; + var input = document.createElement('input'); + input.className = 'js-search-box'; + input.value = 'Search'; + input.name = 'q'; + input.style.width = ( width - 28 ) + 'px'; + input.onfocus = function() { if ( this.value == 'Search' ) this.value = ''; }; + input.onblur = function() { if ( this.value == '' ) this.value = 'Search'; }; + parent.appendChild(input); + var off = fetch_offset(input); + var top = off['top'] + 'px'; + var left = ( parseInt(off['left']) + ( width - 24 ) ) + 'px'; + submit.style.top = top; + submit.style.left = left; + parent.appendChild(submit); +} + +function searchFormSubmit(obj) +{ + var input = obj.previousSibling; + if ( input.value == 'Search' || input.value == '' ) + return false; + var p = obj; + while(true) + { + p = p.parentNode; + if ( !p ) + break; + if ( typeof(p.tagName) != 'string' ) + break; + else if ( p.tagName.toLowerCase() == 'form' ) + { + p.submit(); + } + else if ( p.tagName.toLowerCase() == 'body' ) + { + break; + } + } +} + +/* + * AJAX login box (experimental) + */ + +var ajax_auth_prompt_cache = false; +var ajax_auth_mb_cache = false; +var ajax_auth_level_cache = false; + +function ajaxPromptAdminAuth(call_on_ok, level) +{ + if ( typeof(call_on_ok) == 'function' ) + { + ajax_auth_prompt_cache = call_on_ok; + } + if ( !level ) + level = 2; + ajax_auth_level_cache = level; + var loading_win = '
\ +

Fetching an encryption key...

\ +

Not working? Use the alternate login form.

\ +

Please wait...

\ +
'; + ajax_auth_mb_cache = new messagebox(MB_OKCANCEL|MB_ICONLOCK, 'Please enter your username and password to continue.', loading_win); + ajax_auth_mb_cache.onbeforeclick['OK'] = ajaxValidateLogin; + ajaxAuthLoginInnerSetup(); +} + +function ajaxAuthLoginInnerSetup() +{ + ajaxGet(makeUrlNS('Special', 'Login', 'act=getkey'), function() { + if ( ajax.readyState == 4 ) + { + var response = ajax.responseText; + if ( response.substr(0,1) != '{' ) + { + alert('Invalid JSON response from server: ' + response); + return false; + } + response = parseJSON(response); + var form_html = ' \ + \ + \ + \ + \ + \ + \ + \ + \ +
Username: \ +
Password: \ +
\ +
Trouble logging in? Try the full login form.
\ + Did you forget your password?
\ + Maybe you need to create an account.
\ +
\ + \ + \ + '; + ajax_auth_mb_cache.updateContent(form_html); + $('messageBox').object.nextSibling.firstChild.tabindex = '3'; + $('ajaxlogin_user').object.focus(); + $('ajaxlogin_pass').object.onblur = function() { $('messageBox').object.nextSibling.firstChild.focus(); }; + } + }); +} + +function ajaxValidateLogin() +{ + var username,password,auth_enabled,crypt_key,crypt_data,challenge_salt,challenge_data; + username = document.getElementById('ajaxlogin_user'); + if ( !username ) + return false; + username = document.getElementById('ajaxlogin_user').value; + password = document.getElementById('ajaxlogin_pass').value; + auth_enabled = false; + + disableJSONExts(); + + // + // Encryption test + // + + var str = ''; + for(i=0;i

\ + '; + + ajax_auth_mb_cache.updateContent(loading_win); + + ajaxPost(makeUrlNS('Special', 'Login', 'act=ajaxlogin'), 'params=' + json_data, function() { + if ( ajax.readyState == 4 ) + { + var response = ajax.responseText; + if ( response.substr(0,1) != '{' ) + { + alert('Invalid JSON response from server: ' + response); + ajaxAuthLoginInnerSetup(); + return false; + } + response = parseJSON(response); + switch(response.result) + { + case 'success': + if ( typeof(ajax_auth_prompt_cache) == 'function' ) + { + ajax_auth_prompt_cache(response.key); + } + break; + case 'success_reset': + var conf = confirm('You have logged in using a temporary password. Before you can log in, you must finish resetting your password. Do you want to reset your real password now?'); + if ( conf ) + { + var url = makeUrlNS('Special', 'PasswordReset/stage2/' + response.user_id + '/' + response.temppass); + window.location = url; + } + else + { + ajaxAuthLoginInnerSetup(); + } + break; + case 'error': + alert(response.error); + ajaxAuthLoginInnerSetup(); + break; + default: + alert(ajax.responseText); + break; + } + } + }); + + return true; + +} + +// This code is in the public domain. Feel free to link back to http://jan.moesen.nu/ +function sprintf() +{ + if (!arguments || arguments.length < 1 || !RegExp) + { + return; + } + var str = arguments[0]; + var re = /([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X)(.*)/; + var a = b = [], numSubstitutions = 0, numMatches = 0; + while (a = re.exec(str)) + { + var leftpart = a[1], pPad = a[2], pJustify = a[3], pMinLength = a[4]; + var pPrecision = a[5], pType = a[6], rightPart = a[7]; + + //alert(a + '\n' + [a[0], leftpart, pPad, pJustify, pMinLength, pPrecision); + + numMatches++; + if (pType == '%') + { + subst = '%'; + } + else + { + numSubstitutions++; + if (numSubstitutions >= arguments.length) + { + alert('Error! Not enough function arguments (' + (arguments.length - 1) + ', excluding the string)\nfor the number of substitution parameters in string (' + numSubstitutions + ' so far).'); + } + var param = arguments[numSubstitutions]; + var pad = ''; + if (pPad && pPad.substr(0,1) == "'") pad = leftpart.substr(1,1); + else if (pPad) pad = pPad; + var justifyRight = true; + if (pJustify && pJustify === "-") justifyRight = false; + var minLength = -1; + if (pMinLength) minLength = parseInt(pMinLength); + var precision = -1; + if (pPrecision && pType == 'f') precision = parseInt(pPrecision.substring(1)); + var subst = param; + if (pType == 'b') subst = parseInt(param).toString(2); + else if (pType == 'c') subst = String.fromCharCode(parseInt(param)); + else if (pType == 'd') subst = parseInt(param) ? parseInt(param) : 0; + else if (pType == 'u') subst = Math.abs(param); + else if (pType == 'f') subst = (precision > -1) ? Math.round(parseFloat(param) * Math.pow(10, precision)) / Math.pow(10, precision): parseFloat(param); + else if (pType == 'o') subst = parseInt(param).toString(8); + else if (pType == 's') subst = param; + else if (pType == 'x') subst = ('' + parseInt(param).toString(16)).toLowerCase(); + else if (pType == 'X') subst = ('' + parseInt(param).toString(16)).toUpperCase(); + } + str = leftpart + subst + rightPart; + } + return str; +} + +function paginator_goto(parentobj, this_page, num_pages, perpage, url_string) +{ + var height = $(parentobj).Height(); + var width = $(parentobj).Width(); + var left = $(parentobj).Left(); + var top = $(parentobj).Top(); + var left_pos = left + width ; + var top_pos = height + top; + var div = document.createElement('div'); + div.style.position = 'absolute'; + div.style.top = top_pos + 'px'; + div.className = 'question-box'; + div.style.margin = '1px 0 0 2px'; + var vtmp = 'input_' + Math.floor(Math.random() * 1000000); + div.innerHTML = 'Go to page:
»×'; + + var body = document.getElementsByTagName('body')[0]; + body.appendChild(div); + + document.getElementById(vtmp).onkeypress = function(e){if(e.keyCode==13)this.nextSibling.nextSibling.onclick();}; + document.getElementById(vtmp).focus(); + + // fade the div + /* + if(!div.id) div.id = 'autofade_'+Math.floor(Math.random() * 100000); + var from = '#33FF33'; + Fat.fade_element(div.id,30,2000,from,Fat.get_bgcolor(div.id)); + */ + fly_in_bottom(div, false, true); + + var divh = $(div).Width(); + left_pos = left_pos - divh; + div.style.left = left_pos + 'px'; +} + +function paginator_submit(obj, max, perpage, formatstring) +{ + var userinput = obj.previousSibling.previousSibling.value; + userinput = parseInt(userinput); + var offset = ( userinput - 1 ) * perpage; + if ( userinput > max || isNaN(userinput) || userinput < 1 ) + { + new messagebox(MB_OK|MB_ICONSTOP, 'Invalid entry', 'Please enter a page number between 1 and ' + max + '.'); + return false; + } + var url = sprintf(formatstring, String(offset)); + fly_out_top(obj.parentNode, false, true); + window.location = url; +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/misc.js~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/misc.js~ Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,563 @@ +// Some additional DHTML functions + +function fetch_offset(obj) { + var left_offset = obj.offsetLeft; + var top_offset = obj.offsetTop; + while ((obj = obj.offsetParent) != null) { + left_offset += obj.offsetLeft; + top_offset += obj.offsetTop; + } + return { 'left' : left_offset, 'top' : top_offset }; +} + +function fetch_dimensions(o) { + var w = o.offsetWidth; + var h = o.offsetHeight; + return { 'w' : w, 'h' : h }; +} + +function findParentForm(o) +{ + // Not implemented - someone please let me know how to do this, what I need to do is + // find the first parent
tag above param 'o', not sure how to do it with DOM +} + +function ajaxReverseDNS(o, text) +{ + if(text) var ipaddr = text; + else var ipaddr = o.innerHTML; + rDnsObj = o; + rDnsBannerObj = bannerOn('Retrieving reverse DNS info...'); + ajaxGet(stdAjaxPrefix+'&_mode=rdns&ip='+ipaddr, function() { + if(ajax.readyState == 4) + { + off = fetch_offset(rDnsObj); + dim = fetch_dimensions(rDnsObj); + right = off['left'] + dim['w']; + top = off['top'] + dim['h']; + var thediv = document.createElement('div'); + thediv.className = 'info-box'; + thediv.style.margin = '0'; + thediv.style.position = 'absolute'; + thediv.style.top = top + 'px'; + thediv.style.display = 'none'; + thediv.style.zIndex = getHighestZ() + 2; + thediv.id = 'mdgDynamic_rDnsInfoDiv_'+Math.floor(Math.random() * 1000000); + thediv.innerHTML = 'Reverse DNS:
'+ajax.responseText+' Close'; + var body = document.getElementsByTagName('body'); + body = body[0]; + bannerOff(rDnsBannerObj); + body.appendChild(thediv); + thediv.style.display = 'block'; + left = fetch_dimensions(thediv); + thediv.style.display = 'none'; + left = right - left['w']; + thediv.style.left = left + 'px'; + thediv.style.display = 'block'; + fadeInfoBoxes(); + } + }); +} + +function bannerOn(text) +{ + darken(true); + var thediv = document.createElement('div'); + thediv.className = 'mdg-comment'; + thediv.style.padding = '0'; + thediv.style.marginLeft = '0'; + thediv.style.position = 'absolute'; + thediv.style.display = 'none'; + thediv.style.padding = '4px'; + thediv.style.fontSize = '14pt'; + thediv.id = 'mdgDynamic_bannerDiv_'+Math.floor(Math.random() * 1000000); + thediv.innerHTML = text; + + var body = document.getElementsByTagName('body'); + body = body[0]; + body.appendChild(thediv); + body.style.cursor = 'wait'; + + thediv.style.display = 'block'; + dim = fetch_dimensions(thediv); + thediv.style.display = 'none'; + bdim = { 'w' : getWidth(), 'h' : getHeight() }; + so = getScrollOffset(); + + left = (bdim['w'] / 2) - ( dim['w'] / 2 ); + top = (bdim['h'] / 2) - ( dim['h'] / 2 ) + so; + + thediv.style.top = top + 'px'; + thediv.style.left = left + 'px'; + + thediv.style.display = 'block'; + + return thediv.id; +} + +function bannerOff(id) +{ + e = document.getElementById(id); + if(!e) return; + e.innerHTML = ''; + e.style.display = 'none'; + var body = document.getElementsByTagName('body'); + body = body[0]; + body.style.cursor = 'default'; + enlighten(true); +} + +function disableUnload(message) +{ + if(typeof message != 'string') message = 'You may want to save your changes first.'; + var body = document.getElementsByTagName('body'); + body = body[0]; + body.onbeforeunload='return unescape(\''+escape(message)+'\')'; +} + +function enableUnload() +{ + var body = document.getElementsByTagName('body'); + body = body[0]; + body.onbeforeunload = null; +} + +/** + * Gets the highest z-index of all divs in the document + * @return integer + */ +function getHighestZ() +{ + z = 0; + var divs = document.getElementsByTagName('div'); + for(var i = 0; i < divs.length; i++) + { + if(divs[i].style.zIndex > z) z = divs[i].style.zIndex; + } + return z; +} + +function isKeyPressed(event) +{ + if (event.shiftKey==1) + { + shift = true; + } + else + { + shift = false; + } +} + +function moveDiv(div, newparent) +{ + var backup = div; + var oldparent = div.parentNode; + oldparent.removeChild(div); + newparent.appendChild(backup); +} + +function readCookie(name) {var nameEQ = name + "=";var ca = document.cookie.split(';');for(var i=0;i < ca.length;i++){var c = ca[i];while (c.charAt(0)==' ') c = c.substring(1,c.length);if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);}return null;} +function createCookie(name,value,days){if (days){var date = new Date();date.setTime(date.getTime()+(days*24*60*60*1000));var expires = "; expires="+date.toGMTString();}else var expires = "";document.cookie = name+"="+value+expires+"; path=/";} +function eraseCookie(name) {createCookie(name,"",-1);} + +var busyBannerID; +function goBusy(msg) +{ + if(!msg) msg = 'Please wait...'; + body = document.getElementsByTagName('body'); + body = body[0]; + body.style.cursor = 'wait'; + busyBannerID = bannerOn(msg); +} + +function unBusy() +{ + body = document.getElementsByTagName('body'); + body = body[0]; + body.style.cursor = 'default'; + bannerOff(busyBannerID); +} + +function setAjaxLoading() +{ + if ( document.getElementById('ajaxloadicon') ) + { + document.getElementById('ajaxloadicon').src=scriptPath + '/images/loading.gif'; + } +} + +function unsetAjaxLoading() +{ + if ( document.getElementById('ajaxloadicon') ) + { + document.getElementById('ajaxloadicon').src=scriptPath + '/images/spacer.gif'; + } +} + +/* + * Search boxes + */ + +function buildSearchBoxes() +{ + var divs = document.getElementsByTagName('*'); + var boxes = new Array(); + for ( var i = 0; i < divs.length; i++ ) + { + if ( divs[i].className) + { + if ( divs[i].className.substr(0, 9) == 'searchbox' ) + { + boxes.push(divs[i]); + } + } + } + for ( var i = 0; i < boxes.length; i++ ) + { + if ( boxes[i].className.match(/^searchbox\[([0-9]+)px\]$/) ) + { + var width = boxes[i].className.match(/^searchbox\[([0-9]+)px\]$/); + width = parseInt(width[1]); + } + else + { + var width = 120; + } + createSearchBox(boxes[i], width); + } +} + +function createSearchBox(parent, width) +{ + if ( typeof(parent) != 'object') + { + alert('BUG: createSearchBox(): parent is not an object'); + return false; + } + //parent.style.padding = '0px'; + //parent.style.textAlign = 'center'; + parent.style.width = width + 'px'; + var submit = document.createElement('div'); + submit.onclick = function() { searchFormSubmit(this); }; + submit.className = 'js-search-submit'; + var input = document.createElement('input'); + input.className = 'js-search-box'; + input.value = 'Search'; + input.name = 'q'; + input.style.width = ( width - 28 ) + 'px'; + input.onfocus = function() { if ( this.value == 'Search' ) this.value = ''; }; + input.onblur = function() { if ( this.value == '' ) this.value = 'Search'; }; + parent.appendChild(input); + var off = fetch_offset(input); + var top = off['top'] + 'px'; + var left = ( parseInt(off['left']) + ( width - 24 ) ) + 'px'; + submit.style.top = top; + submit.style.left = left; + parent.appendChild(submit); +} + +function searchFormSubmit(obj) +{ + var input = obj.previousSibling; + if ( input.value == 'Search' || input.value == '' ) + return false; + var p = obj; + while(true) + { + p = p.parentNode; + if ( !p ) + break; + if ( typeof(p.tagName) != 'string' ) + break; + else if ( p.tagName.toLowerCase() == 'form' ) + { + p.submit(); + } + else if ( p.tagName.toLowerCase() == 'body' ) + { + break; + } + } +} + +/* + * AJAX login box (experimental) + */ + +var ajax_auth_prompt_cache = false; +var ajax_auth_mb_cache = false; +var ajax_auth_level_cache = false; + +function ajaxPromptAdminAuth(call_on_ok, level) +{ + if ( typeof(call_on_ok) == 'function' ) + { + ajax_auth_prompt_cache = call_on_ok; + } + if ( !level ) + level = 2; + ajax_auth_level_cache = level; + var loading_win = '
\ +

Fetching an encryption key...

\ +

Not working? Use the alternate login form.

\ +

Please wait...

\ +
'; + ajax_auth_mb_cache = new messagebox(MB_OKCANCEL|MB_ICONLOCK, 'Please enter your username and password to continue.', loading_win); + ajax_auth_mb_cache.onbeforeclick['OK'] = ajaxValidateLogin; + ajaxAuthLoginInnerSetup(); +} + +function ajaxAuthLoginInnerSetup() +{ + ajaxGet(makeUrlNS('Special', 'Login', 'act=getkey'), function() { + if ( ajax.readyState == 4 ) + { + var response = ajax.responseText; + if ( response.substr(0,1) != '{' ) + { + alert('Invalid JSON response from server: ' + response); + return false; + } + response = parseJSON(response); + var form_html = ' \ + \ + \ + \ + \ + \ + \ + \ + \ +
Username: \ +
Password: \ +
\ +
Trouble logging in? Try the full login form.
\ + Did you forget your password?
\ + Maybe you need to create an account.
\ +
\ + \ + \ + '; + ajax_auth_mb_cache.updateContent(form_html); + $('messageBox').object.nextSibling.firstChild.tabindex = '3'; + $('ajaxlogin_user').object.focus(); + $('ajaxlogin_pass').object.onblur = function() { $('messageBox').object.nextSibling.firstChild.focus(); }; + } + }); +} + +function ajaxValidateLogin() +{ + var username,password,auth_enabled,crypt_key,crypt_data,challenge_salt,challenge_data; + username = document.getElementById('ajaxlogin_user'); + if ( !username ) + return false; + username = document.getElementById('ajaxlogin_user').value; + password = document.getElementById('ajaxlogin_pass').value; + auth_enabled = false; + + disableJSONExts(); + + // + // Encryption test + // + + var str = ''; + for(i=0;i

\ + '; + + ajax_auth_mb_cache.updateContent(loading_win); + + ajaxPost(makeUrlNS('Special', 'Login', 'act=ajaxlogin'), 'params=' + json_data, function() { + if ( ajax.readyState == 4 ) + { + var response = ajax.responseText; + if ( response.substr(0,1) != '{' ) + { + alert('Invalid JSON response from server: ' + response); + ajaxAuthLoginInnerSetup(); + return false; + } + response = parseJSON(response); + switch(response.result) + { + case 'success': + if ( typeof(ajax_auth_prompt_cache) == 'function' ) + { + ajax_auth_prompt_cache(response.key); + } + break; + case 'error': + alert(response.error); + ajaxAuthLoginInnerSetup(); + break; + default: + alert(ajax.responseText); + break; + } + } + }); + + return true; + +} + +// This code is in the public domain. Feel free to link back to http://jan.moesen.nu/ +function sprintf() +{ + if (!arguments || arguments.length < 1 || !RegExp) + { + return; + } + var str = arguments[0]; + var re = /([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X)(.*)/; + var a = b = [], numSubstitutions = 0, numMatches = 0; + while (a = re.exec(str)) + { + var leftpart = a[1], pPad = a[2], pJustify = a[3], pMinLength = a[4]; + var pPrecision = a[5], pType = a[6], rightPart = a[7]; + + //alert(a + '\n' + [a[0], leftpart, pPad, pJustify, pMinLength, pPrecision); + + numMatches++; + if (pType == '%') + { + subst = '%'; + } + else + { + numSubstitutions++; + if (numSubstitutions >= arguments.length) + { + alert('Error! Not enough function arguments (' + (arguments.length - 1) + ', excluding the string)\nfor the number of substitution parameters in string (' + numSubstitutions + ' so far).'); + } + var param = arguments[numSubstitutions]; + var pad = ''; + if (pPad && pPad.substr(0,1) == "'") pad = leftpart.substr(1,1); + else if (pPad) pad = pPad; + var justifyRight = true; + if (pJustify && pJustify === "-") justifyRight = false; + var minLength = -1; + if (pMinLength) minLength = parseInt(pMinLength); + var precision = -1; + if (pPrecision && pType == 'f') precision = parseInt(pPrecision.substring(1)); + var subst = param; + if (pType == 'b') subst = parseInt(param).toString(2); + else if (pType == 'c') subst = String.fromCharCode(parseInt(param)); + else if (pType == 'd') subst = parseInt(param) ? parseInt(param) : 0; + else if (pType == 'u') subst = Math.abs(param); + else if (pType == 'f') subst = (precision > -1) ? Math.round(parseFloat(param) * Math.pow(10, precision)) / Math.pow(10, precision): parseFloat(param); + else if (pType == 'o') subst = parseInt(param).toString(8); + else if (pType == 's') subst = param; + else if (pType == 'x') subst = ('' + parseInt(param).toString(16)).toLowerCase(); + else if (pType == 'X') subst = ('' + parseInt(param).toString(16)).toUpperCase(); + } + str = leftpart + subst + rightPart; + } + return str; +} + +function paginator_goto(parentobj, this_page, num_pages, perpage, url_string) +{ + var height = $(parentobj).Height(); + var width = $(parentobj).Width(); + var left = $(parentobj).Left(); + var top = $(parentobj).Top(); + var left_pos = left + width ; + var top_pos = height + top; + var div = document.createElement('div'); + div.style.position = 'absolute'; + div.style.top = top_pos + 'px'; + div.className = 'question-box'; + div.style.margin = '1px 0 0 2px'; + var vtmp = 'input_' + Math.floor(Math.random() * 1000000); + div.innerHTML = 'Go to page:
»×'; + + var body = document.getElementsByTagName('body')[0]; + body.appendChild(div); + + document.getElementById(vtmp).onkeypress = function(e){if(e.keyCode==13)this.nextSibling.nextSibling.onclick();}; + document.getElementById(vtmp).focus(); + + // fade the div + /* + if(!div.id) div.id = 'autofade_'+Math.floor(Math.random() * 100000); + var from = '#33FF33'; + Fat.fade_element(div.id,30,2000,from,Fat.get_bgcolor(div.id)); + */ + fly_in_bottom(div, false, true); + + var divh = $(div).Width(); + left_pos = left_pos - divh; + div.style.left = left_pos + 'px'; +} + +function paginator_submit(obj, max, perpage, formatstring) +{ + var userinput = obj.previousSibling.previousSibling.value; + userinput = parseInt(userinput); + var offset = ( userinput - 1 ) * perpage; + if ( userinput > max || isNaN(userinput) || userinput < 1 ) + { + new messagebox(MB_OK|MB_ICONSTOP, 'Invalid entry', 'Please enter a page number between 1 and ' + max + '.'); + return false; + } + var url = sprintf(formatstring, String(offset)); + fly_out_top(obj.parentNode, false, true); + window.location = url; +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/rijndael.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/rijndael.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,579 @@ +/* rijndael.js Rijndael Reference Implementation + Copyright (c) 2001 Fritz Schneider + + This software is provided as-is, without express or implied warranty. + Permission to use, copy, modify, distribute or sell this software, with or + without fee, for any purpose and by any individual or organization, is hereby + granted, provided that the above copyright notice and this paragraph appear + in all copies. Distribution as a part of an application or binary must + include the above copyright notice in the documentation and/or other materials + provided with the application or distribution. + + + As the above disclaimer notes, you are free to use this code however you + want. However, I would request that you send me an email + (fritz /at/ cs /dot/ ucsd /dot/ edu) to say hi if you find this code useful + or instructional. Seeing that people are using the code acts as + encouragement for me to continue development. If you *really* want to thank + me you can buy the book I wrote with Thomas Powell, _JavaScript: + _The_Complete_Reference_ :) + + This code is an UNOPTIMIZED REFERENCE implementation of Rijndael. + If there is sufficient interest I can write an optimized (word-based, + table-driven) version, although you might want to consider using a + compiled language if speed is critical to your application. As it stands, + one run of the monte carlo test (10,000 encryptions) can take up to + several minutes, depending upon your processor. You shouldn't expect more + than a few kilobytes per second in throughput. + + Also note that there is very little error checking in these functions. + Doing proper error checking is always a good idea, but the ideal + implementation (using the instanceof operator and exceptions) requires + IE5+/NS6+, and I've chosen to implement this code so that it is compatible + with IE4/NS4. + + And finally, because JavaScript doesn't have an explicit byte/char data + type (although JavaScript 2.0 most likely will), when I refer to "byte" + in this code I generally mean "32 bit integer with value in the interval + [0,255]" which I treat as a byte. + + See http://www-cse.ucsd.edu/~fritz/rijndael.html for more documentation + of the (very simple) API provided by this code. + + Fritz Schneider + fritz at cs.ucsd.edu + +*/ + +// Rijndael parameters -- Valid values are 128, 192, or 256 + +var keySizeInBits = ( typeof AES_BITS == 'number' ) ? AES_BITS : 128; +var blockSizeInBits = ( typeof AES_BLOCKSIZE == 'number' ) ? AES_BLOCKSIZE : 128; + +/////// You shouldn't have to modify anything below this line except for +/////// the function getRandomBytes(). +// +// Note: in the following code the two dimensional arrays are indexed as +// you would probably expect, as array[row][column]. The state arrays +// are 2d arrays of the form state[4][Nb]. + + +// The number of rounds for the cipher, indexed by [Nk][Nb] +var roundsArray = [ ,,,,[,,,,10,, 12,, 14],, + [,,,,12,, 12,, 14],, + [,,,,14,, 14,, 14] ]; + +// The number of bytes to shift by in shiftRow, indexed by [Nb][row] +var shiftOffsets = [ ,,,,[,1, 2, 3],,[,1, 2, 3],,[,1, 3, 4] ]; + +// The round constants used in subkey expansion +var Rcon = [ +0x01, 0x02, 0x04, 0x08, 0x10, 0x20, +0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, +0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, +0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, +0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ]; + +// Precomputed lookup table for the SBox +var SBox = [ + 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, +118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, +114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, +216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, +235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, +179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, +190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, +249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, +188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, +23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, +144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, + 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, +141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, + 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, +181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, +248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, +140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, + 22 ]; + +// Precomputed lookup table for the inverse SBox +var SBoxInverse = [ + 82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, +251, 124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, +233, 203, 84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, +250, 195, 78, 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, +109, 139, 209, 37, 114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, +204, 93, 101, 182, 146, 108, 112, 72, 80, 253, 237, 185, 218, 94, 21, + 70, 87, 167, 141, 157, 132, 144, 216, 171, 0, 140, 188, 211, 10, 247, +228, 88, 5, 184, 179, 69, 6, 208, 44, 30, 143, 202, 63, 15, 2, +193, 175, 189, 3, 1, 19, 138, 107, 58, 145, 17, 65, 79, 103, 220, +234, 151, 242, 207, 206, 240, 180, 230, 115, 150, 172, 116, 34, 231, 173, + 53, 133, 226, 249, 55, 232, 28, 117, 223, 110, 71, 241, 26, 113, 29, + 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27, 252, 86, 62, 75, +198, 210, 121, 32, 154, 219, 192, 254, 120, 205, 90, 244, 31, 221, 168, + 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, 128, 236, 95, 96, 81, +127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239, 160, +224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97, + 23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12, +125 ]; + +function str_split(string, chunklen) +{ + if(!chunklen) chunklen = 1; + ret = new Array(); + for ( i = 0; i < string.length; i+=chunklen ) + { + ret[ret.length] = string.slice(i, i+chunklen); + } + return ret; +} + +// This method circularly shifts the array left by the number of elements +// given in its parameter. It returns the resulting array and is used for +// the ShiftRow step. Note that shift() and push() could be used for a more +// elegant solution, but they require IE5.5+, so I chose to do it manually. + +function cyclicShiftLeft(theArray, positions) { + var temp = theArray.slice(0, positions); + theArray = theArray.slice(positions).concat(temp); + return theArray; +} + +// Cipher parameters ... do not change these +var Nk = keySizeInBits / 32; +var Nb = blockSizeInBits / 32; +var Nr = roundsArray[Nk][Nb]; + +// Multiplies the element "poly" of GF(2^8) by x. See the Rijndael spec. + +function xtime(poly) { + poly <<= 1; + return ((poly & 0x100) ? (poly ^ 0x11B) : (poly)); +} + +// Multiplies the two elements of GF(2^8) together and returns the result. +// See the Rijndael spec, but should be straightforward: for each power of +// the indeterminant that has a 1 coefficient in x, add y times that power +// to the result. x and y should be bytes representing elements of GF(2^8) + +function mult_GF256(x, y) { + var bit, result = 0; + + for (bit = 1; bit < 256; bit *= 2, y = xtime(y)) { + if (x & bit) + result ^= y; + } + return result; +} + +// Performs the substitution step of the cipher. State is the 2d array of +// state information (see spec) and direction is string indicating whether +// we are performing the forward substitution ("encrypt") or inverse +// substitution (anything else) + +function byteSub(state, direction) { + var S; + if (direction == "encrypt") // Point S to the SBox we're using + S = SBox; + else + S = SBoxInverse; + for (var i = 0; i < 4; i++) // Substitute for every byte in state + for (var j = 0; j < Nb; j++) + state[i][j] = S[state[i][j]]; +} + +// Performs the row shifting step of the cipher. + +function shiftRow(state, direction) { + for (var i=1; i<4; i++) // Row 0 never shifts + if (direction == "encrypt") + state[i] = cyclicShiftLeft(state[i], shiftOffsets[Nb][i]); + else + state[i] = cyclicShiftLeft(state[i], Nb - shiftOffsets[Nb][i]); + +} + +// Performs the column mixing step of the cipher. Most of these steps can +// be combined into table lookups on 32bit values (at least for encryption) +// to greatly increase the speed. + +function mixColumn(state, direction) { + var b = []; // Result of matrix multiplications + for (var j = 0; j < Nb; j++) { // Go through each column... + for (var i = 0; i < 4; i++) { // and for each row in the column... + if (direction == "encrypt") + b[i] = mult_GF256(state[i][j], 2) ^ // perform mixing + mult_GF256(state[(i+1)%4][j], 3) ^ + state[(i+2)%4][j] ^ + state[(i+3)%4][j]; + else + b[i] = mult_GF256(state[i][j], 0xE) ^ + mult_GF256(state[(i+1)%4][j], 0xB) ^ + mult_GF256(state[(i+2)%4][j], 0xD) ^ + mult_GF256(state[(i+3)%4][j], 9); + } + for (var i = 0; i < 4; i++) // Place result back into column + state[i][j] = b[i]; + } +} + +// Adds the current round key to the state information. Straightforward. + +function addRoundKey(state, roundKey) { + for (var j = 0; j < Nb; j++) { // Step through columns... + state[0][j] ^= (roundKey[j] & 0xFF); // and XOR + state[1][j] ^= ((roundKey[j]>>8) & 0xFF); + state[2][j] ^= ((roundKey[j]>>16) & 0xFF); + state[3][j] ^= ((roundKey[j]>>24) & 0xFF); + } +} + +// This function creates the expanded key from the input (128/192/256-bit) +// key. The parameter key is an array of bytes holding the value of the key. +// The returned value is an array whose elements are the 32-bit words that +// make up the expanded key. + +function keyExpansion(key) { + var expandedKey = new Array(); + var temp; + + // in case the key size or parameters were changed... + Nk = keySizeInBits / 32; + Nb = blockSizeInBits / 32; + Nr = roundsArray[Nk][Nb]; + + for (var j=0; j < Nk; j++) // Fill in input key first + expandedKey[j] = + (key[4*j]) | (key[4*j+1]<<8) | (key[4*j+2]<<16) | (key[4*j+3]<<24); + + // Now walk down the rest of the array filling in expanded key bytes as + // per Rijndael's spec + for (j = Nk; j < Nb * (Nr + 1); j++) { // For each word of expanded key + temp = expandedKey[j - 1]; + if (j % Nk == 0) + temp = ( (SBox[(temp>>8) & 0xFF]) | + (SBox[(temp>>16) & 0xFF]<<8) | + (SBox[(temp>>24) & 0xFF]<<16) | + (SBox[temp & 0xFF]<<24) ) ^ Rcon[Math.floor(j / Nk) - 1]; + else if (Nk > 6 && j % Nk == 4) + temp = (SBox[(temp>>24) & 0xFF]<<24) | + (SBox[(temp>>16) & 0xFF]<<16) | + (SBox[(temp>>8) & 0xFF]<<8) | + (SBox[temp & 0xFF]); + expandedKey[j] = expandedKey[j-Nk] ^ temp; + } + return expandedKey; +} + +// Rijndael's round functions... + +function Round(state, roundKey) { + byteSub(state, "encrypt"); + shiftRow(state, "encrypt"); + mixColumn(state, "encrypt"); + addRoundKey(state, roundKey); +} + +function InverseRound(state, roundKey) { + addRoundKey(state, roundKey); + mixColumn(state, "decrypt"); + shiftRow(state, "decrypt"); + byteSub(state, "decrypt"); +} + +function FinalRound(state, roundKey) { + byteSub(state, "encrypt"); + shiftRow(state, "encrypt"); + addRoundKey(state, roundKey); +} + +function InverseFinalRound(state, roundKey){ + addRoundKey(state, roundKey); + shiftRow(state, "decrypt"); + byteSub(state, "decrypt"); +} + +// encrypt is the basic encryption function. It takes parameters +// block, an array of bytes representing a plaintext block, and expandedKey, +// an array of words representing the expanded key previously returned by +// keyExpansion(). The ciphertext block is returned as an array of bytes. + +function encrypt(block, expandedKey) { + var i; + if (!block || block.length*8 != blockSizeInBits) + return; + if (!expandedKey) + return; + + block = packBytes(block); + addRoundKey(block, expandedKey); + for (i=1; i0; i--) + InverseRound(block, expandedKey.slice(Nb*i, Nb*(i+1))); + addRoundKey(block, expandedKey); + return unpackBytes(block); +} + +// This method takes a byte array (byteArray) and converts it to a string by +// applying String.fromCharCode() to each value and concatenating the result. +// The resulting string is returned. Note that this function SKIPS zero bytes +// under the assumption that they are padding added in formatPlaintext(). +// Obviously, do not invoke this method on raw data that can contain zero +// bytes. It is really only appropriate for printable ASCII/Latin-1 +// values. Roll your own function for more robust functionality :) + +function byteArrayToString(byteArray) { + var result = ""; + for(var i=0; i "10ff". The function returns a +// string. + +function byteArrayToHex(byteArray) { + var result = ""; + if (!byteArray) + return; + for (var i=0; i [16, 255]. This +// function returns an array. + +function hexToByteArray(hexString) { + /* + var byteArray = []; + if (hexString.length % 2) // must have even length + return; + if (hexString.indexOf("0x") == 0 || hexString.indexOf("0X") == 0) + hexString = hexString.substring(2); + for (var i = 0; i 0 && i < bpb; i--) + plaintext[plaintext.length] = 0; + + return plaintext; +} + +// Returns an array containing "howMany" random bytes. YOU SHOULD CHANGE THIS +// TO RETURN HIGHER QUALITY RANDOM BYTES IF YOU ARE USING THIS FOR A "REAL" +// APPLICATION. + +function getRandomBytes(howMany) { + var i; + var bytes = new Array(); + for (i=0; i0; block--) { + aBlock = + decrypt(ciphertext.slice(block*bpb,(block+1)*bpb), expandedKey); + if (mode == "CBC") + for (var i=0; i targetheight) + { + // reduce the height by intertiabase * inertiainc + heightnow -= inertiabase; + + // increase the intertiabase by the amount to keep it changing + inertiabase += inertiainc; + + // it's possible to exceed the height we want so we use a ternary - (condition) ? when true : when false; + block.style.height = (heightnow > 1) ? heightnow + "px" : targetheight + "px"; + } + else + { + // finished, so hide the div properly and kill the interval + clearInterval(slideinterval); + block.style.display = "none"; + } +} + +// this is the function our slideout interval uses, it keeps adding +// to the height till it's fully displayed +function slideout() +{ + block.style.display = 'block'; + if(heightnow < targetheight) + { + // increases the height by the inertia stuff + heightnow += inertiabase; + + // increase the inertia stuff + inertiabase += inertiainc; + + // it's possible to exceed the height we want so we use a ternary - (condition) ? when true : when false; + block.style.height = (heightnow < targetheight) ? heightnow + "px" : targetheight + "px"; + + } + else + { + // finished, so make sure the height is what it's meant to be (inertia can make it off a little) + // then kill the interval + clearInterval(slideinterval); + block.style.height = targetheight + "px"; + } +} + +// returns the height of the div from our array of such things +function divheight(d) +{ + for(var i=0; i([\s\S]*?)\/g); + code2 = ''; + for(var i in code) + if(typeof(code[i]) == 'string') + code2 = code2 + code[i]; + code = code2.replace(/\/g, "'$1' : \"$2\","); + code = '( { ' + code + ' "________null________" : false } )'; + vars = eval(code); + return vars; +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/toolbar.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/toolbar.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,60 @@ +// Page toolbar - selecting buttons + +function unselectAllButtonsMajor() +{ + if ( !document.getElementById('pagebar_main') ) + return false; + obj = document.getElementById('pagebar_main').firstChild; + while(obj) + { + if(obj.id == 'mdgToolbar_article' || obj.id == 'mdgToolbar_discussion') + { + obj.className = ''; + } + obj = obj.nextSibling; + } +} + +function unselectAllButtonsMinor() +{ + if ( !document.getElementById('pagebar_main') ) + return false; + obj = document.getElementById('pagebar_main').firstChild.nextSibling; + while(obj) + { + if ( obj.className != 'selected' ) + { + obj = obj.nextSibling; + continue; + } + if(obj.id != 'mdgToolbar_article' && obj.id != 'mdgToolbar_discussion') + { + if ( obj.className ) + obj.className = ''; + } + obj = obj.nextSibling; + } +} + +function selectButtonMajor(which) +{ + if ( !document.getElementById('pagebar_main') ) + return false; + if(typeof(document.getElementById('mdgToolbar_'+which)) == 'object') + { + unselectAllButtonsMajor(); + document.getElementById('mdgToolbar_'+which).className = 'selected'; + } +} + +function selectButtonMinor(which) +{ + if ( !document.getElementById('pagebar_main') ) + return false; + if(typeof(document.getElementById('mdgToolbar_'+which)) == 'object') + { + unselectAllButtonsMinor(); + document.getElementById('mdgToolbar_'+which).className = 'selected'; + } +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/static/windows.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/windows.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,342 @@ +/* + * Enano JWS - Javascript Windowing System + * Sorry if I stole the name ;) + * Copyright (C) 2006-2007 Dan Fuhry + * Yes, it's part of Enano, so it's GPL + */ + + var position; + function getScrollOffset() + { + var position; + if (self.pageYOffset) + { + position = self.pageYOffset; + } + else if (document.documentElement && document.documentElement.scrollTop) + { + position = document.documentElement.scrollTop; + } + else if (document.body) + { + position = document.body.scrollTop; + } + return position; + } + position = getScrollOffset(); + + var jws = { + position : position, + obj : null, + startup : function() { + jws.debug('jws.startup()'); + var divs = document.getElementsByTagName('div'); + if(IE) { position = document.body.scrollTop; } + else { position = window.pageYOffset; } + for(i=0;i
X
'; + divs[i].style.width = '640px'; + divs[i].style.height = '480px'; + Drag.init(tb, divs[i]); + } + } + }, + initWindow : function(o) { + jws.debug('jws.initWindow('+o+' ['+o.id+'])'); + var divs = document.getElementsByTagName('div'); + for(i=0;i
'; + divs[i].style.width = '640px'; + divs[i].style.height = '480px'; + Drag.init(tb, divs[i]); + } + } + }, + closeWin : function(id) { + jws.debug('jws.closeWin(\''+id+'\')'); + document.getElementById(id).style.display = 'none'; + enlighten(); + }, + openWin : function(id, x, y) { + darken(); + var e = document.getElementById(id); + if(!x) x = 640; + if(!y) y = 480; + jws.debug('jws.openWin(\''+id+'\', '+x+', '+y+')'); + e.style.display = 'block'; + e.style.width = x+'px'; + e.style.height = y+'px'; + + var divs = document.getElementsByTagName('div'); + for(i=0;i"+t; + } + } + } // class jws + +//window.onscroll=jws['scrollHandler']; + +/* + * Utility functions + */ + +// getElementWidth() and getElementHeight() +// Source: http://www.aspandjavascript.co.uk/javascript/javascript_api/get_element_width_height.asp + +function getElementHeight(Elem) { + if (ns4) { + var elem = getObjNN4(document, Elem); + return elem.clip.height; + } else { + if(document.getElementById) { + var elem = document.getElementById(Elem); + } else if (document.all){ + var elem = document.all[Elem]; + } + if (op5) { + xPos = elem.style.pixelHeight; + } else { + xPos = elem.offsetHeight; + } + return xPos; + } +} + +function getElementWidth(Elem) { + if (ns4) { + var elem = getObjNN4(document, Elem); + return elem.clip.width; + } else { + if(document.getElementById) { + var elem = document.getElementById(Elem); + } else if (document.all){ + var elem = document.all[Elem]; + } + if (op5) { + xPos = elem.style.pixelWidth; + } else { + xPos = elem.offsetWidth; + } + return xPos; + } +} + +function getHeight() { + var myHeight = 0; + if( typeof( window.innerWidth ) == 'number' ) { + myHeight = window.innerHeight; + } else if( document.documentElement && + ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) { + myHeight = document.documentElement.clientHeight; + } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) { + myHeight = document.body.clientHeight; + } + return myHeight; +} + +function getWidth() { + var myWidth = 0; + if( typeof( window.innerWidth ) == 'number' ) { + myWidth = window.innerWidth; + } else if( document.documentElement && + ( document.documentElement.clientWidth || document.documentElement.clientWidth ) ) { + myWidth = document.documentElement.clientWidth; + } else if( document.body && ( document.body.clientWidth || document.body.clientWidth ) ) { + myWidth = document.body.clientWidth; + } + return myWidth; +} + +/************************************************** + * dom-drag.js + * 09.25.2001 + * www.youngpup.net + **************************************************/ + +var Drag = { + + obj : null, + + init : function(o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper) + { + o.onmousedown = Drag.start; + + o.hmode = bSwapHorzRef ? false : true ; + o.vmode = bSwapVertRef ? false : true ; + + o.root = oRoot && oRoot != null ? oRoot : o ; + + if (o.hmode && isNaN(parseInt(o.root.style.left ))) o.root.style.left = "0px"; + if (o.vmode && isNaN(parseInt(o.root.style.top ))) o.root.style.top = "0px"; + if (!o.hmode && isNaN(parseInt(o.root.style.right ))) o.root.style.right = "0px"; + if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px"; + + o.minX = typeof minX != 'undefined' ? minX : null; + o.minY = typeof minY != 'undefined' ? minY : null; + o.maxX = typeof maxX != 'undefined' ? maxX : null; + o.maxY = typeof maxY != 'undefined' ? maxY : null; + + o.xMapper = fXMapper ? fXMapper : null; + o.yMapper = fYMapper ? fYMapper : null; + + o.root.onDragStart = new Function(); + o.root.onDragEnd = new Function(); + o.root.onDrag = new Function(); + }, + + start : function(e) + { + var o = Drag.obj = this; + e = Drag.fixE(e); + var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom); + var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right ); + o.root.onDragStart(x, y); + + o.lastMouseX = e.clientX; + o.lastMouseY = e.clientY; + + if (o.hmode) { + if (o.minX != null) o.minMouseX = e.clientX - x + o.minX; + if (o.maxX != null) o.maxMouseX = o.minMouseX + o.maxX - o.minX; + } else { + if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x; + if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x; + } + + if (o.vmode) { + if (o.minY != null) o.minMouseY = e.clientY - y + o.minY; + if (o.maxY != null) o.maxMouseY = o.minMouseY + o.maxY - o.minY; + } else { + if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y; + if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y; + } + + document.onmousemove = Drag.drag; + document.onmouseup = Drag.end; + + return false; + }, + + drag : function(e) + { + e = Drag.fixE(e); + var o = Drag.obj; + + var ey = e.clientY; + var ex = e.clientX; + var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom); + var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right ); + var nx, ny; + + if (o.minX != null) ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX); + if (o.maxX != null) ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX); + if (o.minY != null) ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY); + if (o.maxY != null) ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY); + + nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1)); + ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1)); + + if (o.xMapper) nx = o.xMapper(y) + else if (o.yMapper) ny = o.yMapper(x) + + Drag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px"; + Drag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px"; + Drag.obj.lastMouseX = ex; + Drag.obj.lastMouseY = ey; + + Drag.obj.root.onDrag(nx, ny); + return false; + }, + + end : function() + { + document.onmousemove = getMouseXY; + document.onmouseup = null; + Drag.obj.root.onDragEnd( parseInt(Drag.obj.root.style[Drag.obj.hmode ? "left" : "right"]), + parseInt(Drag.obj.root.style[Drag.obj.vmode ? "top" : "bottom"])); + Drag.obj = null; + }, + + fixE : function(e) + { + if (typeof e == 'undefined') e = window.event; + if (typeof e.layerX == 'undefined') e.layerX = e.offsetX; + if (typeof e.layerY == 'undefined') e.layerY = e.offsetY; + return e; + } +}; + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/blank.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/blank.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,9 @@ + + + blank_page + + + + + + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/langs/en.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/langs/en.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,41 @@ +// UK lang variables + +tinyMCE.addToLang('',{ +bold_desc : 'Bold (Ctrl+B)', +italic_desc : 'Italic (Ctrl+I)', +underline_desc : 'Underline (Ctrl+U)', +striketrough_desc : 'Strikethrough', +justifyleft_desc : 'Align left', +justifycenter_desc : 'Align center', +justifyright_desc : 'Align right', +justifyfull_desc : 'Align full', +bullist_desc : 'Unordered list', +numlist_desc : 'Ordered list', +outdent_desc : 'Outdent', +indent_desc : 'Indent', +undo_desc : 'Undo (Ctrl+Z)', +redo_desc : 'Redo (Ctrl+Y)', +link_desc : 'Insert/edit link', +unlink_desc : 'Unlink', +image_desc : 'Insert/edit image', +cleanup_desc : 'Cleanup messy code', +focus_alert : 'A editor instance must be focused before using this command.', +edit_confirm : 'Do you want to use the WYSIWYG mode for this textarea?', +insert_link_title : 'Insert/edit link', +insert : 'Insert', +update : 'Update', +cancel : 'Cancel', +insert_link_url : 'Link URL', +insert_link_target : 'Target', +insert_link_target_same : 'Open link in the same window', +insert_link_target_blank : 'Open link in a new window', +insert_image_title : 'Insert/edit image', +insert_image_src : 'Image URL', +insert_image_alt : 'Image description', +help_desc : 'Help', +bold_img : "bold.gif", +italic_img : "italic.gif", +underline_img : "underline.gif", +clipboard_msg : 'Copy/Cut/Paste is not available in Mozilla and Firefox.\nDo you want more information about this issue?', +popup_blocked : 'Sorry, but we have noticed that your popup-blocker has disabled a window that provides application functionality. You will need to disable popup blocking on this site in order to fully utilize this tool.' +}); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/langs/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/langs/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,9 @@ +Beginning with version 2.0.5 the language packs are no +longer included with the core distribution. +Language packs can be downloaded from the TinyMCE website: +http://tinymce.moxiecode.com/download.php + +The language pack codes are based on ISO-639-1: +http://www.loc.gov/standards/iso639-2/englangn.html + +Plrease try using entities if possible. Like å etc for non a-z characters. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/license.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/license.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advhr/css/advhr.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advhr/css/advhr.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,23 @@ +input.radio { + border: 1px none #000000; + background-color: transparent; + vertical-align: middle; +} + +.panel_wrapper div.current { + height: 80px; +} + +#width { + width: 50px; + vertical-align: middle; +} + +#width2 { + width: 50px; + vertical-align: middle; +} + +#size { + width: 100px; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advhr/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advhr/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('advhr');var TinyMCE_AdvancedHRPlugin={getInfo:function(){return{longname:'Advanced HR',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advhr',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},getControlHTML:function(cn){switch(cn){case"advhr":return tinyMCE.getButtonHTML(cn,'lang_insert_advhr_desc','{$pluginurl}/images/advhr.gif','mceAdvancedHr')}return""},execCommand:function(editor_id,element,command,user_interface,value){switch(command){case"mceAdvancedHr":var template=new Array();template['file']='../../plugins/advhr/rule.htm';template['width']=250;template['height']=160;template['width']+=tinyMCE.getLang('lang_advhr_delta_width',0);template['height']+=tinyMCE.getLang('lang_advhr_delta_height',0);var size="",width="",noshade="";if(tinyMCE.selectedElement!=null&&tinyMCE.selectedElement.nodeName.toLowerCase()=="hr"){tinyMCE.hrElement=tinyMCE.selectedElement;if(tinyMCE.hrElement){size=tinyMCE.hrElement.getAttribute('size')?tinyMCE.hrElement.getAttribute('size'):"";width=tinyMCE.hrElement.getAttribute('width')?tinyMCE.hrElement.getAttribute('width'):"";noshade=tinyMCE.hrElement.getAttribute('noshade')?tinyMCE.hrElement.getAttribute('noshade'):""}tinyMCE.openWindow(template,{editor_id:editor_id,size:size,width:width,noshade:noshade,mceDo:'update'})}else{if(tinyMCE.isMSIE){tinyMCE.execInstanceCommand(editor_id,'mceInsertContent',false,'
')}else{tinyMCE.openWindow(template,{editor_id:editor_id,inline:"yes",size:size,width:width,noshade:noshade,mceDo:'insert'})}}return true}return false},handleNodeChange:function(editor_id,node,undo_index,undo_levels,visual_aid,any_selection){if(node==null)return;do{if(node.nodeName=="HR"){tinyMCE.switchClass(editor_id+'_advhr','mceButtonSelected');return true}}while((node=node.parentNode));tinyMCE.switchClass(editor_id+'_advhr','mceButtonNormal');return true}};tinyMCE.addPlugin("advhr",TinyMCE_AdvancedHRPlugin); \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advhr/editor_plugin_src.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advhr/editor_plugin_src.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,90 @@ +/** + * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $ + * + * @author Moxiecode + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +/* Import plugin specific language pack */ +tinyMCE.importPluginLanguagePack('advhr'); + +var TinyMCE_AdvancedHRPlugin = { + getInfo : function() { + return { + longname : 'Advanced HR', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advhr', + version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion + } + }, + + getControlHTML : function(cn) { + switch (cn) { + case "advhr": + return tinyMCE.getButtonHTML(cn, 'lang_insert_advhr_desc', '{$pluginurl}/images/advhr.gif', 'mceAdvancedHr'); + } + + return ""; + }, + + /** + * Executes the mceAdvanceHr command. + */ + execCommand : function(editor_id, element, command, user_interface, value) { + // Handle commands + switch (command) { + case "mceAdvancedHr": + var template = new Array(); + + template['file'] = '../../plugins/advhr/rule.htm'; // Relative to theme + template['width'] = 250; + template['height'] = 160; + + template['width'] += tinyMCE.getLang('lang_advhr_delta_width', 0); + template['height'] += tinyMCE.getLang('lang_advhr_delta_height', 0); + + var size = "", width = "", noshade = ""; + if (tinyMCE.selectedElement != null && tinyMCE.selectedElement.nodeName.toLowerCase() == "hr") { + tinyMCE.hrElement = tinyMCE.selectedElement; + + if (tinyMCE.hrElement) { + size = tinyMCE.hrElement.getAttribute('size') ? tinyMCE.hrElement.getAttribute('size') : ""; + width = tinyMCE.hrElement.getAttribute('width') ? tinyMCE.hrElement.getAttribute('width') : ""; + noshade = tinyMCE.hrElement.getAttribute('noshade') ? tinyMCE.hrElement.getAttribute('noshade') : ""; + } + + tinyMCE.openWindow(template, {editor_id : editor_id, size : size, width : width, noshade : noshade, mceDo : 'update'}); + } else { + if (tinyMCE.isMSIE) { + tinyMCE.execInstanceCommand(editor_id, 'mceInsertContent', false,'
'); + } else { + tinyMCE.openWindow(template, {editor_id : editor_id, inline : "yes", size : size, width : width, noshade : noshade, mceDo : 'insert'}); + } + } + + return true; + } + + // Pass to next handler in chain + return false; + }, + + handleNodeChange : function(editor_id, node, undo_index, undo_levels, visual_aid, any_selection) { + if (node == null) + return; + + do { + if (node.nodeName == "HR") { + tinyMCE.switchClass(editor_id + '_advhr', 'mceButtonSelected'); + return true; + } + } while ((node = node.parentNode)); + + tinyMCE.switchClass(editor_id + '_advhr', 'mceButtonNormal'); + + return true; + } +}; + +tinyMCE.addPlugin("advhr", TinyMCE_AdvancedHRPlugin); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advhr/images/advhr.gif Binary file includes/clientside/tinymce/plugins/advhr/images/advhr.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advhr/jscripts/rule.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advhr/jscripts/rule.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,43 @@ +function init() { + tinyMCEPopup.resizeToInnerSize(); + + var formObj = document.forms[0]; + formObj.width.value = tinyMCE.getWindowArg('width'); + formObj.size.value = tinyMCE.getWindowArg('size'); + formObj.insert.value = tinyMCE.getLang('lang_' + tinyMCE.getWindowArg('mceDo'),'Insert',true); + if (tinyMCE.getWindowArg('noshade')) { + formObj.noshade.checked = true; + } + if (tinyMCE.getWindowArg('width').lastIndexOf('%')!=-1) { + formObj.width2.value = "%"; + formObj.width.value = formObj.width.value.substring(0,formObj.width.value.length-1); + } +} + +function insertHR() { + var formObj = document.forms[0]; + var width = formObj.width.value; + var size = formObj.size.value; + var html = ' + + {$lang_insert_advhr_desc} + + + + + + + +
+ + +
+
+ + + + + + + + + + + + + +
+ + +
+
+
+ +
+
+ +
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advimage/css/advimage.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advimage/css/advimage.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,66 @@ +/* CSS file for advimage plugin popup */ + +.mceImageList { + width: 280px; +} + +.mceActionPanel { + margin-top: 7px; +} + +.alignPreview { + border: 1px solid black; + width: 140px; + height: 140px; + overflow: hidden; + padding: 5px; +} + +.checkbox { + border: 0; +} + +.panel_wrapper div.current { + height: 305px; +} + +#prev { + margin: 0; + border: 1px + solid black; + width: 99%; + height: 150px; + overflow: auto; +} + +#align, #classlist { + width: 150px; +} + +#width, #height { + vertical-align: middle; + width: 50px; + text-align: center; +} + +#vspace, #hspace, #border { + vertical-align: middle; + width: 30px; + text-align: center; +} + +#classlist { + width: 180px; +} + +input { + width: 280px; +} + +#constrain, #onmousemovecheck { + width: auto; +} + +#id, #dir, #lang, #usemap, #longdesc { + width: 200px; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advimage/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advimage/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('advimage');var TinyMCE_AdvancedImagePlugin={getInfo:function(){return{longname:'Advanced image',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advimage',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},getControlHTML:function(cn){switch(cn){case"image":return tinyMCE.getButtonHTML(cn,'lang_image_desc','{$themeurl}/images/image.gif','mceAdvImage')}return""},execCommand:function(editor_id,element,command,user_interface,value){switch(command){case"mceAdvImage":var template=new Array();template['file']='../../plugins/advimage/image.htm';template['width']=480;template['height']=380;template['width']+=tinyMCE.getLang('lang_advimage_delta_width',0);template['height']+=tinyMCE.getLang('lang_advimage_delta_height',0);var inst=tinyMCE.getInstanceById(editor_id);var elm=inst.getFocusElement();if(elm!=null&&tinyMCE.getAttrib(elm,'class').indexOf('mceItem')!=-1)return true;tinyMCE.openWindow(template,{editor_id:editor_id,inline:"yes"});return true}return false},cleanup:function(type,content){switch(type){case"insert_to_editor_dom":var imgs=content.getElementsByTagName("img"),src,i;for(i=0;i + + {$lang_insert_image_title} + + + + + + + + + +
+ + +
+
+
+ {$lang_advimage_general} + + + + + + + + + + + + + + + + + + +
+ + + + +
 
 
+
+ +
+ {$lang_advimage_preview} + +
+
+ +
+
+ {$lang_advimage_tab_appearance} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ {$lang_advimage_example_img} + Lorem ipsum, Dolor sit amet, consectetuer adipiscing loreum ipsum edipiscing elit, sed diam + nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.Loreum ipsum + edipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam + erat volutpat. +
+
+ x + px +
  + + + + +
+
+ +
+
+
+ +
+
+ {$lang_advimage_swap_image} + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
 
 
+ + + + +
 
 
+
+ +
+ {$lang_advimage_misc} + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
 
+
+
+
+ +
+
+ +
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advimage/images/sample.gif Binary file includes/clientside/tinymce/plugins/advimage/images/sample.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advimage/jscripts/functions.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advimage/jscripts/functions.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,534 @@ +/* Functions for the advimage plugin popup */ + +var preloadImg = null; +var orgImageWidth, orgImageHeight; + +function preinit() { + // Initialize + tinyMCE.setWindowArg('mce_windowresize', false); + + // Import external list url javascript + var url = tinyMCE.getParam("external_image_list_url"); + if (url != null) { + // Fix relative + if (url.charAt(0) != '/' && url.indexOf('://') == -1) + url = tinyMCE.documentBasePath + "/" + url; + + document.write(''); + } +} + +function convertURL(url, node, on_save) { + return eval("tinyMCEPopup.windowOpener." + tinyMCE.settings['urlconverter_callback'] + "(url, node, on_save);"); +} + +function getImageSrc(str) { + var pos = -1; + + if (!str) + return ""; + + if ((pos = str.indexOf('this.src=')) != -1) { + var src = str.substring(pos + 10); + + src = src.substring(0, src.indexOf('\'')); + + if (tinyMCE.getParam('convert_urls')) + src = convertURL(src, null, true); + + return src; + } + + return ""; +} + +function init() { + tinyMCEPopup.resizeToInnerSize(); + + var formObj = document.forms[0]; + var inst = tinyMCE.getInstanceById(tinyMCE.getWindowArg('editor_id')); + var elm = inst.getFocusElement(); + var action = "insert"; + var html = ""; + + // Image list src + html = getImageListHTML('imagelistsrc','src','onSelectMainImage'); + if (html == "") + document.getElementById("imagelistsrcrow").style.display = 'none'; + else + document.getElementById("imagelistsrccontainer").innerHTML = html; + + // Image list oversrc + html = getImageListHTML('imagelistover','onmouseoversrc'); + if (html == "") + document.getElementById("imagelistoverrow").style.display = 'none'; + else + document.getElementById("imagelistovercontainer").innerHTML = html; + + // Image list outsrc + html = getImageListHTML('imagelistout','onmouseoutsrc'); + if (html == "") + document.getElementById("imagelistoutrow").style.display = 'none'; + else + document.getElementById("imagelistoutcontainer").innerHTML = html; + + // Src browser + html = getBrowserHTML('srcbrowser','src','image','advimage'); + document.getElementById("srcbrowsercontainer").innerHTML = html; + + // Over browser + html = getBrowserHTML('oversrcbrowser','onmouseoversrc','image','advimage'); + document.getElementById("onmouseoversrccontainer").innerHTML = html; + + // Out browser + html = getBrowserHTML('outsrcbrowser','onmouseoutsrc','image','advimage'); + document.getElementById("onmouseoutsrccontainer").innerHTML = html; + + // Longdesc browser + html = getBrowserHTML('longdescbrowser','longdesc','file','advimage'); + document.getElementById("longdesccontainer").innerHTML = html; + + // Resize some elements + if (isVisible('srcbrowser')) + document.getElementById('src').style.width = '260px'; + + if (isVisible('oversrcbrowser')) + document.getElementById('onmouseoversrc').style.width = '260px'; + + if (isVisible('outsrcbrowser')) + document.getElementById('onmouseoutsrc').style.width = '260px'; + + if (isVisible('longdescbrowser')) + document.getElementById('longdesc').style.width = '180px'; + + // Check action + if (elm != null && elm.nodeName == "IMG") + action = "update"; + + formObj.insert.value = tinyMCE.getLang('lang_' + action, 'Insert', true); + + if (action == "update") { + var src = tinyMCE.getAttrib(elm, 'src'); + var onmouseoversrc = getImageSrc(tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onmouseover'))); + var onmouseoutsrc = getImageSrc(tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onmouseout'))); + + src = convertURL(src, elm, true); + + // Use mce_src if found + var mceRealSrc = tinyMCE.getAttrib(elm, 'mce_src'); + if (mceRealSrc != "") { + src = mceRealSrc; + + if (tinyMCE.getParam('convert_urls')) + src = convertURL(src, elm, true); + } + + if (onmouseoversrc != "" && tinyMCE.getParam('convert_urls')) + onmouseoversrc = convertURL(onmouseoversrc, elm, true); + + if (onmouseoutsrc != "" && tinyMCE.getParam('convert_urls')) + onmouseoutsrc = convertURL(onmouseoutsrc, elm, true); + + // Setup form data + var style = tinyMCE.parseStyle(tinyMCE.getAttrib(elm, "style")); + + // Store away old size + orgImageWidth = trimSize(getStyle(elm, 'width')) + orgImageHeight = trimSize(getStyle(elm, 'height')); + + formObj.src.value = src; + formObj.alt.value = tinyMCE.getAttrib(elm, 'alt'); + formObj.title.value = tinyMCE.getAttrib(elm, 'title'); + formObj.border.value = trimSize(getStyle(elm, 'border', 'borderWidth')); + formObj.vspace.value = tinyMCE.getAttrib(elm, 'vspace'); + formObj.hspace.value = tinyMCE.getAttrib(elm, 'hspace'); + formObj.width.value = orgImageWidth; + formObj.height.value = orgImageHeight; + formObj.onmouseoversrc.value = onmouseoversrc; + formObj.onmouseoutsrc.value = onmouseoutsrc; + formObj.id.value = tinyMCE.getAttrib(elm, 'id'); + formObj.dir.value = tinyMCE.getAttrib(elm, 'dir'); + formObj.lang.value = tinyMCE.getAttrib(elm, 'lang'); + formObj.longdesc.value = tinyMCE.getAttrib(elm, 'longdesc'); + formObj.usemap.value = tinyMCE.getAttrib(elm, 'usemap'); + formObj.style.value = tinyMCE.serializeStyle(style); + + // Select by the values + if (tinyMCE.isMSIE) + selectByValue(formObj, 'align', getStyle(elm, 'align', 'styleFloat')); + else + selectByValue(formObj, 'align', getStyle(elm, 'align', 'cssFloat')); + + addClassesToList('classlist', 'advimage_styles'); + + selectByValue(formObj, 'classlist', tinyMCE.getAttrib(elm, 'class')); + selectByValue(formObj, 'imagelistsrc', src); + selectByValue(formObj, 'imagelistover', onmouseoversrc); + selectByValue(formObj, 'imagelistout', onmouseoutsrc); + + updateStyle(); + showPreviewImage(src, true); + changeAppearance(); + + window.focus(); + } else + addClassesToList('classlist', 'advimage_styles'); + + // If option enabled default contrain proportions to checked + if (tinyMCE.getParam("advimage_constrain_proportions", true)) + formObj.constrain.checked = true; + + // Check swap image if valid data + if (formObj.onmouseoversrc.value != "" || formObj.onmouseoutsrc.value != "") + setSwapImageDisabled(false); + else + setSwapImageDisabled(true); +} + +function setSwapImageDisabled(state) { + var formObj = document.forms[0]; + + formObj.onmousemovecheck.checked = !state; + + setBrowserDisabled('overbrowser', state); + setBrowserDisabled('outbrowser', state); + + if (formObj.imagelistover) + formObj.imagelistover.disabled = state; + + if (formObj.imagelistout) + formObj.imagelistout.disabled = state; + + formObj.onmouseoversrc.disabled = state; + formObj.onmouseoutsrc.disabled = state; +} + +function setAttrib(elm, attrib, value) { + var formObj = document.forms[0]; + var valueElm = formObj.elements[attrib]; + + if (typeof(value) == "undefined" || value == null) { + value = ""; + + if (valueElm) + value = valueElm.value; + } + + if (value != "") { + elm.setAttribute(attrib, value); + + if (attrib == "style") + attrib = "style.cssText"; + + if (attrib == "longdesc") + attrib = "longDesc"; + + if (attrib == "width") { + attrib = "style.width"; + value = value + "px"; + value = value.replace(/%px/g, 'px'); + } + + if (attrib == "height") { + attrib = "style.height"; + value = value + "px"; + value = value.replace(/%px/g, 'px'); + } + + if (attrib == "class") + attrib = "className"; + + eval('elm.' + attrib + "=value;"); + } else + elm.removeAttribute(attrib); +} + +function makeAttrib(attrib, value) { + var formObj = document.forms[0]; + var valueElm = formObj.elements[attrib]; + + if (typeof(value) == "undefined" || value == null) { + value = ""; + + if (valueElm) + value = valueElm.value; + } + + if (value == "") + return ""; + + // XML encode it + value = value.replace(/&/g, '&'); + value = value.replace(/\"/g, '"'); + value = value.replace(//g, '>'); + + return ' ' + attrib + '="' + value + '"'; +} + +function insertAction() { + var inst = tinyMCE.getInstanceById(tinyMCE.getWindowArg('editor_id')); + var elm = inst.getFocusElement(); + var formObj = document.forms[0]; + var src = formObj.src.value; + var onmouseoversrc = formObj.onmouseoversrc.value; + var onmouseoutsrc = formObj.onmouseoutsrc.value; + + if (!AutoValidator.validate(formObj)) { + alert(tinyMCE.getLang('lang_invalid_data')); + return false; + } + + if (tinyMCE.getParam("accessibility_warnings")) { + if (formObj.alt.value == "" && !confirm(tinyMCE.getLang('lang_advimage_missing_alt', '', true))) + return; + } + + if (onmouseoversrc && onmouseoversrc != "") + onmouseoversrc = "this.src='" + convertURL(onmouseoversrc, tinyMCE.imgElement) + "';"; + + if (onmouseoutsrc && onmouseoutsrc != "") + onmouseoutsrc = "this.src='" + convertURL(onmouseoutsrc, tinyMCE.imgElement) + "';"; + + if (elm != null && elm.nodeName == "IMG") { + setAttrib(elm, 'src', convertURL(src, tinyMCE.imgElement)); + setAttrib(elm, 'mce_src', src); + setAttrib(elm, 'alt'); + setAttrib(elm, 'title'); + setAttrib(elm, 'border'); + setAttrib(elm, 'vspace'); + setAttrib(elm, 'hspace'); + setAttrib(elm, 'width'); + setAttrib(elm, 'height'); + setAttrib(elm, 'onmouseover', onmouseoversrc); + setAttrib(elm, 'onmouseout', onmouseoutsrc); + setAttrib(elm, 'id'); + setAttrib(elm, 'dir'); + setAttrib(elm, 'lang'); + setAttrib(elm, 'longdesc'); + setAttrib(elm, 'usemap'); + setAttrib(elm, 'style'); + setAttrib(elm, 'class', getSelectValue(formObj, 'classlist')); + setAttrib(elm, 'align', getSelectValue(formObj, 'align')); + + //tinyMCEPopup.execCommand("mceRepaint"); + + // Repaint if dimensions changed + if (formObj.width.value != orgImageWidth || formObj.height.value != orgImageHeight) + inst.repaint(); + + // Refresh in old MSIE + if (tinyMCE.isMSIE5) + elm.outerHTML = elm.outerHTML; + } else { + var html = "' +} + +function updateImageData() { + var formObj = document.forms[0]; + + preloadImg = document.getElementById('previewImg'); + + if (formObj.width.value == "") + formObj.width.value = preloadImg.width; + + if (formObj.height.value == "") + formObj.height.value = preloadImg.height; + + updateStyle(); +} + +function resetImageData() { + var formObj = document.forms[0]; + formObj.width.value = formObj.height.value = ""; +} + +function getSelectValue(form_obj, field_name) { + var elm = form_obj.elements[field_name]; + + if (elm == null || elm.options == null) + return ""; + + return elm.options[elm.selectedIndex].value; +} + +function getImageListHTML(elm_id, target_form_element, onchange_func) { + if (typeof(tinyMCEImageList) == "undefined" || tinyMCEImageList.length == 0) + return ""; + + var html = ""; + + html += ''; + + return html; + + // tinyMCE.debug('-- image list start --', html, '-- image list end --'); +} + +// While loading +preinit(); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advimage/langs/en.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advimage/langs/en.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,27 @@ +// UK lang variables + +tinyMCE.addToLang('advimage',{ +tab_general : 'General', +tab_appearance : 'Appearance', +tab_advanced : 'Advanced', +general : 'General', +title : 'Title', +preview : 'Preview', +constrain_proportions : 'Constrain proportions', +langdir : 'Language direction', +langcode : 'Language code', +long_desc : 'Long description link', +style : 'Style', +classes : 'Classes', +ltr : 'Left to right', +rtl : 'Right to left', +id : 'Id', +image_map : 'Image map', +swap_image : 'Swap image', +alt_image : 'Alternative image', +mouseover : 'for mouse over', +mouseout : 'for mouse out', +misc : 'Miscellaneous', +example_img : 'Appearance preview image', +missing_alt : 'Are you sure you want to continue without including an Image Description? Without it the image may not be accessible to some users with disabilities, or to those using a text browser, or browsing the Web with images turned off.' +}); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advimage/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advimage/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +Check the TinyMCE documentation for details on this plugin. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advlink/css/advlink.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advlink/css/advlink.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,35 @@ +/* CSS file for advimage plugin popup */ + +.mceLinkList, .mceAnchorList, #targetlist { + width: 280px; +} + +.mceActionPanel { + margin-top: 7px; +} + +.panel_wrapper div.current { + height: 320px; +} + +#classlist, #title, #href { + width: 280px; +} + +#popupurl, #popupname { + width: 200px; +} + +#popupwidth, #popupheight, #popupleft, #popuptop { + width: 30px; + vertical-align: middle; + text-align: center; +} + +#id, #style, #classes, #target, #dir, #hreflang, #lang, #charset, #type, #rel, #rev, #tabindex, #accesskey { + width: 200px; +} + +#events_panel input { + width: 200px; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advlink/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advlink/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('advlink');var TinyMCE_AdvancedLinkPlugin={getInfo:function(){return{longname:'Advanced link',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advlink',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},initInstance:function(inst){inst.addShortcut('ctrl','k','lang_advlink_desc','mceAdvLink')},getControlHTML:function(cn){switch(cn){case"link":return tinyMCE.getButtonHTML(cn,'lang_link_desc','{$themeurl}/images/link.gif','mceAdvLink')}return""},execCommand:function(editor_id,element,command,user_interface,value){switch(command){case"mceAdvLink":var anySelection=false;var inst=tinyMCE.getInstanceById(editor_id);var focusElm=inst.getFocusElement();var selectedText=inst.selection.getSelectedText();if(tinyMCE.selectedElement)anySelection=(tinyMCE.selectedElement.nodeName.toLowerCase()=="img")||(selectedText&&selectedText.length>0);if(anySelection||(focusElm!=null&&focusElm.nodeName=="A")){var template=new Array();template['file']='../../plugins/advlink/link.htm';template['width']=480;template['height']=400;template['width']+=tinyMCE.getLang('lang_advlink_delta_width',0);template['height']+=tinyMCE.getLang('lang_advlink_delta_height',0);tinyMCE.openWindow(template,{editor_id:editor_id,inline:"yes"})}return true}return false},handleNodeChange:function(editor_id,node,undo_index,undo_levels,visual_aid,any_selection){if(node==null)return;do{if(node.nodeName=="A"&&tinyMCE.getAttrib(node,'href')!=""){tinyMCE.switchClass(editor_id+'_advlink','mceButtonSelected');return true}}while((node=node.parentNode));if(any_selection){tinyMCE.switchClass(editor_id+'_advlink','mceButtonNormal');return true}tinyMCE.switchClass(editor_id+'_advlink','mceButtonDisabled');return true}};tinyMCE.addPlugin("advlink",TinyMCE_AdvancedLinkPlugin); \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advlink/editor_plugin_src.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advlink/editor_plugin_src.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,88 @@ +/** + * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $ + * + * @author Moxiecode + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +/* Import plugin specific language pack */ +tinyMCE.importPluginLanguagePack('advlink'); + +var TinyMCE_AdvancedLinkPlugin = { + getInfo : function() { + return { + longname : 'Advanced link', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advlink', + version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion + }; + }, + + initInstance : function(inst) { + inst.addShortcut('ctrl', 'k', 'lang_advlink_desc', 'mceAdvLink'); + }, + + getControlHTML : function(cn) { + switch (cn) { + case "link": + return tinyMCE.getButtonHTML(cn, 'lang_link_desc', '{$themeurl}/images/link.gif', 'mceAdvLink'); + } + + return ""; + }, + + execCommand : function(editor_id, element, command, user_interface, value) { + switch (command) { + case "mceAdvLink": + var anySelection = false; + var inst = tinyMCE.getInstanceById(editor_id); + var focusElm = inst.getFocusElement(); + var selectedText = inst.selection.getSelectedText(); + + if (tinyMCE.selectedElement) + anySelection = (tinyMCE.selectedElement.nodeName.toLowerCase() == "img") || (selectedText && selectedText.length > 0); + + if (anySelection || (focusElm != null && focusElm.nodeName == "A")) { + var template = new Array(); + + template['file'] = '../../plugins/advlink/link.htm'; + template['width'] = 480; + template['height'] = 400; + + // Language specific width and height addons + template['width'] += tinyMCE.getLang('lang_advlink_delta_width', 0); + template['height'] += tinyMCE.getLang('lang_advlink_delta_height', 0); + + tinyMCE.openWindow(template, {editor_id : editor_id, inline : "yes"}); + } + + return true; + } + + return false; + }, + + handleNodeChange : function(editor_id, node, undo_index, undo_levels, visual_aid, any_selection) { + if (node == null) + return; + + do { + if (node.nodeName == "A" && tinyMCE.getAttrib(node, 'href') != "") { + tinyMCE.switchClass(editor_id + '_advlink', 'mceButtonSelected'); + return true; + } + } while ((node = node.parentNode)); + + if (any_selection) { + tinyMCE.switchClass(editor_id + '_advlink', 'mceButtonNormal'); + return true; + } + + tinyMCE.switchClass(editor_id + '_advlink', 'mceButtonDisabled'); + + return true; + } +}; + +tinyMCE.addPlugin("advlink", TinyMCE_AdvancedLinkPlugin); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advlink/jscripts/functions.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advlink/jscripts/functions.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,568 @@ +/* Functions for the advlink plugin popup */ + +var templates = { + "window.open" : "window.open('${url}','${target}','${options}')" +}; + +function preinit() { + // Initialize + tinyMCE.setWindowArg('mce_windowresize', false); + + // Import external list url javascript + var url = tinyMCE.getParam("external_link_list_url"); + if (url != null) { + // Fix relative + if (url.charAt(0) != '/' && url.indexOf('://') == -1) + url = tinyMCE.documentBasePath + "/" + url; + + document.write(''); + } +} + +function changeClass() { + var formObj = document.forms[0]; + formObj.classes.value = getSelectValue(formObj, 'classlist'); +} + +function init() { + tinyMCEPopup.resizeToInnerSize(); + + var formObj = document.forms[0]; + var inst = tinyMCE.getInstanceById(tinyMCE.getWindowArg('editor_id')); + var elm = inst.getFocusElement(); + var action = "insert"; + var html; + + document.getElementById('hrefbrowsercontainer').innerHTML = getBrowserHTML('hrefbrowser','href','file','advlink'); + document.getElementById('popupurlbrowsercontainer').innerHTML = getBrowserHTML('popupurlbrowser','popupurl','file','advlink'); + document.getElementById('linklisthrefcontainer').innerHTML = getLinkListHTML('linklisthref','href'); + document.getElementById('anchorlistcontainer').innerHTML = getAnchorListHTML('anchorlist','href'); + document.getElementById('targetlistcontainer').innerHTML = getTargetListHTML('targetlist','target'); + + // Link list + html = getLinkListHTML('linklisthref','href'); + if (html == "") + document.getElementById("linklisthrefrow").style.display = 'none'; + else + document.getElementById("linklisthrefcontainer").innerHTML = html; + + // Resize some elements + if (isVisible('hrefbrowser')) + document.getElementById('href').style.width = '260px'; + + if (isVisible('popupurlbrowser')) + document.getElementById('popupurl').style.width = '180px'; + + elm = tinyMCE.getParentElement(elm, "a"); + if (elm != null && elm.nodeName == "A") + action = "update"; + + formObj.insert.value = tinyMCE.getLang('lang_' + action, 'Insert', true); + + setPopupControlsDisabled(true); + + if (action == "update") { + var href = tinyMCE.getAttrib(elm, 'href'); + + href = convertURL(href, elm, true); + + // Use mce_href if found + var mceRealHref = tinyMCE.getAttrib(elm, 'mce_href'); + if (mceRealHref != "") { + href = mceRealHref; + + if (tinyMCE.getParam('convert_urls')) + href = convertURL(href, elm, true); + } + + var onclick = tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onclick')); + + // Setup form data + setFormValue('href', href); + setFormValue('title', tinyMCE.getAttrib(elm, 'title')); + setFormValue('id', tinyMCE.getAttrib(elm, 'id')); + setFormValue('style', tinyMCE.serializeStyle(tinyMCE.parseStyle(tinyMCE.getAttrib(elm, "style")))); + setFormValue('rel', tinyMCE.getAttrib(elm, 'rel')); + setFormValue('rev', tinyMCE.getAttrib(elm, 'rev')); + setFormValue('charset', tinyMCE.getAttrib(elm, 'charset')); + setFormValue('hreflang', tinyMCE.getAttrib(elm, 'hreflang')); + setFormValue('dir', tinyMCE.getAttrib(elm, 'dir')); + setFormValue('lang', tinyMCE.getAttrib(elm, 'lang')); + setFormValue('tabindex', tinyMCE.getAttrib(elm, 'tabindex', typeof(elm.tabindex) != "undefined" ? elm.tabindex : "")); + setFormValue('accesskey', tinyMCE.getAttrib(elm, 'accesskey', typeof(elm.accesskey) != "undefined" ? elm.accesskey : "")); + setFormValue('type', tinyMCE.getAttrib(elm, 'type')); + setFormValue('onfocus', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onfocus'))); + setFormValue('onblur', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onblur'))); + setFormValue('onclick', onclick); + setFormValue('ondblclick', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'ondblclick'))); + setFormValue('onmousedown', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onmousedown'))); + setFormValue('onmouseup', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onmouseup'))); + setFormValue('onmouseover', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onmouseover'))); + setFormValue('onmousemove', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onmousemove'))); + setFormValue('onmouseout', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onmouseout'))); + setFormValue('onkeypress', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onkeypress'))); + setFormValue('onkeydown', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onkeydown'))); + setFormValue('onkeyup', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onkeyup'))); + setFormValue('target', tinyMCE.getAttrib(elm, 'target')); + setFormValue('classes', tinyMCE.getAttrib(elm, 'class')); + + // Parse onclick data + if (onclick != null && onclick.indexOf('window.open') != -1) + parseWindowOpen(onclick); + else + parseFunction(onclick); + + // Select by the values + selectByValue(formObj, 'dir', tinyMCE.getAttrib(elm, 'dir')); + selectByValue(formObj, 'rel', tinyMCE.getAttrib(elm, 'rel')); + selectByValue(formObj, 'rev', tinyMCE.getAttrib(elm, 'rev')); + selectByValue(formObj, 'linklisthref', href); + + if (href.charAt(0) == '#') + selectByValue(formObj, 'anchorlist', href); + + addClassesToList('classlist', 'advlink_styles'); + + selectByValue(formObj, 'classlist', tinyMCE.getAttrib(elm, 'class'), true); + selectByValue(formObj, 'targetlist', tinyMCE.getAttrib(elm, 'target'), true); + } else + addClassesToList('classlist', 'advlink_styles'); + + window.focus(); +} + +function setFormValue(name, value) { + document.forms[0].elements[name].value = value; +} + +function convertURL(url, node, on_save) { + return eval("tinyMCEPopup.windowOpener." + tinyMCE.settings['urlconverter_callback'] + "(url, node, on_save);"); +} + +function parseWindowOpen(onclick) { + var formObj = document.forms[0]; + + // Preprocess center code + if (onclick.indexOf('return false;') != -1) { + formObj.popupreturn.checked = true; + onclick = onclick.replace('return false;', ''); + } else + formObj.popupreturn.checked = false; + + var onClickData = parseLink(onclick); + + if (onClickData != null) { + formObj.ispopup.checked = true; + setPopupControlsDisabled(false); + + var onClickWindowOptions = parseOptions(onClickData['options']); + var url = onClickData['url']; + + if (tinyMCE.getParam('convert_urls')) + url = convertURL(url, null, true); + + formObj.popupname.value = onClickData['target']; + formObj.popupurl.value = url; + formObj.popupwidth.value = getOption(onClickWindowOptions, 'width'); + formObj.popupheight.value = getOption(onClickWindowOptions, 'height'); + + formObj.popupleft.value = getOption(onClickWindowOptions, 'left'); + formObj.popuptop.value = getOption(onClickWindowOptions, 'top'); + + if (formObj.popupleft.value.indexOf('screen') != -1) + formObj.popupleft.value = "c"; + + if (formObj.popuptop.value.indexOf('screen') != -1) + formObj.popuptop.value = "c"; + + formObj.popuplocation.checked = getOption(onClickWindowOptions, 'location') == "yes"; + formObj.popupscrollbars.checked = getOption(onClickWindowOptions, 'scrollbars') == "yes"; + formObj.popupmenubar.checked = getOption(onClickWindowOptions, 'menubar') == "yes"; + formObj.popupresizable.checked = getOption(onClickWindowOptions, 'resizable') == "yes"; + formObj.popuptoolbar.checked = getOption(onClickWindowOptions, 'toolbar') == "yes"; + formObj.popupstatus.checked = getOption(onClickWindowOptions, 'status') == "yes"; + formObj.popupdependent.checked = getOption(onClickWindowOptions, 'dependent') == "yes"; + + buildOnClick(); + } +} + +function parseFunction(onclick) { + var formObj = document.forms[0]; + var onClickData = parseLink(onclick); + + // TODO: Add stuff here +} + +function getOption(opts, name) { + return typeof(opts[name]) == "undefined" ? "" : opts[name]; +} + +function setPopupControlsDisabled(state) { + var formObj = document.forms[0]; + + formObj.popupname.disabled = state; + formObj.popupurl.disabled = state; + formObj.popupwidth.disabled = state; + formObj.popupheight.disabled = state; + formObj.popupleft.disabled = state; + formObj.popuptop.disabled = state; + formObj.popuplocation.disabled = state; + formObj.popupscrollbars.disabled = state; + formObj.popupmenubar.disabled = state; + formObj.popupresizable.disabled = state; + formObj.popuptoolbar.disabled = state; + formObj.popupstatus.disabled = state; + formObj.popupreturn.disabled = state; + formObj.popupdependent.disabled = state; + + setBrowserDisabled('popupurlbrowser', state); +} + +function parseLink(link) { + link = link.replace(new RegExp(''', 'g'), "'"); + + var fnName = link.replace(new RegExp("\\s*([A-Za-z0-9\.]*)\\s*\\(.*", "gi"), "$1"); + + // Is function name a template function + var template = templates[fnName]; + if (template) { + // Build regexp + var variableNames = template.match(new RegExp("'?\\$\\{[A-Za-z0-9\.]*\\}'?", "gi")); + var regExp = "\\s*[A-Za-z0-9\.]*\\s*\\("; + var replaceStr = ""; + for (var i=0; i'); + for (var i=0; i'; + html += ''; + + for (var i=0; i' + name + ''; + } + + html += ''; + + return html; +} + +function insertAction() { + var inst = tinyMCE.getInstanceById(tinyMCE.getWindowArg('editor_id')); + var elm = inst.getFocusElement(); + + elm = tinyMCE.getParentElement(elm, "a"); + + tinyMCEPopup.execCommand("mceBeginUndoLevel"); + + // Create new anchor elements + if (elm == null) { + if (tinyMCE.isSafari) + tinyMCEPopup.execCommand("mceInsertContent", false, '' + inst.selection.getSelectedHTML() + ''); + else + tinyMCEPopup.execCommand("createlink", false, "#mce_temp_url#"); + + var elementArray = tinyMCE.getElementsByAttributeValue(inst.getBody(), "a", "href", "#mce_temp_url#"); + for (var i=0; i' + tinyMCELinkList[i][0] + ''; + + html += ''; + + return html; + + // tinyMCE.debug('-- image list start --', html, '-- image list end --'); +} + +function getTargetListHTML(elm_id, target_form_element) { + var targets = tinyMCE.getParam('theme_advanced_link_targets', '').split(';'); + var html = ''; + + html += ''; + + return html; +} + +// While loading +preinit(); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advlink/langs/en.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advlink/langs/en.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,46 @@ +// UK lang variables + +tinyMCE.addToLang('advlink',{ +general_tab : 'General', +popup_tab : 'Popup', +events_tab : 'Events', +advanced_tab : 'Advanced', +general_props : 'General properties', +popup_props : 'Popup properties', +event_props : 'Events', +advanced_props : 'Advanced properties', +popup_opts : 'Options', +anchor_names : 'Anchors', +target_same : 'Open in this window / frame', +target_parent : 'Open in parent window / frame', +target_top : 'Open in top frame (replaces all frames)', +target_blank : 'Open in new window', +popup : 'Javascript popup', +popup_url : 'Popup URL', +popup_name : 'Window name', +popup_return : 'Insert \'return false\'', +popup_scrollbars : 'Show scrollbars', +popup_statusbar : 'Show status bar', +popup_toolbar : 'Show toolbars', +popup_menubar : 'Show menu bar', +popup_location : 'Show location bar', +popup_resizable : 'Make window resizable', +popup_dependent : 'Dependent (Mozilla/Firefox only)', +popup_size : 'Size', +popup_position : 'Position (X/Y)', +id : 'Id', +style: 'Style', +classes : 'Classes', +target_name : 'Target name', +langdir : 'Language direction', +target_langcode : 'Target language', +langcode : 'Language code', +encoding : 'Target character encoding', +mime : 'Target MIME type', +rel : 'Relationship page to target', +rev : 'Relationship target to page', +tabindex : 'Tabindex', +accesskey : 'Accesskey', +ltr : 'Left to right', +rtl : 'Right to left' +}); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advlink/link.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advlink/link.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,337 @@ + + + {$lang_insert_link_title} + + + + + + + + +
+ + +
+
+
+ {$lang_advlink_general_props} + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
 
 
 
 
+ +
+
+
+ + + +
+
+ {$lang_advlink_advanced_props} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+
+ +
+
+ {$lang_advlink_event_props} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/advlink/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/advlink/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +Check the TinyMCE documentation for details on this plugin. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/autosave/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/autosave/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('autosave');var TinyMCE_AutoSavePlugin={getInfo:function(){return{longname:'Auto save',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autosave',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},_beforeUnloadHandler:function(){var n,inst,anyDirty=false,msg=tinyMCE.getLang("lang_autosave_unload_msg");if(tinyMCE.getParam("fullscreen_is_enabled"))return;for(n in tinyMCE.instances){inst=tinyMCE.instances[n];if(!tinyMCE.isInstance(inst))continue;if(inst.isDirty())return msg}return}};window.onbeforeunload=TinyMCE_AutoSavePlugin._beforeUnloadHandler;tinyMCE.addPlugin("autosave",TinyMCE_AutoSavePlugin); \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/autosave/editor_plugin_src.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/autosave/editor_plugin_src.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,46 @@ +/** + * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $ + * + * @author Moxiecode + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +/* Import plugin specific language pack */ +tinyMCE.importPluginLanguagePack('autosave'); + +var TinyMCE_AutoSavePlugin = { + getInfo : function() { + return { + longname : 'Auto save', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autosave', + version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion + }; + }, + + // Private plugin internal methods + + _beforeUnloadHandler : function() { + var n, inst, anyDirty = false, msg = tinyMCE.getLang("lang_autosave_unload_msg"); + + if (tinyMCE.getParam("fullscreen_is_enabled")) + return; + + for (n in tinyMCE.instances) { + inst = tinyMCE.instances[n]; + + if (!tinyMCE.isInstance(inst)) + continue; + + if (inst.isDirty()) + return msg; + } + + return; + } +}; + +window.onbeforeunload = TinyMCE_AutoSavePlugin._beforeUnloadHandler; + +tinyMCE.addPlugin("autosave", TinyMCE_AutoSavePlugin); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/autosave/langs/en.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/autosave/langs/en.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,5 @@ +// EN lang variables + +tinyMCE.addToLang('',{ +autosave_unload_msg : 'The changes you made will be lost if you navigate away from this page.' +}); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/autosave/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/autosave/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +Check the TinyMCE documentation for details on this plugin. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/bbcode/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/bbcode/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +var TinyMCE_BBCodePlugin={getInfo:function(){return{longname:'BBCode Plugin',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/bbcode',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},cleanup:function(type,content){var dialect=tinyMCE.getParam('bbcode_dialect','punbb').toLowerCase();switch(type){case"insert_to_editor":content=this['_'+dialect+'_bbcode2html'](content);break;case"get_from_editor":content=this['_'+dialect+'_html2bbcode'](content);break}return content},_punbb_html2bbcode:function(s){s=tinyMCE.trim(s);function rep(re,str){s=s.replace(re,str)};rep(/(.*?)<\/a>/gi,"[url]$1[/url]");rep(/(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]");rep(/(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]");rep(/(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]");rep(/(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]");rep(/(.*?)<\/font>/gi,"[color=$1]$2[/color]");rep(/(.*?)<\/font>/gi,"$1");rep(//gi,"[img]$1[/img]");rep(/(.*?)<\/span>/gi,"[code]$1[/code]");rep(/(.*?)<\/span>/gi,"[quote]$1[/quote]");rep(/(.*?)<\/strong>/gi,"[code][b]$1[/b][/code]");rep(/(.*?)<\/strong>/gi,"[quote][b]$1[/b][/quote]");rep(/(.*?)<\/em>/gi,"[code][i]$1[/i][/code]");rep(/(.*?)<\/em>/gi,"[quote][i]$1[/i][/quote]");rep(/(.*?)<\/u>/gi,"[code][u]$1[/u][/code]");rep(/(.*?)<\/u>/gi,"[quote][u]$1[/u][/quote]");rep(/<\/(strong|b)>/gi,"[/b]");rep(/<(strong|b)>/gi,"[b]");rep(/<\/(em|i)>/gi,"[/i]");rep(/<(em|i)>/gi,"[i]");rep(/<\/u>/gi,"[/u]");rep(//gi,"[u]");rep(/
/gi,"\n");rep(//gi,"\n");rep(/
/gi,"\n");rep(/

/gi,"");rep(/<\/p>/gi,"\n");rep(/ /gi," ");rep(/"/gi,"\"");rep(/</gi,"<");rep(/>/gi,">");rep(/&/gi,"&");rep(/&undefined;/gi,"'");return s},_punbb_bbcode2html:function(s){s=tinyMCE.trim(s);function rep(re,str){s=s.replace(re,str)};rep(/\n/gi,"
");rep(/\[b\]/gi,"");rep(/\[\/b\]/gi,"");rep(/\[i\]/gi,"");rep(/\[\/i\]/gi,"");rep(/\[u\]/gi,"");rep(/\[\/u\]/gi,"");rep(/\[url\](.*?)\[\/url\]/gi,"
$1");rep(/\[img\](.*?)\[\/img\]/gi,"");rep(/\[color=(.*?)\](.*?)\[\/color\]/gi,"$2");rep(/\[code\](.*?)\[\/code\]/gi,"$1 ");rep(/\[quote.*?\](.*?)\[\/quote\]/gi,"$1 ");return s}};tinyMCE.addPlugin("bbcode",TinyMCE_BBCodePlugin); \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/bbcode/editor_plugin_src.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/bbcode/editor_plugin_src.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,102 @@ +var TinyMCE_BBCodePlugin = { + getInfo : function() { + return { + longname : 'BBCode Plugin', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/bbcode', + version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion + }; + }, + + cleanup : function(type, content) { + var dialect = tinyMCE.getParam('bbcode_dialect', 'punbb').toLowerCase(); + + switch (type) { + case "insert_to_editor": + content = this['_' + dialect + '_bbcode2html'](content); + break; + + case "get_from_editor": + content = this['_' + dialect + '_html2bbcode'](content); + break; + } + + return content; + }, + + // Private methods + + // HTML -> BBCode in PunBB dialect + _punbb_html2bbcode : function(s) { + s = tinyMCE.trim(s); + + function rep(re, str) { + s = s.replace(re, str); + }; + + // example: to [b] + rep(/(.*?)<\/a>/gi,"[url]$1[/url]"); + rep(/(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"); + rep(/(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"); + rep(/(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"); + rep(/(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"); + rep(/(.*?)<\/font>/gi,"[color=$1]$2[/color]"); + rep(/(.*?)<\/font>/gi,"$1"); + rep(//gi,"[img]$1[/img]"); + rep(/(.*?)<\/span>/gi,"[code]$1[/code]"); + rep(/(.*?)<\/span>/gi,"[quote]$1[/quote]"); + rep(/(.*?)<\/strong>/gi,"[code][b]$1[/b][/code]"); + rep(/(.*?)<\/strong>/gi,"[quote][b]$1[/b][/quote]"); + rep(/(.*?)<\/em>/gi,"[code][i]$1[/i][/code]"); + rep(/(.*?)<\/em>/gi,"[quote][i]$1[/i][/quote]"); + rep(/(.*?)<\/u>/gi,"[code][u]$1[/u][/code]"); + rep(/(.*?)<\/u>/gi,"[quote][u]$1[/u][/quote]"); + rep(/<\/(strong|b)>/gi,"[/b]"); + rep(/<(strong|b)>/gi,"[b]"); + rep(/<\/(em|i)>/gi,"[/i]"); + rep(/<(em|i)>/gi,"[i]"); + rep(/<\/u>/gi,"[/u]"); + rep(//gi,"[u]"); + rep(/
/gi,"\n"); + rep(//gi,"\n"); + rep(/
/gi,"\n"); + rep(/

/gi,""); + rep(/<\/p>/gi,"\n"); + rep(/ /gi," "); + rep(/"/gi,"\""); + rep(/</gi,"<"); + rep(/>/gi,">"); + rep(/&/gi,"&"); + rep(/&undefined;/gi,"'"); // quickfix + + return s; + }, + + // BBCode -> HTML from PunBB dialect + _punbb_bbcode2html : function(s) { + s = tinyMCE.trim(s); + + function rep(re, str) { + s = s.replace(re, str); + }; + + // example: [b] to + rep(/\n/gi,"
"); + rep(/\[b\]/gi,""); + rep(/\[\/b\]/gi,""); + rep(/\[i\]/gi,""); + rep(/\[\/i\]/gi,""); + rep(/\[u\]/gi,""); + rep(/\[\/u\]/gi,""); + rep(/\[url\](.*?)\[\/url\]/gi,"
$1"); + rep(/\[img\](.*?)\[\/img\]/gi,""); + rep(/\[color=(.*?)\](.*?)\[\/color\]/gi,"$2"); + rep(/\[code\](.*?)\[\/code\]/gi,"$1 "); + rep(/\[quote.*?\](.*?)\[\/quote\]/gi,"$1 "); + + return s; + } +}; + +tinyMCE.addPlugin("bbcode", TinyMCE_BBCodePlugin); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/cleanup/editor_plugin.js diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/cleanup/editor_plugin_src.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/cleanup/editor_plugin_src.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,10 @@ +/** + * $Id: editor_plugin_src.js 162 2007-01-03 16:16:52Z spocke $ + * + * Experimental plugin for new Cleanup routine, this logic will be moved into the core ones it's stable enougth. + * + * @author Moxiecode + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +/* Dummy file since cleanup is now moved to core */ diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/cleanup/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/cleanup/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +Dummy plugin since cleanup is now moved into core. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/contextmenu/css/contextmenu.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/contextmenu/css/contextmenu.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,74 @@ +.contextMenuIEPopup { + padding: 0; + margin: 0; + border: 0; + overflow: hidden; +} + +.contextMenu { + position: absolute; + cursor: default; + z-index: 1000; + border: 1px solid #D4D0C8; + background-color: #FFFFFF; +} + +.contextMenuItem, .contextMenuItemOver { +} + +.contextMenuSeparator { + width: 100%; + background-color: #D4D0C8; + border: 0; +} + +.contextMenuImage, .contextMenuItemDisabled { + border: 0; +} + +.contextMenuIcon { + background-color: #F0F0EE; +} + +.contextMenuItemOver .contextMenuIcon { + background-color: #B6BDD2; +} + +.contextMenuIcon { + background-color: #F0F0EE; +} + +.contextMenuItemDisabled img { + filter:progid:DXImageTransform.Microsoft.Alpha(opacity=30); + -moz-opacity:0.3; + opacity: 0.3; +} + +.contextMenuText { + font-family: Tahoma, Verdana, Arial, Helvetica; + font-size: 11px; + line-height: 20px; +} + +.contextMenuItemDisabled { + color: #AAAAAA; +} + +.contextMenuText a { + display: block; + line-height: 20px; + width: 100%; + text-decoration: none; + color: black; + font-weight: normal; + margin: 0; + padding: 0; +} + +.contextMenuText a:hover { + background-color: #B6BDD2; + text-decoration: none !important; + font-weight: normal; + margin: 0; + padding: 0; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/contextmenu/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/contextmenu/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +if(!tinyMCE.settings['contextmenu_skip_plugin_css']){tinyMCE.loadCSS(tinyMCE.baseURL+"/plugins/contextmenu/css/contextmenu.css")}var TinyMCE_ContextMenuPlugin={_contextMenu:null,getInfo:function(){return{longname:'Context menus',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/contextmenu',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},initInstance:function(inst){if(tinyMCE.isMSIE5_0&&tinyMCE.isOpera)return;TinyMCE_ContextMenuPlugin._contextMenu=new TinyMCE_ContextMenu({commandhandler:"TinyMCE_ContextMenuPlugin._commandHandler",spacer_image:tinyMCE.baseURL+"/plugins/contextmenu/images/spacer.gif"});tinyMCE.addEvent(inst.getDoc(),"click",TinyMCE_ContextMenuPlugin._hideContextMenu);tinyMCE.addEvent(inst.getDoc(),"keypress",TinyMCE_ContextMenuPlugin._hideContextMenu);tinyMCE.addEvent(inst.getDoc(),"keydown",TinyMCE_ContextMenuPlugin._hideContextMenu);tinyMCE.addEvent(document,"click",TinyMCE_ContextMenuPlugin._hideContextMenu);tinyMCE.addEvent(document,"keypress",TinyMCE_ContextMenuPlugin._hideContextMenu);tinyMCE.addEvent(document,"keydown",TinyMCE_ContextMenuPlugin._hideContextMenu);if(tinyMCE.isGecko){tinyMCE.addEvent(inst.getDoc(),"contextmenu",function(e){TinyMCE_ContextMenuPlugin._showContextMenu(tinyMCE.isMSIE?inst.contentWindow.event:e,inst)})}else tinyMCE.addEvent(inst.getDoc(),"contextmenu",TinyMCE_ContextMenuPlugin._onContextMenu)},_onContextMenu:function(e){var elm=tinyMCE.isMSIE?e.srcElement:e.target;var targetInst,body;if((body=tinyMCE.getParentElement(elm,"body"))!=null){for(var n in tinyMCE.instances){var inst=tinyMCE.instances[n];if(!tinyMCE.isInstance(inst))continue;if(body==inst.getBody()){targetInst=inst;break}}return TinyMCE_ContextMenuPlugin._showContextMenu(tinyMCE.isMSIE?targetInst.contentWindow.event:e,targetInst)}},_showContextMenu:function(e,inst){function getAttrib(elm,name){return elm.getAttribute(name)?elm.getAttribute(name):""}var x,y,elm,contextMenu;var pos=tinyMCE.getAbsPosition(inst.iframeElement);x=tinyMCE.isMSIE?e.screenX:pos.absLeft+(e.pageX-inst.getBody().scrollLeft);y=tinyMCE.isMSIE?e.screenY:pos.absTop+(e.pageY-inst.getBody().scrollTop);elm=tinyMCE.isMSIE?e.srcElement:e.target;contextMenu=this._contextMenu;contextMenu.inst=inst;window.setTimeout(function(){var theme=tinyMCE.getParam("theme");contextMenu.clearAll();var sel=inst.selection.getSelectedText().length!=0||elm.nodeName=="IMG";contextMenu.addItem(tinyMCE.baseURL+"/themes/"+theme+"/images/cut.gif","$lang_cut_desc","Cut","",!sel);contextMenu.addItem(tinyMCE.baseURL+"/themes/"+theme+"/images/copy.gif","$lang_copy_desc","Copy","",!sel);contextMenu.addItem(tinyMCE.baseURL+"/themes/"+theme+"/images/paste.gif","$lang_paste_desc","Paste","",false);if(sel||(elm?(elm.nodeName=='A'&&tinyMCE.getAttrib(elm,'name')=='')||(elm.nodeName=='IMG'):false)){contextMenu.addSeparator();contextMenu.addItem(tinyMCE.baseURL+"/themes/advanced/images/link.gif","$lang_link_desc",inst.hasPlugin("advlink")?"mceAdvLink":"mceLink");contextMenu.addItem(tinyMCE.baseURL+"/themes/advanced/images/unlink.gif","$lang_unlink_desc","unlink","",(elm?(elm.nodeName!='A')&&(elm.nodeName!='IMG'):true))}elm=tinyMCE.getParentElement(elm,"img,table,td"+(inst.hasPlugin("advhr")?',hr':''));if(elm){switch(elm.nodeName){case"IMG":contextMenu.addSeparator();if(tinyMCE.hasPlugin('flash')&&tinyMCE.getAttrib(elm,'class').indexOf('mceItemFlash')!=-1)contextMenu.addItem(tinyMCE.baseURL+"/plugins/flash/images/flash.gif","$lang_flash_props","mceFlash");else if(tinyMCE.hasPlugin('media')&&/mceItem(Flash|ShockWave|WindowsMedia|QuickTime|RealMedia)/.test(tinyMCE.getAttrib(elm,'class')))contextMenu.addItem(tinyMCE.baseURL+"/plugins/flash/images/flash.gif","$lang_media_title","mceMedia");else contextMenu.addItem(tinyMCE.baseURL+"/themes/"+theme+"/images/image.gif","$lang_image_props_desc",inst.hasPlugin("advimage")?"mceAdvImage":"mceImage");break;case"HR":contextMenu.addSeparator();contextMenu.addItem(tinyMCE.baseURL+"/plugins/advhr/images/advhr.gif","$lang_insert_advhr_desc","mceAdvancedHr");break;case"TABLE":case"TD":if(inst.hasPlugin("table")){var colspan=(elm.nodeName=="TABLE")?"":getAttrib(elm,"colspan");var rowspan=(elm.nodeName=="TABLE")?"":getAttrib(elm,"rowspan");colspan=colspan==""?"1":colspan;rowspan=rowspan==""?"1":rowspan;contextMenu.addSeparator();contextMenu.addItem(tinyMCE.baseURL+"/themes/"+theme+"/images/cut.gif","$lang_table_cut_row_desc","mceTableCutRow");contextMenu.addItem(tinyMCE.baseURL+"/themes/"+theme+"/images/copy.gif","$lang_table_copy_row_desc","mceTableCopyRow");contextMenu.addItem(tinyMCE.baseURL+"/themes/"+theme+"/images/paste.gif","$lang_table_paste_row_before_desc","mceTablePasteRowBefore","",inst.tableRowClipboard==null);contextMenu.addItem(tinyMCE.baseURL+"/themes/"+theme+"/images/paste.gif","$lang_table_paste_row_after_desc","mceTablePasteRowAfter","",inst.tableRowClipboard==null);contextMenu.addSeparator();contextMenu.addItem(tinyMCE.baseURL+"/plugins/table/images/table.gif","$lang_table_desc","mceInsertTable","insert");contextMenu.addItem(tinyMCE.baseURL+"/plugins/table/images/table.gif","$lang_table_props_desc","mceInsertTable");contextMenu.addItem(tinyMCE.baseURL+"/plugins/table/images/table_cell_props.gif","$lang_table_cell_desc","mceTableCellProps");contextMenu.addItem(tinyMCE.baseURL+"/plugins/table/images/table_delete.gif","$lang_table_del","mceTableDelete");contextMenu.addSeparator();contextMenu.addItem(tinyMCE.baseURL+"/plugins/table/images/table_row_props.gif","$lang_table_row_desc","mceTableRowProps");contextMenu.addItem(tinyMCE.baseURL+"/plugins/table/images/table_insert_row_before.gif","$lang_table_row_before_desc","mceTableInsertRowBefore");contextMenu.addItem(tinyMCE.baseURL+"/plugins/table/images/table_insert_row_after.gif","$lang_table_row_after_desc","mceTableInsertRowAfter");contextMenu.addItem(tinyMCE.baseURL+"/plugins/table/images/table_delete_row.gif","$lang_table_delete_row_desc","mceTableDeleteRow");contextMenu.addSeparator();contextMenu.addItem(tinyMCE.baseURL+"/plugins/table/images/table_insert_col_before.gif","$lang_table_col_before_desc","mceTableInsertColBefore");contextMenu.addItem(tinyMCE.baseURL+"/plugins/table/images/table_insert_col_after.gif","$lang_table_col_after_desc","mceTableInsertColAfter");contextMenu.addItem(tinyMCE.baseURL+"/plugins/table/images/table_delete_col.gif","$lang_table_delete_col_desc","mceTableDeleteCol");contextMenu.addSeparator();contextMenu.addItem(tinyMCE.baseURL+"/plugins/table/images/table_split_cells.gif","$lang_table_split_cells_desc","mceTableSplitCells","",(colspan=="1"&&rowspan=="1"));contextMenu.addItem(tinyMCE.baseURL+"/plugins/table/images/table_merge_cells.gif","$lang_table_merge_cells_desc","mceTableMergeCells","",false)}break}}else{if(inst.hasPlugin("table")){contextMenu.addSeparator();contextMenu.addItem(tinyMCE.baseURL+"/plugins/table/images/table.gif","$lang_table_desc","mceInsertTable","insert")}}contextMenu.show(x,y)},10);tinyMCE.cancelEvent(e);return false},_hideContextMenu:function(){if(TinyMCE_ContextMenuPlugin._contextMenu)TinyMCE_ContextMenuPlugin._contextMenu.hide()},_commandHandler:function(command,value){var cm=TinyMCE_ContextMenuPlugin._contextMenu;cm.hide();var ui=false;if(command=="mceInsertTable"||command=="mceTableCellProps"||command=="mceTableRowProps"||command=="mceTableMergeCells")ui=true;if(command=="Paste")value=null;if(tinyMCE.getParam("dialog_type")=="modal"&&tinyMCE.isMSIE){window.setTimeout(function(){cm.inst.execCommand(command,ui,value)},100)}else cm.inst.execCommand(command,ui,value)}};tinyMCE.addPlugin("contextmenu",TinyMCE_ContextMenuPlugin);function TinyMCE_ContextMenu(settings){var doc,self=this;function defParam(key,def_val){settings[key]=typeof(settings[key])!="undefined"?settings[key]:def_val}this.isMSIE=(navigator.appName=="Microsoft Internet Explorer");this.contextMenuDiv=document.createElement("div");this.contextMenuDiv.className="contextMenu";this.contextMenuDiv.setAttribute("class","contextMenu");this.contextMenuDiv.style.display="none";this.contextMenuDiv.style.position='absolute';this.contextMenuDiv.style.zindex=1000;this.contextMenuDiv.style.left='0';this.contextMenuDiv.style.top='0';this.contextMenuDiv.unselectable="on";document.body.appendChild(this.contextMenuDiv);defParam("commandhandler","");defParam("spacer_image","images/spacer.gif");this.items=new Array();this.settings=settings;this.html="";if(tinyMCE.isMSIE&&!tinyMCE.isMSIE5_0&&!tinyMCE.isOpera){this.pop=window.createPopup();doc=this.pop.document;doc.open();doc.write('');doc.close()}};TinyMCE_ContextMenu.prototype={clearAll:function(){this.html="";this.contextMenuDiv.innerHTML=""},addSeparator:function(){this.html+=''},addItem:function(icon,title,command,value,disabled){if(title.charAt(0)=='$')title=tinyMCE.getLang(title.substring(1));var onMouseDown='';var html='';if(tinyMCE.isMSIE&&!tinyMCE.isMSIE5_0)onMouseDown='contextMenu.execCommand(\''+command+'\', \''+value+'\');return false;';else onMouseDown=this.settings['commandhandler']+'(\''+command+'\', \''+value+'\');return false;';if(icon=="")icon=this.settings['spacer_image'];if(!disabled)html+='';else html+='';html+='';html+='

';html+=' ';html+=title;html+=' ';html+='
';html+='';this.html+=html},show:function(x,y){var vp,width,height,yo;if(this.html=="")return;var html='';html+='';html+=this.html;html+='
';this.contextMenuDiv.innerHTML=html;this.contextMenuDiv.style.display="block";width=this.contextMenuDiv.offsetWidth;height=this.contextMenuDiv.offsetHeight;this.contextMenuDiv.style.display="none";if(tinyMCE.isMSIE&&!tinyMCE.isMSIE5_0&&!tinyMCE.isOpera){this.pop.document.body.innerHTML='
'+html+"
";this.pop.document.tinyMCE=tinyMCE;this.pop.document.contextMenu=this;this.pop.show(x,y,width,height)}else{vp=this.getViewPort();yo=tinyMCE.isMSIE5_0?document.body.scrollTop:self.pageYOffset;this.contextMenuDiv.style.left=(x>vp.left+vp.width-width?vp.left+vp.width-width:x)+'px';this.contextMenuDiv.style.top=(y>vp.top+vp.height-height?vp.top+vp.height-height:y)+'px';this.contextMenuDiv.style.display="block"}},getViewPort:function(){return{left:self.pageXOffset||self.document.documentElement.scrollLeft||self.document.body.scrollLeft,top:self.pageYOffset||self.document.documentElement.scrollTop||self.document.body.scrollTop,width:document.documentElement.offsetWidth||document.body.offsetWidth,height:self.innerHeight||document.documentElement.clientHeight||document.body.clientHeight}},hide:function(){if(tinyMCE.isMSIE&&!tinyMCE.isMSIE5_0&&!tinyMCE.isOpera)this.pop.hide();else this.contextMenuDiv.style.display="none"},execCommand:function(command,value){eval(this.settings['commandhandler']+"(command, value);")}}; \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/contextmenu/editor_plugin_src.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/contextmenu/editor_plugin_src.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,357 @@ +/** + * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $ + * + * @author Moxiecode + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +/* Import plugin specific language pack */ +if (!tinyMCE.settings['contextmenu_skip_plugin_css']) { + tinyMCE.loadCSS(tinyMCE.baseURL + "/plugins/contextmenu/css/contextmenu.css"); +} + +var TinyMCE_ContextMenuPlugin = { + // Private fields + _contextMenu : null, + + getInfo : function() { + return { + longname : 'Context menus', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/contextmenu', + version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion + }; + }, + + initInstance : function(inst) { + // Is not working on MSIE 5.0 or Opera no contextmenu event + if (tinyMCE.isMSIE5_0 && tinyMCE.isOpera) + return; + + TinyMCE_ContextMenuPlugin._contextMenu = new TinyMCE_ContextMenu({ + commandhandler : "TinyMCE_ContextMenuPlugin._commandHandler", + spacer_image : tinyMCE.baseURL + "/plugins/contextmenu/images/spacer.gif" + }); + + // Add hide event handles + tinyMCE.addEvent(inst.getDoc(), "click", TinyMCE_ContextMenuPlugin._hideContextMenu); + tinyMCE.addEvent(inst.getDoc(), "keypress", TinyMCE_ContextMenuPlugin._hideContextMenu); + tinyMCE.addEvent(inst.getDoc(), "keydown", TinyMCE_ContextMenuPlugin._hideContextMenu); + tinyMCE.addEvent(document, "click", TinyMCE_ContextMenuPlugin._hideContextMenu); + tinyMCE.addEvent(document, "keypress", TinyMCE_ContextMenuPlugin._hideContextMenu); + tinyMCE.addEvent(document, "keydown", TinyMCE_ContextMenuPlugin._hideContextMenu); + + // Attach contextmenu event + if (tinyMCE.isGecko) { + tinyMCE.addEvent(inst.getDoc(), "contextmenu", function(e) {TinyMCE_ContextMenuPlugin._showContextMenu(tinyMCE.isMSIE ? inst.contentWindow.event : e, inst);}); + } else + tinyMCE.addEvent(inst.getDoc(), "contextmenu", TinyMCE_ContextMenuPlugin._onContextMenu); + }, + + // Private plugin internal methods + + _onContextMenu : function(e) { + var elm = tinyMCE.isMSIE ? e.srcElement : e.target; + var targetInst, body; + + // Find instance + if ((body = tinyMCE.getParentElement(elm, "body")) != null) { + for (var n in tinyMCE.instances) { + var inst = tinyMCE.instances[n]; + if (!tinyMCE.isInstance(inst)) + continue; + + if (body == inst.getBody()) { + targetInst = inst; + break; + } + } + + return TinyMCE_ContextMenuPlugin._showContextMenu(tinyMCE.isMSIE ? targetInst.contentWindow.event : e, targetInst); + } + }, + + _showContextMenu : function(e, inst) { + function getAttrib(elm, name) { + return elm.getAttribute(name) ? elm.getAttribute(name) : ""; + } + + var x, y, elm, contextMenu; + var pos = tinyMCE.getAbsPosition(inst.iframeElement); + + x = tinyMCE.isMSIE ? e.screenX : pos.absLeft + (e.pageX - inst.getBody().scrollLeft); + y = tinyMCE.isMSIE ? e.screenY : pos.absTop + (e.pageY - inst.getBody().scrollTop); + elm = tinyMCE.isMSIE ? e.srcElement : e.target; + + contextMenu = this._contextMenu; + contextMenu.inst = inst; + + // Mozilla needs some time + window.setTimeout(function () { + var theme = tinyMCE.getParam("theme"); + + contextMenu.clearAll(); + var sel = inst.selection.getSelectedText().length != 0 || elm.nodeName == "IMG"; + + // Default items + contextMenu.addItem(tinyMCE.baseURL + "/themes/" + theme + "/images/cut.gif", "$lang_cut_desc", "Cut", "", !sel); + contextMenu.addItem(tinyMCE.baseURL + "/themes/" + theme + "/images/copy.gif", "$lang_copy_desc", "Copy", "", !sel); + contextMenu.addItem(tinyMCE.baseURL + "/themes/" + theme + "/images/paste.gif", "$lang_paste_desc", "Paste", "", false); + + if (sel || (elm ? (elm.nodeName == 'A' && tinyMCE.getAttrib(elm, 'name') == '') || (elm.nodeName == 'IMG') : false)) { + contextMenu.addSeparator(); + contextMenu.addItem(tinyMCE.baseURL + "/themes/advanced/images/link.gif", "$lang_link_desc", inst.hasPlugin("advlink") ? "mceAdvLink" : "mceLink"); + contextMenu.addItem(tinyMCE.baseURL + "/themes/advanced/images/unlink.gif", "$lang_unlink_desc", "unlink", "", (elm ? (elm.nodeName != 'A') && (elm.nodeName != 'IMG') : true)); + } + + // Get element + elm = tinyMCE.getParentElement(elm, "img,table,td" + (inst.hasPlugin("advhr") ? ',hr' : '')); + if (elm) { + switch (elm.nodeName) { + case "IMG": + contextMenu.addSeparator(); + + // If flash + if (tinyMCE.hasPlugin('flash') && tinyMCE.getAttrib(elm, 'class').indexOf('mceItemFlash') != -1) + contextMenu.addItem(tinyMCE.baseURL + "/plugins/flash/images/flash.gif", "$lang_flash_props", "mceFlash"); + else if (tinyMCE.hasPlugin('media') && /mceItem(Flash|ShockWave|WindowsMedia|QuickTime|RealMedia)/.test(tinyMCE.getAttrib(elm, 'class'))) + contextMenu.addItem(tinyMCE.baseURL + "/plugins/flash/images/flash.gif", "$lang_media_title", "mceMedia"); + else + contextMenu.addItem(tinyMCE.baseURL + "/themes/" + theme + "/images/image.gif", "$lang_image_props_desc", inst.hasPlugin("advimage") ? "mceAdvImage" : "mceImage"); + break; + + case "HR": + contextMenu.addSeparator(); + contextMenu.addItem(tinyMCE.baseURL + "/plugins/advhr/images/advhr.gif", "$lang_insert_advhr_desc", "mceAdvancedHr"); + break; + + case "TABLE": + case "TD": + // Is table plugin loaded + if (inst.hasPlugin("table")) { + var colspan = (elm.nodeName == "TABLE") ? "" : getAttrib(elm, "colspan"); + var rowspan = (elm.nodeName == "TABLE") ? "" : getAttrib(elm, "rowspan"); + + colspan = colspan == "" ? "1" : colspan; + rowspan = rowspan == "" ? "1" : rowspan; + + contextMenu.addSeparator(); + contextMenu.addItem(tinyMCE.baseURL + "/themes/" + theme + "/images/cut.gif", "$lang_table_cut_row_desc", "mceTableCutRow"); + contextMenu.addItem(tinyMCE.baseURL + "/themes/" + theme + "/images/copy.gif", "$lang_table_copy_row_desc", "mceTableCopyRow"); + contextMenu.addItem(tinyMCE.baseURL + "/themes/" + theme + "/images/paste.gif", "$lang_table_paste_row_before_desc", "mceTablePasteRowBefore", "", inst.tableRowClipboard == null); + contextMenu.addItem(tinyMCE.baseURL + "/themes/" + theme + "/images/paste.gif", "$lang_table_paste_row_after_desc", "mceTablePasteRowAfter", "", inst.tableRowClipboard == null); + + /* contextMenu.addItem(tinyMCE.baseURL + "/themes/" + theme + "/images/justifyleft.gif", "$lang_justifyleft_desc", "JustifyLeft", "", false); + contextMenu.addItem(tinyMCE.baseURL + "/themes/" + theme + "/images/justifycenter.gif", "$lang_justifycenter_desc", "JustifyCenter", "", false); + contextMenu.addItem(tinyMCE.baseURL + "/themes/" + theme + "/images/justifyright.gif", "$lang_justifyright_desc", "JustifyRight", "", false); + contextMenu.addItem(tinyMCE.baseURL + "/themes/" + theme + "/images/justifyfull.gif", "$lang_justifyfull_desc", "JustifyFull", "", false);*/ + contextMenu.addSeparator(); + contextMenu.addItem(tinyMCE.baseURL + "/plugins/table/images/table.gif", "$lang_table_desc", "mceInsertTable", "insert"); + contextMenu.addItem(tinyMCE.baseURL + "/plugins/table/images/table.gif", "$lang_table_props_desc", "mceInsertTable"); + contextMenu.addItem(tinyMCE.baseURL + "/plugins/table/images/table_cell_props.gif", "$lang_table_cell_desc", "mceTableCellProps"); + contextMenu.addItem(tinyMCE.baseURL + "/plugins/table/images/table_delete.gif", "$lang_table_del", "mceTableDelete"); + contextMenu.addSeparator(); + contextMenu.addItem(tinyMCE.baseURL + "/plugins/table/images/table_row_props.gif", "$lang_table_row_desc", "mceTableRowProps"); + contextMenu.addItem(tinyMCE.baseURL + "/plugins/table/images/table_insert_row_before.gif", "$lang_table_row_before_desc", "mceTableInsertRowBefore"); + contextMenu.addItem(tinyMCE.baseURL + "/plugins/table/images/table_insert_row_after.gif", "$lang_table_row_after_desc", "mceTableInsertRowAfter"); + contextMenu.addItem(tinyMCE.baseURL + "/plugins/table/images/table_delete_row.gif", "$lang_table_delete_row_desc", "mceTableDeleteRow"); + contextMenu.addSeparator(); + contextMenu.addItem(tinyMCE.baseURL + "/plugins/table/images/table_insert_col_before.gif", "$lang_table_col_before_desc", "mceTableInsertColBefore"); + contextMenu.addItem(tinyMCE.baseURL + "/plugins/table/images/table_insert_col_after.gif", "$lang_table_col_after_desc", "mceTableInsertColAfter"); + contextMenu.addItem(tinyMCE.baseURL + "/plugins/table/images/table_delete_col.gif", "$lang_table_delete_col_desc", "mceTableDeleteCol"); + contextMenu.addSeparator(); + contextMenu.addItem(tinyMCE.baseURL + "/plugins/table/images/table_split_cells.gif", "$lang_table_split_cells_desc", "mceTableSplitCells", "", (colspan == "1" && rowspan == "1")); + contextMenu.addItem(tinyMCE.baseURL + "/plugins/table/images/table_merge_cells.gif", "$lang_table_merge_cells_desc", "mceTableMergeCells", "", false); + } + break; + } + } else { + // Add table specific + if (inst.hasPlugin("table")) { + contextMenu.addSeparator(); + contextMenu.addItem(tinyMCE.baseURL + "/plugins/table/images/table.gif", "$lang_table_desc", "mceInsertTable", "insert"); + } + } + + contextMenu.show(x, y); + }, 10); + + // Cancel default handeling + tinyMCE.cancelEvent(e); + return false; + }, + + _hideContextMenu : function() { + if (TinyMCE_ContextMenuPlugin._contextMenu) + TinyMCE_ContextMenuPlugin._contextMenu.hide(); + }, + + _commandHandler : function(command, value) { + var cm = TinyMCE_ContextMenuPlugin._contextMenu; + + cm.hide(); + + // UI must be true on these + var ui = false; + if (command == "mceInsertTable" || command == "mceTableCellProps" || command == "mceTableRowProps" || command == "mceTableMergeCells") + ui = true; + + if (command == "Paste") + value = null; + + if (tinyMCE.getParam("dialog_type") == "modal" && tinyMCE.isMSIE) { + // Cell properties will generate access denied error is this isn't done?! + window.setTimeout(function() { + cm.inst.execCommand(command, ui, value); + }, 100); + } else + cm.inst.execCommand(command, ui, value); + } +}; + +tinyMCE.addPlugin("contextmenu", TinyMCE_ContextMenuPlugin); + +// Context menu class + +function TinyMCE_ContextMenu(settings) { + var doc, self = this; + + // Default value function + function defParam(key, def_val) { + settings[key] = typeof(settings[key]) != "undefined" ? settings[key] : def_val; + } + + this.isMSIE = (navigator.appName == "Microsoft Internet Explorer"); + + // Setup contextmenu div + this.contextMenuDiv = document.createElement("div"); + this.contextMenuDiv.className = "contextMenu"; + this.contextMenuDiv.setAttribute("class", "contextMenu"); + this.contextMenuDiv.style.display = "none"; + this.contextMenuDiv.style.position = 'absolute'; + this.contextMenuDiv.style.zindex = 1000; + this.contextMenuDiv.style.left = '0'; + this.contextMenuDiv.style.top = '0'; + this.contextMenuDiv.unselectable = "on"; + + document.body.appendChild(this.contextMenuDiv); + + // Setup default values + defParam("commandhandler", ""); + defParam("spacer_image", "images/spacer.gif"); + + this.items = new Array(); + this.settings = settings; + this.html = ""; + + // IE Popup + if (tinyMCE.isMSIE && !tinyMCE.isMSIE5_0 && !tinyMCE.isOpera) { + this.pop = window.createPopup(); + doc = this.pop.document; + doc.open(); + doc.write(''); + doc.close(); + } +}; + +TinyMCE_ContextMenu.prototype = { + clearAll : function() { + this.html = ""; + this.contextMenuDiv.innerHTML = ""; + }, + + addSeparator : function() { + this.html += ''; + }, + + addItem : function(icon, title, command, value, disabled) { + if (title.charAt(0) == '$') + title = tinyMCE.getLang(title.substring(1)); + + var onMouseDown = ''; + var html = ''; + + if (tinyMCE.isMSIE && !tinyMCE.isMSIE5_0) + onMouseDown = 'contextMenu.execCommand(\'' + command + '\', \'' + value + '\');return false;'; + else + onMouseDown = this.settings['commandhandler'] + '(\'' + command + '\', \'' + value + '\');return false;'; + + if (icon == "") + icon = this.settings['spacer_image']; + + if (!disabled) + html += ''; + else + html += ''; + + html += ''; + html += ''; + html += ''; + + // Add to main + this.html += html; + }, + + show : function(x, y) { + var vp, width, height, yo; + + if (this.html == "") + return; + + var html = ''; + + html += ''; + html += this.html; + html += '
'; + + this.contextMenuDiv.innerHTML = html; + + // Get dimensions + this.contextMenuDiv.style.display = "block"; + width = this.contextMenuDiv.offsetWidth; + height = this.contextMenuDiv.offsetHeight; + this.contextMenuDiv.style.display = "none"; + + if (tinyMCE.isMSIE && !tinyMCE.isMSIE5_0 && !tinyMCE.isOpera) { + // Setup popup and show + this.pop.document.body.innerHTML = '
' + html + "
"; + this.pop.document.tinyMCE = tinyMCE; + this.pop.document.contextMenu = this; + this.pop.show(x, y, width, height); + } else { + vp = this.getViewPort(); + yo = tinyMCE.isMSIE5_0 ? document.body.scrollTop : self.pageYOffset; + this.contextMenuDiv.style.left = (x > vp.left + vp.width - width ? vp.left + vp.width - width : x) + 'px'; + this.contextMenuDiv.style.top = (y > vp.top + vp.height - height ? vp.top + vp.height - height : y) + 'px'; + this.contextMenuDiv.style.display = "block"; + } + }, + + getViewPort : function() { + return { + left : self.pageXOffset || self.document.documentElement.scrollLeft || self.document.body.scrollLeft, + top: self.pageYOffset || self.document.documentElement.scrollTop || self.document.body.scrollTop, + width : document.documentElement.offsetWidth || document.body.offsetWidth, + height : self.innerHeight || document.documentElement.clientHeight || document.body.clientHeight + }; + }, + + hide : function() { + if (tinyMCE.isMSIE && !tinyMCE.isMSIE5_0 && !tinyMCE.isOpera) + this.pop.hide(); + else + this.contextMenuDiv.style.display = "none"; + }, + + execCommand : function(command, value) { + eval(this.settings['commandhandler'] + "(command, value);"); + } +}; diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/contextmenu/images/spacer.gif Binary file includes/clientside/tinymce/plugins/contextmenu/images/spacer.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/contextmenu/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/contextmenu/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +Check the TinyMCE documentation for details on this plugin. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/devkit/css/devkit.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/devkit/css/devkit.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,117 @@ +body { + overflow: hidden; +} + +h1 { + font-size: 14px; + margin: 0 0 10px 0; +} + +h2 { + font-size: 12px; + margin: 3px 0 3px 0; +} + +h3 { + font-size: 11px; + margin: 3px 0 3px 0; +} + +#log { + font-family: Verdana; + border: 1px solid gray; + width: 100%; height: 240px; + overflow: scroll; + white-space: nowrap; +} + +#log span { + display: block; +} + +#log span.msg { + float: left; +} + +#log span.time { + float: left; +} + +#log br { + clear: both; +} + +#logfilter { + width: 350px; +} + +#logenabled { + border: 0; +} + +#settings_panel span, #info_panel span, #content_panel span, #command_states_panel span, #undo_redo_panel span { + display: block; + margin: 5px 0 5px 0; +} + +div.data { + width: 100%; height: 240px; + overflow: scroll; + border: 1px solid gray; +} + +#misc_panel div.data { + height: 270px; +} + +.data input { + width: 265px; + border: 0; +} + +.data h2 { + margin-left: 5px; +} + +.data h3 { + margin-left: 7px; +} + +.data div { + margin-left: 7px; +} + +.data table { + margin: 0 0 15px 15px; +} + +.data p { + margin: 0; padding: 0; + margin-top: 5px; + margin-left: 5px; +} + +table, td { + border: 1px solid gray; + border-collapse: collapse; +} + +#flip { + position: absolute; + left: 295; top: 384px; +} + +.bspec { + color: gray; +} + +.dep { + color: #880000; +} + +.col1 { + width: 265px; +} + +div.undodata { +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/devkit/css/devkit_ui.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/devkit/css/devkit_ui.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,15 @@ +#devkit { + position: absolute; + top: -385px; right: 0; + width: 640px; height: 390px; + border: 1px solid black; + z-index: 10000; +} + +.devkitup { + top: -385px !important; +} + +.devkitdown { + top: 0 !important; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/devkit/devkit.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/devkit/devkit.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,140 @@ + + + {$lang_devkit_title} + + + + + + + + + +
+

{$lang_devkit_title}

+ + + +
+
+
+ {$lang_devkit_log_tab} + +
+ +
+ +
+ +
+ + + +
+ +
+
+ + +
+
+ +
+
+ {$lang_devkit_info_tab} + + [{$lang_devkit_refresh}] + +
+

{$lang_devkit_info_help}

+
+
+
+ +
+
+ {$lang_devkit_settings_tab} + + [{$lang_devkit_refresh}] + +
+

{$lang_devkit_settings_help}

+
+
+
+ +
+
+ {$lang_devkit_content_tab} + + [{$lang_devkit_refresh}] + +
+

{$lang_devkit_content_help}

+
+
+
+ +
+
+ {$lang_devkit_command_states_tab} + + [{$lang_devkit_refresh}] + +
+

{$lang_devkit_command_states_help}

+
+
+
+ +
+
+ {$lang_devkit_undo_redo_tab} + + [{$lang_devkit_refresh}] + +
+

{$lang_devkit_undo_redo_help}

+
+ + +
+
+ +
+
+ {$lang_devkit_misc_tab} + +
+

{$lang_devkit_misc_help}

+
+

Selection: [Store selection] [Restore selection]

+
+
Insert custom HTML content
+
+ +
+
Eval JS:
+
+ +
+
+
+
+
+ +
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/devkit/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/devkit/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('devkit');var TinyMCE_DevKitPlugin={_logFilter:'\\[(importCSS|execCommand|execInstanceCommand|debug)\\]',_logPadding:'',_startTime:null,_benchMark:false,_winLoaded:false,_isDebugEvents:false,getInfo:function(){return{longname:'Development Kit',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/devkit',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},initInstance:function(inst){this._setup()},_setup:function(){if(this._loaded)return;this._loaded=true;document.___TinyMCE=tinyMCE;this._logFilter=tinyMCE.getParam('devkit_log_filter',this._logFilter);this._benchMark=tinyMCE.getParam('devkit_bench_mark',false);var ifr=document.createElement('iframe');ifr.setAttribute("id","devkit");ifr.setAttribute("frameBorder","0");ifr.setAttribute("src",tinyMCE.baseURL+'/plugins/devkit/devkit.htm');document.body.appendChild(ifr);tinyMCE.importCSS(document,tinyMCE.baseURL+'/plugins/devkit/css/devkit_ui.css')},_start:function(){this._logPadding+='\u00a0';return new Date().getTime()},_end:function(st){if(this._logPadding.length>0)this._logPadding=this._logPadding.substring(0,this._logPadding.length-1);if(this._benchMark)this._log("benchmark","Execution time: "+(new Date().getTime()-st))},_log:function(t){var m,a,i,e=document.getElementById('devkit'),now=new Date().getTime();if(!this._startTime)this._startTime=now;m=(this._logPadding.length>1?this._logPadding:'')+'['+(now-this._startTime)+'] ['+t+'] ';a=this._log.arguments;for(i=1;i1)m+=', ';m+=a[i]}if(!new RegExp(this._logFilter,'gi').test(m)){if(this._logPadding.length>0)this._logPadding=this._logPadding.substring(0,this._logPadding.length-1);return}if(!this._winLoaded)tinyMCE.log[tinyMCE.log.length]=m;else e.contentWindow.debug(m)},_debugEvents:function(s){var i,ld,inst,n,ev=['CheckboxStateChange','DOMAttrModified','DOMMenuItemActive','DOMMenuItemInactive','DOMMouseScroll','DOMNodeInserted','DOMNodeRemoved','RadioStateChange','blur','broadcast','change','click','close','command','commandupdate','contextmenu','dblclick','dragdrop','dragenter','dragexit','draggesture','dragover','focus','input','keydown','keypress','keyup','load','mousedown','mouseout','mouseover','mouseup','overflow','overflowchanged','popuphidden','popuphiding','popupshowing','popupshown','select','syncfrompreference','synctopreference','underflow','unload','abort','activate','afterprint','afterupdate','beforeactivate','beforecopy','beforecut','beforedeactivate','beforeeditfocus','beforepaste','beforeprint','beforeunload','beforeupdate','bounce','cellchange','controlselect','copy','cut','dataavailable','datasetchanged','datasetcomplete','deactivate','dragend','dragleave','dragstart','drop','error','errorupdate','filterchange','finish','focusin','focusout','help','layoutcomplete','losecapture','mouseenter','mouseleave','mousewheel','move','moveend','movestart','paste','propertychange','readystatechange','reset','resize','resizeend','resizestart','rowenter','rowexit','rowsdelete','rowsinserted','scroll','selectionchange','selectstart','start','stop','submit'];if(TinyMCE_DevKitPlugin._isDebugEvents==s)return;TinyMCE_DevKitPlugin._isDebugEvents=s;for(n in tinyMCE.instances){inst=tinyMCE.instances[n];if(!tinyMCE.isInstance(inst)||inst.getDoc()==ld)continue;ld=inst.getDoc();for(i=0;i0?',':'')+s(o[i]);return v+']'}v='{';for(i in o)v+=typeof o[i]!='function'?(v.length>1?',"':'"')+i+'":'+s(o[i]):'';return v+'}'}return''+o}};tinyMCE.__debug=tinyMCE.debug;tinyMCE.debug=function(){var a,i,m='',now=new Date().getTime(),start=TinyMCE_DevKitPlugin._startTime;if(!start)TinyMCE_DevKitPlugin._startTime=start=now;a=this.debug.arguments;for(i=0;i0)m+=', ';m+=a[i]}TinyMCE_DevKitPlugin._log('debug',m)};tinyMCE.dump=function(o){tinyMCE.debug(TinyMCE_DevKitPlugin._serialize(o))};tinyMCE.sleep=function(t){var s=new Date().getTime(),b;while(new Date().getTime()-s 0) + this._logPadding = this._logPadding.substring(0, this._logPadding.length - 1); + + if (this._benchMark) + this._log("benchmark", "Execution time: " + (new Date().getTime() - st)); + }, + + _log : function(t) { + var m, a, i, e = document.getElementById('devkit'), now = new Date().getTime(); + + if (!this._startTime) + this._startTime = now; + + m = (this._logPadding.length > 1 ? this._logPadding : '') + '[' + (now - this._startTime) + '] [' + t + '] '; + + a = this._log.arguments; + for (i=1; i 1) + m += ', '; + + m += a[i]; + } + + if (!new RegExp(this._logFilter, 'gi').test(m)) { + if (this._logPadding.length > 0) + this._logPadding = this._logPadding.substring(0, this._logPadding.length - 1); + + return; + } + + if (!this._winLoaded) + tinyMCE.log[tinyMCE.log.length] = m; + else + e.contentWindow.debug(m); + }, + + _debugEvents : function(s) { + var i, ld, inst, n, ev = ['CheckboxStateChange','DOMAttrModified','DOMMenuItemActive', + 'DOMMenuItemInactive','DOMMouseScroll','DOMNodeInserted','DOMNodeRemoved', + 'RadioStateChange','blur','broadcast','change','click','close','command', + 'commandupdate','contextmenu','dblclick','dragdrop','dragenter','dragexit', + 'draggesture','dragover','focus','input','keydown','keypress','keyup','load', + 'mousedown','mouseout','mouseover','mouseup','overflow','overflowchanged','popuphidden', + 'popuphiding','popupshowing','popupshown','select','syncfrompreference','synctopreference', + 'underflow','unload','abort','activate','afterprint','afterupdate','beforeactivate', + 'beforecopy','beforecut','beforedeactivate','beforeeditfocus','beforepaste','beforeprint', + 'beforeunload','beforeupdate','bounce','cellchange','controlselect','copy','cut', + 'dataavailable','datasetchanged','datasetcomplete','deactivate','dragend','dragleave', + 'dragstart','drop','error','errorupdate','filterchange','finish','focusin','focusout', + 'help','layoutcomplete','losecapture','mouseenter','mouseleave','mousewheel', + 'move','moveend','movestart','paste','propertychange','readystatechange','reset','resize', + 'resizeend','resizestart','rowenter','rowexit','rowsdelete','rowsinserted','scroll', + 'selectionchange','selectstart','start','stop','submit']; + // mousemove + + if (TinyMCE_DevKitPlugin._isDebugEvents == s) + return; + + TinyMCE_DevKitPlugin._isDebugEvents = s; + + for (n in tinyMCE.instances) { + inst = tinyMCE.instances[n]; + + if (!tinyMCE.isInstance(inst) || inst.getDoc() == ld) + continue; + + ld = inst.getDoc(); + + for (i=0; i 0 ? ',' : '') + s(o[i]); + + return v + ']'; + } + + v = '{'; + + for (i in o) + v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : ''; + + return v + '}'; + } + + return '' + o; + } +}; + +// Patch and piggy back functions +tinyMCE.__debug = tinyMCE.debug; +tinyMCE.debug = function() { + var a, i, m = '', now = new Date().getTime(), start = TinyMCE_DevKitPlugin._startTime; + + if (!start) + TinyMCE_DevKitPlugin._startTime = start = now; + + a = this.debug.arguments; + for (i=0; i 0) + m += ', '; + + m += a[i]; + } + + TinyMCE_DevKitPlugin._log('debug', m); +}; + +tinyMCE.dump = function(o) { + tinyMCE.debug(TinyMCE_DevKitPlugin._serialize(o)); +}; + +tinyMCE.sleep = function(t) { + var s = new Date().getTime(), b; + + while (new Date().getTime() - s < t) b=1; +}; + +tinyMCE.__execCommand = tinyMCE.execCommand; +tinyMCE.execCommand = function(command, user_interface, value) { + var r, st, dk = TinyMCE_DevKitPlugin; + + st = dk._start(); + dk._log('execCommand', command, user_interface, value); + r = tinyMCE.__execCommand(command, user_interface, value); + dk._end(st); + + return r; +}; + +tinyMCE.__execInstanceCommand = tinyMCE.execInstanceCommand; +tinyMCE.execInstanceCommand = function(editor_id, command, user_interface, value, focus) { + var r, st, dk = TinyMCE_DevKitPlugin; + + st = dk._start(); + dk._log('execInstanceCommand', editor_id, command, user_interface, value); + r = tinyMCE.__execInstanceCommand(editor_id, command, user_interface, value); + dk._end(st); + + return r; +}; + +TinyMCE_Engine.prototype.__handleEvent = TinyMCE_Engine.prototype.handleEvent; +TinyMCE_Engine.prototype.handleEvent = function(e) { + var r, st, dk = TinyMCE_DevKitPlugin; + + st = dk._start(); + dk._log('handleEvent', e.type); + r = tinyMCE.__handleEvent(e); + dk._end(st); + + return r; +}; + +tinyMCE.__importCSS = tinyMCE.importCSS; +tinyMCE.importCSS = function(doc, css) { + var r, st, dk = TinyMCE_DevKitPlugin; + + st = dk._start(); + dk._log('importCSS', doc, css); + r = tinyMCE.__importCSS(doc, css); + dk._end(st); + + return r; +}; + +tinyMCE.__triggerNodeChange = tinyMCE.triggerNodeChange; +tinyMCE.triggerNodeChange = function(focus, setup_content) { + var r, st, dk = TinyMCE_DevKitPlugin; + + st = dk._start(); + dk._log('triggerNodeChange', focus, setup_content); + r = tinyMCE.__triggerNodeChange(focus, setup_content); + dk._end(st); + + return r; +}; + +tinyMCE.__dispatchCallback = tinyMCE.dispatchCallback; +tinyMCE.dispatchCallback = function(i, p, n) { + var r, st, dk = TinyMCE_DevKitPlugin; + + st = dk._start(); + dk._log('dispatchCallback', i, p, n); + r = tinyMCE.__dispatchCallback(i, p, n); + dk._end(st); + + return r; +}; + +tinyMCE.__executeCallback = tinyMCE.executeCallback; +tinyMCE.executeCallback = function(i, p, n) { + var r, st, dk = TinyMCE_DevKitPlugin; + + st = dk._start(); + dk._log('executeCallback', i, p, n); + r = tinyMCE.__executeCallback(i, p, n); + dk._end(st); + + return r; +}; + +tinyMCE.__execCommandCallback = tinyMCE.execCommandCallback; +tinyMCE.execCommandCallback = function(i, p, n) { + var r, st, dk = TinyMCE_DevKitPlugin; + + st = dk._start(); + dk._log('execCommandCallback', i, p, n); + r = tinyMCE.__execCommandCallback(i, p, n); + dk._end(st); + + return r; +}; + +tinyMCE.addPlugin("devkit", TinyMCE_DevKitPlugin); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/devkit/images/flip_down.gif Binary file includes/clientside/tinymce/plugins/devkit/images/flip_down.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/devkit/images/flip_up.gif Binary file includes/clientside/tinymce/plugins/devkit/images/flip_up.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/devkit/jscripts/devkit.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/devkit/jscripts/devkit.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,331 @@ +var devkit = parent.tinyMCE.plugins['devkit'], logEnabled = true, flip = false, book = null; + +function init() { + var log, i, f = document.forms[0]; + + devkit._winLoaded = true; + + log = tinyMCE.log; + + for (i=0; i'; + h += ''; + + h += addRenderInfo('editorId', inst.editorId); + h += addRenderInfo('visualAid', inst.visualAid); + h += addRenderInfo('foreColor', inst.foreColor); + h += addRenderInfo('backColor', inst.backColor); + h += addRenderInfo('formTargetElementId', inst.formTargetElementId); + h += addRenderInfo('formElement', inst.formElement ? inst.formElement.nodeName : null); + h += addRenderInfo('oldTargetElement', inst.oldTargetElement ? inst.oldTargetElement.nodeName : null); + h += addRenderInfo('linkElement', inst.linkElement ? inst.linkElement.nodeName : null, 'dep'); + h += addRenderInfo('imgElement', inst.imgElement ? inst.imgElement.nodeName : null, 'dep'); + h += addRenderInfo('selectedNode', inst.selectedNode ? inst.selectedNode.nodeName : null, 'dep'); + h += addRenderInfo('targetElement', inst.targetElement ? inst.targetElement.nodeName : null); + h += addRenderInfo('getBody().nodeName', inst.getBody() ? inst.getBody().nodeName : null); + h += addRenderInfo('getBody().getAttribute("id")', inst.getBody() ? inst.getBody().getAttribute("id") : null); + h += addRenderInfo('getDoc().location', inst.getDoc() ? inst.getDoc().location : null); + h += addRenderInfo('startContent', inst.startContent); + h += addRenderInfo('isHidden()', inst.isHidden()); + h += addRenderInfo('isDirty()', inst.isDirty()); + h += addRenderInfo('undoRedo.undoLevels.length', inst.undoRedo.undoLevels.length); + h += addRenderInfo('undoRedo.undoIndex', inst.undoRedo.undoIndex); + h += addRenderInfo('selection.getSelectedHTML()', inst.selection.getSelectedHTML()); + h += addRenderInfo('selection.isCollapsed()', inst.selection.isCollapsed() || 'false'); + h += addRenderInfo('selection.getSelectedText()', inst.selection.getSelectedText()); + h += addRenderInfo('selection.getFocusElement().nodeName', inst.selection.getFocusElement().nodeName); + h += addRenderInfo('selection.getFocusElement().outerHTML', tinyMCE.getOuterHTML(inst.selection.getFocusElement())); + + if ((tinyMCE.isGecko || tinyMCE.isOpera) && sel && rng) { + h += addRenderInfo('selection.getSel().anchorNode.nodeName', sel.anchorNode ? sel.anchorNode.nodeName : null, 'bspec'); + h += addRenderInfo('selection.getSel().anchorOffset', sel.anchorOffset, 'bspec'); + h += addRenderInfo('selection.getSel().focusNode.nodeName', sel.focusNode ? sel.focusNode.nodeName : null, 'bspec'); + h += addRenderInfo('selection.getSel().focusOffset', sel.focusOffset, 'bspec'); + h += addRenderInfo('selection.getRng().startContainer.nodeName', rng.startContainer ? rng.startContainer.nodeName : null, 'bspec'); + h += addRenderInfo('selection.getRng().startOffset', rng.startOffset, 'bspec'); + h += addRenderInfo('selection.getRng().endContainer.nodeName', rng.endContainer ? rng.endContainer.nodeName : null, 'bspec'); + h += addRenderInfo('selection.getRng().endOffset', rng.endOffset, 'bspec'); + } + + if (typeof(rng.item) != 'undefined' || typeof(rng.htmlText) != 'undefined') { + if (!rng.item) { + h += addRenderInfo('selection.getSel().type', sel.type, 'bspec'); + h += addRenderInfo('selection.getRng().htmlText', rng.htmlText, 'bspec'); + h += addRenderInfo('selection.getRng().text', rng.text, 'bspec'); + } else + h += addRenderInfo('selection.getRng().item(0).nodeName', rng.item(0).nodeName, 'bspec'); + } + + h += '
'; + } + + h += '

Fields marked in gray is not cross browser and should be used with care.

'; + h += '

Fields marked red are marked deprecated and will be removed in the future.


'; + + se.innerHTML = h; +} + +function addRenderInfo(n, v, c) { + return '' + n + ''; +} + +function renderSettings() { + var se = document.getElementById('settings'), n, sn, inst, h = '', v; + + for (n in tinyMCE.instances) { + inst = tinyMCE.instances[n]; + + if (!tinyMCE.isInstance(inst)) + continue; + + h += '

Instance id: ' + inst.editorId + '

'; + h += ''; + + for (sn in inst.settings) { + v = inst.settings[sn]; + + h += ''; + } + + h += '
' + tinyMCE.xmlEncode(sn) + '
'; + } + + se.innerHTML = h; +} + +function renderContent() { + var se = document.getElementById('content'), n, inst, h = ''; + + for (n in tinyMCE.instances) { + inst = tinyMCE.instances[n]; + + if (!tinyMCE.isInstance(inst)) + continue; + + h += '

Instance id: ' + inst.editorId + '

'; + + h += '

Start content - inst.startContent:

'; + h += '
' + tinyMCE.xmlEncode(inst.startContent) + '
'; + + h += '

Raw content - inst.getBody().innerHTML or inst.getHTML(true):

'; + h += '
' + tinyMCE.xmlEncode(inst.getHTML(true)) + '
'; + + h += '

Cleaned content - inst.getHTML():

'; + h += '
' + tinyMCE.xmlEncode(inst.getHTML()) + '
'; + + if (inst.serializedHTML) { + h += '

Serialized HTML content - inst.serializedHTML:

'; + h += '
' + tinyMCE.xmlEncode(inst.serializedHTML) + '
'; + } + } + + se.innerHTML = h; +} + +function renderCommandStates() { + var se = document.getElementById('command_states'), n, inst, h = '', v, ex; + var cmds = new Array('2D-Position','AbsolutePosition','BackColor','BlockDirLTR','BlockDirRTL','Bold','BrowseMode','Copy','CreateBookmark','CreateLink','Cut','Delete','DirLTR','DirRTL','EditMode','enableInlineTableEditing','enableObjectResizing','FontName','FontSize','ForeColor','FormatBlock','Indent','InsertButton','InsertFieldset','InsertHorizontalRule','InsertIFrame','InsertImage','InsertInputButton','InsertInputCheckbox','InsertInputFileUpload','InsertInputHidden','InsertInputImage','InsertInputPassword','InsertInputRadio','InsertInputReset','InsertInputSubmit','InsertInputText','InsertMarquee','InsertOrderedList','InsertParagraph','InsertSelectDropdown','InsertSelectListbox','InsertTextArea','InsertUnorderedList','Italic','JustifyCenter','JustifyFull','JustifyLeft','JustifyNone','JustifyRight','LiveResize','MultipleSelection','Open','Outdent','OverWrite','Paste','PlayImage','Redo','Refresh','RemoveFormat','SaveAs','SelectAll','SizeToControl','SizeToControlHeight','SizeToControlWidth','Stop','StopImage','StrikeThrough','styleWithCSS','Subscript','Superscript','UnBookmark','Underline','Undo','Unlink','Unselect'), i; + + for (n in tinyMCE.instances) { + inst = tinyMCE.instances[n]; + + if (!tinyMCE.isInstance(inst)) + continue; + + h += '

Instance id: ' + inst.editorId + '

'; + h += ''; + + for (i=0; i'; + } + + h += '
'; + } + + se.innerHTML = h; +} + +function renderUndoRedo() { + var se = document.getElementById('undo_redo'), inst, n, h = '', i, le, id, d, ur; + var f = document.forms[0]; + + if (tinyMCE.undoLevels) { + le = tinyMCE.undoLevels; + + h += '

Global undo/redo

'; + h += ''; + h += ''; + h += ''; + h += '
undoLevels.length' + le.length + '
undoIndex' + tinyMCE.undoIndex + '
'; + + for (i=0; i'; + } + + for (n in tinyMCE.instances) { + inst = tinyMCE.instances[n]; + + if (!tinyMCE.isInstance(inst)) + continue; + + ur = inst.undoRedo; + le = ur.undoLevels; + + h += '

Instance id: ' + inst.editorId + '

'; + h += ''; + h += ''; + h += ''; + h += ''; + h += ''; + h += '
undoLevels.length' + le.length + '
undoIndex' + ur.undoIndex + '
typingUndoIndex' + ur.typingUndoIndex + '
undoRedo' + ur.undoRedo + '
'; + + for (i=0; i'; + h += '
' + tinyMCE.xmlEncode(le[i].content) + '
'; + + if (i > 0 && f.undo_diff.checked) { + d = diff_main(i > 0 ? le[i-1].content.replace(/[\r\n]+/g, '') : null, le[i].content.replace(/[\r\n]+/g, ''), false); + diff_cleanup_semantic(d); + h += '

Diff ' + (i-1) + ',' + i + '

' + diff_prettyhtml(d) + '
'; + } + } + } + + se.innerHTML = h; +} + +function clearLog() { + document.getElementById('log').innerHTML = ''; + devkit._startTime = null; +} + +function cancelAction() { + parent.document.getElementById('devkit').style.display = 'none'; +} + +function toggleDebugEvents(s) { + devkit._debugEvents(s); +} + +function storeSelection() { + book = tinyMCE.selectedInstance.selection.getBookmark(); + + return false; +} + +function restoreSelection() { + tinyMCE.selectedInstance.selection.moveToBookmark(book); + + return false; +} \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/devkit/jscripts/diff.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/devkit/jscripts/diff.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1192 @@ +// Diff_Match_Patch v1.3 +// Computes the difference between two texts to create a patch. +// Applies the patch onto another text, allowing for errors. +// Copyright (C) 2006 Neil Fraser +// http://neil.fraser.name/software/diff_match_patch/ + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License (www.gnu.org) for more details. + + +// Constants. +// Redefine these in your program to override the defaults. + +// Number of seconds to map a diff before giving up. (0 for infinity) +var DIFF_TIMEOUT = 1.0; +// Cost of an empty edit operation in terms of edit characters. +var DIFF_EDIT_COST = 4; +// Tweak the relative importance (0.0 = accuracy, 1.0 = proximity) +var MATCH_BALANCE = 0.5; +// At what point is no match declared (0.0 = perfection, 1.0 = very loose) +var MATCH_THRESHOLD = 0.5; +// The min and max cutoffs used when computing text lengths. +var MATCH_MINLENGTH = 100; +var MATCH_MAXLENGTH = 1000; +// Chunk size for context length. +var PATCH_MARGIN = 4; + + + ////////////////////////////////////////////////////////////////////// + // Diff // +////////////////////////////////////////////////////////////////////// + +// The data structure representing a diff is an array of tuples: +// [[-1, "Hello"], [1, "Goodbye"], [0, " world."]] +// which means: delete "Hello", add "Goodbye" and keep " world." + + +function diff_main(text1, text2, checklines) { + // Find the differences between two texts. Return an array of changes. + // If checklines is present and false, then don't run a line-level diff first to identify the changed areas. + // Check for equality (speedup) + if (text1 == text2) + return [[0, text1]]; + + if (typeof checklines == 'undefined') + checklines = true; + + var a; + // Trim off common prefix (speedup) + a = diff_prefix(text1, text2); + text1 = a[0]; + text2 = a[1]; + var commonprefix = a[2]; + + // Trim off common suffix (speedup) + a = diff_suffix(text1, text2); + text1 = a[0]; + text2 = a[1]; + var commonsuffix = a[2]; + + var diff, i; + var longtext = text1.length > text2.length ? text1 : text2; + var shorttext = text1.length > text2.length ? text2 : text1; + + if (!text1) { // Just add some text (speedup) + diff = [[1, text2]]; + } else if (!text2) { // Just delete some text (speedup) + diff = [[-1, text1]]; + } else if ((i = longtext.indexOf(shorttext)) != -1) { + // Shorter text is inside the longer text (speedup) + diff = [[1, longtext.substring(0, i)], [0, shorttext], [1, longtext.substring(i+shorttext.length)]]; + // Swap insertions for deletions if diff is reversed. + if (text1.length > text2.length) + diff[0][0] = diff[2][0] = -1; + } else { + longtext = shorttext = null; // Garbage collect + // Check to see if the problem can be split in two. + var hm = diff_halfmatch(text1, text2); + if (hm) { + // A half-match was found, sort out the return data. + var text1_a = hm[0]; + var text1_b = hm[1]; + var text2_a = hm[2]; + var text2_b = hm[3]; + var mid_common = hm[4]; + // Send both pairs off for separate processing. + var diff_a = diff_main(text1_a, text2_a, checklines); + var diff_b = diff_main(text1_b, text2_b, checklines); + // Merge the results. + diff = diff_a.concat([[0, mid_common]], diff_b); + } else { + // Perform a real diff. + if (checklines && text1.length + text2.length < 250) + checklines = false; // Too trivial for the overhead. + if (checklines) { + // Scan the text on a line-by-line basis first. + a = diff_lines2chars(text1, text2); + text1 = a[0]; + text2 = a[1]; + var linearray = a[2]; + } + diff = diff_map(text1, text2); + if (!diff) // No acceptable result. + diff = [[-1, text1], [1, text2]]; + if (checklines) { + diff_chars2lines(diff, linearray); // Convert the diff back to original text. + diff_cleanup_semantic(diff); // Eliminate freak matches (e.g. blank lines) + + // Rediff any replacement blocks, this time on character-by-character basis. + diff.push([0, '']); // Add a dummy entry at the end. + var pointer = 0; + var count_delete = 0; + var count_insert = 0; + var text_delete = ''; + var text_insert = ''; + while(pointer < diff.length) { + if (diff[pointer][0] == 1) { + count_insert++; + text_insert += diff[pointer][1]; + } else if (diff[pointer][0] == -1) { + count_delete++; + text_delete += diff[pointer][1]; + } else { // Upon reaching an equality, check for prior redundancies. + if (count_delete >= 1 && count_insert >= 1) { + // Delete the offending records and add the merged ones. + a = diff_main(text_delete, text_insert, false); + diff.splice(pointer - count_delete - count_insert, count_delete + count_insert); + pointer = pointer - count_delete - count_insert; + for (i=a.length-1; i>=0; i--) + diff.splice(pointer, 0, a[i]); + pointer = pointer + a.length; + } + count_insert = 0; + count_delete = 0; + text_delete = ''; + text_insert = ''; + } + pointer++; + } + diff.pop(); // Remove the dummy entry at the end. + + } + } + } + + if (commonprefix) + diff.unshift([0, commonprefix]); + if (commonsuffix) + diff.push([0, commonsuffix]); + diff_cleanup_merge(diff); + return diff; +} + + +function diff_lines2chars(text1, text2) { + // Split text into an array of strings. + // Reduce the texts to a string of hashes where each character represents one line. + var linearray = new Array(); // linearray[4] == "Hello\n" + var linehash = new Object(); // linehash["Hello\n"] == 4 + + // "\x00" is a valid JavaScript character, but the Venkman debugger doesn't like it (bug 335098) + // So we'll insert a junk entry to avoid generating a null character. + linearray.push(''); + + function diff_lines2chars_munge(text) { + // My first ever closure! + var i, line; + var chars = ''; + while (text) { + i = text.indexOf('\n'); + if (i == -1) + i = text.length; + line = text.substring(0, i+1); + text = text.substring(i+1); + if (linehash.hasOwnProperty ? linehash.hasOwnProperty(line) : (linehash[line] !== undefined)) { + chars += String.fromCharCode(linehash[line]); + } else { + linearray.push(line); + linehash[line] = linearray.length - 1; + chars += String.fromCharCode(linearray.length - 1); + } + } + return chars; + } + + var chars1 = diff_lines2chars_munge(text1); + var chars2 = diff_lines2chars_munge(text2); + return [chars1, chars2, linearray]; +} + + +function diff_chars2lines(diff, linearray) { + // Rehydrate the text in a diff from a string of line hashes to real lines of text. + var chars, text; + for (var x=0; x 0 && now.getTime() > ms_end) // Timeout reached + return null; + + // Walk the front path one step. + v_map1[d] = new Object(); + for (var k=-d; k<=d; k+=2) { + if (k == -d || k != d && v1[k-1] < v1[k+1]) + x = v1[k+1]; + else + x = v1[k-1]+1; + y = x - k; + footstep = x+","+y; + if (front && (hasOwnProperty ? footsteps.hasOwnProperty(footstep) : (footsteps[footstep] !== undefined))) + done = true; + if (!front) + footsteps[footstep] = d; + while (!done && x < text1.length && y < text2.length && text1.charAt(x) == text2.charAt(y)) { + x++; y++; + footstep = x+","+y; + if (front && (hasOwnProperty ? footsteps.hasOwnProperty(footstep) : (footsteps[footstep] !== undefined))) + done = true; + if (!front) + footsteps[footstep] = d; + } + v1[k] = x; + v_map1[d][x+","+y] = true; + if (done) { + // Front path ran over reverse path. + v_map2 = v_map2.slice(0, footsteps[footstep]+1); + var a = diff_path1(v_map1, text1.substring(0, x), text2.substring(0, y)); + return a.concat(diff_path2(v_map2, text1.substring(x), text2.substring(y))); + } + } + + // Walk the reverse path one step. + v_map2[d] = new Object(); + for (var k=-d; k<=d; k+=2) { + if (k == -d || k != d && v2[k-1] < v2[k+1]) + x = v2[k+1]; + else + x = v2[k-1]+1; + y = x - k; + footstep = (text1.length-x)+","+(text2.length-y); + if (!front && (hasOwnProperty ? footsteps.hasOwnProperty(footstep) : (footsteps[footstep] !== undefined))) + done = true; + if (front) + footsteps[footstep] = d; + while (!done && x < text1.length && y < text2.length && text1.charAt(text1.length-x-1) == text2.charAt(text2.length-y-1)) { + x++; y++; + footstep = (text1.length-x)+","+(text2.length-y); + if (!front && (hasOwnProperty ? footsteps.hasOwnProperty(footstep) : (footsteps[footstep] !== undefined))) + done = true; + if (front) + footsteps[footstep] = d; + } + v2[k] = x; + v_map2[d][x+","+y] = true; + if (done) { + // Reverse path ran over front path. + v_map1 = v_map1.slice(0, footsteps[footstep]+1); + var a = diff_path1(v_map1, text1.substring(0, text1.length-x), text2.substring(0, text2.length-y)); + return a.concat(diff_path2(v_map2, text1.substring(text1.length-x), text2.substring(text2.length-y))); + } + } + } + // Number of diffs equals number of characters, no commonality at all. + return null; +} + + +function diff_path1(v_map, text1, text2) { + // Work from the middle back to the start to determine the path. + var path = []; + var x = text1.length; + var y = text2.length; + var last_op = null; + for (var d=v_map.length-2; d>=0; d--) { + while(1) { + if (v_map[d].hasOwnProperty ? v_map[d].hasOwnProperty((x-1)+","+y) : (v_map[d][(x-1)+","+y] !== undefined)) { + x--; + if (last_op === -1) + path[0][1] = text1.charAt(x) + path[0][1]; + else + path.unshift([-1, text1.charAt(x)]); + last_op = -1; + break; + } else if (v_map[d].hasOwnProperty ? v_map[d].hasOwnProperty(x+","+(y-1)) : (v_map[d][x+","+(y-1)] !== undefined)) { + y--; + if (last_op === 1) + path[0][1] = text2.charAt(y) + path[0][1]; + else + path.unshift([1, text2.charAt(y)]); + last_op = 1; + break; + } else { + x--; + y--; + //if (text1.charAt(x) != text2.charAt(y)) + // return alert("No diagonal. Can't happen. (diff_path1)"); + if (last_op === 0) + path[0][1] = text1.charAt(x) + path[0][1]; + else + path.unshift([0, text1.charAt(x)]); + last_op = 0; + } + } + } + return path; +} + + +function diff_path2(v_map, text1, text2) { + // Work from the middle back to the end to determine the path. + var path = []; + var x = text1.length; + var y = text2.length; + var last_op = null; + for (var d=v_map.length-2; d>=0; d--) { + while(1) { + if (v_map[d].hasOwnProperty ? v_map[d].hasOwnProperty((x-1)+","+y) : (v_map[d][(x-1)+","+y] !== undefined)) { + x--; + if (last_op === -1) + path[path.length-1][1] += text1.charAt(text1.length-x-1); + else + path.push([-1, text1.charAt(text1.length-x-1)]); + last_op = -1; + break; + } else if (v_map[d].hasOwnProperty ? v_map[d].hasOwnProperty(x+","+(y-1)) : (v_map[d][x+","+(y-1)] !== undefined)) { + y--; + if (last_op === 1) + path[path.length-1][1] += text2.charAt(text2.length-y-1); + else + path.push([1, text2.charAt(text2.length-y-1)]); + last_op = 1; + break; + } else { + x--; + y--; + //if (text1.charAt(text1.length-x-1) != text2.charAt(text2.length-y-1)) + // return alert("No diagonal. Can't happen. (diff_path2)"); + if (last_op === 0) + path[path.length-1][1] += text1.charAt(text1.length-x-1); + else + path.push([0, text1.charAt(text1.length-x-1)]); + last_op = 0; + } + } + } + return path; +} + + +function diff_prefix(text1, text2) { + // Trim off common prefix + var pointermin = 0; + var pointermax = Math.min(text1.length, text2.length); + var pointermid = pointermax; + while(pointermin < pointermid) { + if (text1.substring(0, pointermid) == text2.substring(0, pointermid)) + pointermin = pointermid; + else + pointermax = pointermid; + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + var commonprefix = text1.substring(0, pointermid); + text1 = text1.substring(pointermid); + text2 = text2.substring(pointermid); + return [text1, text2, commonprefix]; +} + + +function diff_suffix(text1, text2) { + // Trim off common suffix + var pointermin = 0; + var pointermax = Math.min(text1.length, text2.length); + var pointermid = pointermax; + while(pointermin < pointermid) { + if (text1.substring(text1.length-pointermid) == text2.substring(text2.length-pointermid)) + pointermin = pointermid; + else + pointermax = pointermid; + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + var commonsuffix = text1.substring(text1.length-pointermid); + text1 = text1.substring(0, text1.length-pointermid); + text2 = text2.substring(0, text2.length-pointermid); + return [text1, text2, commonsuffix]; +} + + +function diff_halfmatch(text1, text2) { + // Do the two texts share a substring which is at least half the length of the longer text? + var longtext = text1.length > text2.length ? text1 : text2; + var shorttext = text1.length > text2.length ? text2 : text1; + if (longtext.length < 10 || shorttext.length < 1) + return null; // Pointless. + + function diff_halfmatch_i(longtext, shorttext, i) { + // Start with a 1/4 length substring at position i as a seed. + var seed = longtext.substring(i, i+Math.floor(longtext.length/4)); + var j = -1; + var best_common = ''; + var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b; + while ((j = shorttext.indexOf(seed, j+1)) != -1) { + var my_prefix = diff_prefix(longtext.substring(i), shorttext.substring(j)); + var my_suffix = diff_suffix(longtext.substring(0, i), shorttext.substring(0, j)); + if (best_common.length < (my_suffix[2] + my_prefix[2]).length) { + best_common = my_suffix[2] + my_prefix[2]; + best_longtext_a = my_suffix[0]; + best_longtext_b = my_prefix[0]; + best_shorttext_a = my_suffix[1]; + best_shorttext_b = my_prefix[1]; + } + } + if (best_common.length >= longtext.length/2) + return [best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b, best_common]; + else + return null; + } + + // First check if the second quarter is the seed for a half-match. + var hm1 = diff_halfmatch_i(longtext, shorttext, Math.ceil(longtext.length/4)); + // Check again based on the third quarter. + var hm2 = diff_halfmatch_i(longtext, shorttext, Math.ceil(longtext.length/2)); + var hm; + if (!hm1 && !hm2) + return null; + else if (!hm2) + hm = hm1; + else if (!hm1) + hm = hm2; + else // Both matched. Select the longest. + hm = hm1[4].length > hm2[4].length ? hm1 : hm2; + + // A half-match was found, sort out the return data. + if (text1.length > text2.length) { + var text1_a = hm[0]; + var text1_b = hm[1]; + var text2_a = hm[2]; + var text2_b = hm[3]; + } else { + var text2_a = hm[0]; + var text2_b = hm[1]; + var text1_a = hm[2]; + var text1_b = hm[3]; + } + var mid_common = hm[4]; + return [text1_a, text1_b, text2_a, text2_b, mid_common]; +} + + +function diff_cleanup_semantic(diff) { + // Reduce the number of edits by eliminating semantically trivial equalities. + var changes = false; + var equalities = []; // Stack of indices where equalities are found. + var lastequality = null; // Always equal to equalities[equalities.length-1][1] + var pointer = 0; // Index of current position. + var length_changes1 = 0; // Number of characters that changed prior to the equality. + var length_changes2 = 0; // Number of characters that changed after the equality. + while (pointer < diff.length) { + if (diff[pointer][0] == 0) { // equality found + equalities.push(pointer); + length_changes1 = length_changes2; + length_changes2 = 0; + lastequality = diff[pointer][1]; + } else { // an insertion or deletion + length_changes2 += diff[pointer][1].length; + if (lastequality != null && (lastequality.length <= length_changes1) && (lastequality.length <= length_changes2)) { + //alert("Splitting: '"+lastequality+"'"); + diff.splice(equalities[equalities.length-1], 0, [-1, lastequality]); // Duplicate record + diff[equalities[equalities.length-1]+1][0] = 1; // Change second copy to insert. + equalities.pop(); // Throw away the equality we just deleted; + equalities.pop(); // Throw away the previous equality; + pointer = equalities.length ? equalities[equalities.length-1] : -1; + length_changes1 = 0; // Reset the counters. + length_changes2 = 0; + lastequality = null; + changes = true; + } + } + pointer++; + } + + if (changes) + diff_cleanup_merge(diff); +} + + +function diff_cleanup_efficiency(diff) { + // Reduce the number of edits by eliminating operationally trivial equalities. + var changes = false; + var equalities = []; // Stack of indices where equalities are found. + var lastequality = ''; // Always equal to equalities[equalities.length-1][1] + var pointer = 0; // Index of current position. + var pre_ins = false; // Is there an insertion operation before the last equality. + var pre_del = false; // Is there an deletion operation before the last equality. + var post_ins = false; // Is there an insertion operation after the last equality. + var post_del = false; // Is there an deletion operation after the last equality. + while (pointer < diff.length) { + if (diff[pointer][0] == 0) { // equality found + if (diff[pointer][1].length < DIFF_EDIT_COST && (post_ins || post_del)) { + // Candidate found. + equalities.push(pointer); + pre_ins = post_ins; + pre_del = post_del; + lastequality = diff[pointer][1]; + } else { + // Not a candidate, and can never become one. + equalities = []; + lastequality = ''; + } + post_ins = post_del = false; + } else { // an insertion or deletion + if (diff[pointer][0] == -1) + post_del = true; + else + post_ins = true; + // Five types to be split: + // ABXYCD + // AXCD + // ABXC + // AXCD + // ABXC + if (lastequality && ((pre_ins && pre_del && post_ins && post_del) || ((lastequality.length < DIFF_EDIT_COST/2) && (pre_ins + pre_del + post_ins + post_del) == 3))) { + //alert("Splitting: '"+lastequality+"'"); + diff.splice(equalities[equalities.length-1], 0, [-1, lastequality]); // Duplicate record + diff[equalities[equalities.length-1]+1][0] = 1; // Change second copy to insert. + equalities.pop(); // Throw away the equality we just deleted; + lastequality = ''; + if (pre_ins && pre_del) { + // No changes made which could affect previous entry, keep going. + post_ins = post_del = true; + equalities = []; + } else { + equalities.pop(); // Throw away the previous equality; + pointer = equalities.length ? equalities[equalities.length-1] : -1; + post_ins = post_del = false; + } + changes = true; + } + } + pointer++; + } + + if (changes) + diff_cleanup_merge(diff); +} + + +function diff_cleanup_merge(diff) { + // Reorder and merge like edit sections. Merge equalities. + // Any edit section can move as long as it doesn't cross an equality. + diff.push([0, '']); // Add a dummy entry at the end. + var pointer = 0; + var count_delete = 0; + var count_insert = 0; + var text_delete = ''; + var text_insert = ''; + var record_insert, record_delete; + var my_xfix; + while(pointer < diff.length) { + if (diff[pointer][0] == 1) { + count_insert++; + text_insert += diff[pointer][1]; + pointer++; + } else if (diff[pointer][0] == -1) { + count_delete++; + text_delete += diff[pointer][1]; + pointer++; + } else { // Upon reaching an equality, check for prior redundancies. + if (count_delete > 1 || count_insert > 1) { + if (count_delete > 1 && count_insert > 1) { + // Factor out any common prefixies. + my_xfix = diff_prefix(text_insert, text_delete); + if (my_xfix[2] != '') { + if ((pointer - count_delete - count_insert) > 0 && diff[pointer - count_delete - count_insert - 1][0] == 0) { + text_insert = my_xfix[0]; + text_delete = my_xfix[1]; + diff[pointer - count_delete - count_insert - 1][1] += my_xfix[2]; + } + } + // Factor out any common suffixies. + my_xfix = diff_suffix(text_insert, text_delete); + if (my_xfix[2] != '') { + text_insert = my_xfix[0]; + text_delete = my_xfix[1]; + diff[pointer][1] = my_xfix[2] + diff[pointer][1]; + } + } + // Delete the offending records and add the merged ones. + if (count_delete == 0) + diff.splice(pointer - count_delete - count_insert, count_delete + count_insert, [1, text_insert]); + else if (count_insert == 0) + diff.splice(pointer - count_delete - count_insert, count_delete + count_insert, [-1, text_delete]); + else + diff.splice(pointer - count_delete - count_insert, count_delete + count_insert, [-1, text_delete], [1, text_insert]); + pointer = pointer - count_delete - count_insert + (count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1; + } else if (pointer != 0 && diff[pointer-1][0] == 0) { + // Merge this equality with the previous one. + diff[pointer-1][1] += diff[pointer][1]; + diff.splice(pointer, 1); + } else { + pointer++; + } + count_insert = 0; + count_delete = 0; + text_delete = ''; + text_insert = ''; + } + } + if (diff[diff.length-1][1] == '') + diff.pop(); // Remove the dummy entry at the end. +} + + +function diff_addindex(diff) { + // Add an index to each tuple, represents where the tuple is located in text2. + // e.g. [[-1, 'h', 0], [1, 'c', 0], [0, 'at', 1]] + var i = 0; + for (var x=0; x1, 5->8 + var chars1 = 0; + var chars2 = 0; + var last_chars1 = 0; + var last_chars2 = 0; + for (var x=0; x loc) // Overshot the location. + break; + last_chars1 = chars1; + last_chars2 = chars2; + } + if (diff.length != x && diff[x][0] == -1) // The location was deleted. + return last_chars2; + // Add the remaining character length. + return last_chars2 + (loc - last_chars1); +} + + +function diff_prettyhtml(diff) { + // Convert a diff array into a pretty HTML report. + diff_addindex(diff); + var html = ''; + for (var x=0; x/g, ">"); + t = t.replace(/\n/g, "¶
"); + if (m == -1) + html += ""+t+""; + else if (m == 1) + html += ""+t+""; + else + html += ""+t+""; + } + return html; +} + + + ////////////////////////////////////////////////////////////////////// + // Match // +////////////////////////////////////////////////////////////////////// + + +function match_getmaxbits() { + // Compute the number of bits in an int. + // The normal answer for JavaScript is 32. + var maxbits = 0; + var oldi = 1; + var newi = 2; + while (oldi != newi) { + maxbits++; + oldi = newi; + newi = newi << 1; + } + return maxbits; +} +var MATCH_MAXBITS = match_getmaxbits(); + + +function match_main(text, pattern, loc) { + // Locate the best instance of 'pattern' in 'text' near 'loc'. + loc = Math.max(0, Math.min(loc, text.length-pattern.length)); + if (text == pattern) { + // Shortcut (potentially not guaranteed by the algorithm) + return 0; + } else if (text.length == 0) { + // Nothing to match. + return null; + } else if (text.substring(loc, loc + pattern.length) == pattern) { + // Perfect match at the perfect spot! (Includes case of null pattern) + return loc; + } else { + // Do a fuzzy compare. + var match = match_bitap(text, pattern, loc); + return match; + } +} + + +function match_bitap(text, pattern, loc) { + // Locate the best instance of 'pattern' in 'text' near 'loc' using the Bitap algorithm. + if (pattern.length > MATCH_MAXBITS) + return alert("Pattern too long for this browser."); + + // Initialise the alphabet. + var s = match_alphabet(pattern); + + var score_text_length = text.length; + // Coerce the text length between reasonable maximums and minimums. + score_text_length = Math.max(score_text_length, MATCH_MINLENGTH); + score_text_length = Math.min(score_text_length, MATCH_MAXLENGTH); + + function match_bitap_score (e, x) { + // Compute and return the score for a match with e errors and x location. + var d = Math.abs(loc-x); + return (e / pattern.length / MATCH_BALANCE) + (d / score_text_length / (1.0 - MATCH_BALANCE)); + } + + // Highest score beyond which we give up. + var score_threshold = MATCH_THRESHOLD; + // Is there a nearby exact match? (speedup) + var best_loc = text.indexOf(pattern, loc); + if (best_loc != -1) + score_threshold = Math.min(match_bitap_score(0, best_loc), score_threshold); + // What about in the other direction? (speedup) + best_loc = text.lastIndexOf(pattern, loc+pattern.length); + if (best_loc != -1) + score_threshold = Math.min(match_bitap_score(0, best_loc), score_threshold); + + // Initialise the bit arrays. + var r = Array(); + var d = -1; + var matchmask = Math.pow(2, pattern.length-1); + best_loc = null; + + var bin_min, bin_mid; + var bin_max = Math.max(loc+loc, text.length); + var last_rd; + for (var d=0; d=start; j--) { + // The alphabet (s) is a sparse hash, so the following lines generate warnings. + if (d == 0) // First pass: exact match. + rd[j] = ((rd[j+1] << 1) | 1) & s[text.charAt(j)]; + else // Subsequent passes: fuzzy match. + rd[j] = ((rd[j+1] << 1) | 1) & s[text.charAt(j)] | ((last_rd[j+1] << 1) | 1) | ((last_rd[j] << 1) | 1) | last_rd[j+1]; + if (rd[j] & matchmask) { + var score = match_bitap_score(d, j); + // This match will almost certainly be better than any existing match. But check anyway. + if (score <= score_threshold) { + // Told you so. + score_threshold = score; + best_loc = j; + if (j > loc) { + // When passing loc, don't exceed our current distance from loc. + start = Math.max(0, loc - (j - loc)); + } else { + // Already passed loc, downhill from here on in. + break; + } + } + } + } + if (match_bitap_score(d+1, loc) > score_threshold) // No hope for a (better) match at greater error levels. + break; + last_rd = rd; + } + return best_loc; +} + + +function match_alphabet(pattern) { + // Initialise the alphabet for the Bitap algorithm. + var s = Object(); + for (var i=0; i 2) { + diff_cleanup_semantic(diff); + diff_cleanup_efficiency(diff); + } + } + if (diff.length == 0) + return []; // Get rid of the null case. + var patches = []; + var patch = new patch_obj(); + var char_count1 = 0; // Number of characters into the text1 string. + var char_count2 = 0; // Number of characters into the text2 string. + var last_type = null; + var prepatch_text = text1; // Recreate the patches to determine context info. + var postpatch_text = text1; + for (var x=0; x= 2*PATCH_MARGIN) { + // Time for a new patch. + if (patch.diffs.length != 0) { + patch_addcontext(patch, prepatch_text); + patches.push(patch); + var patch = new patch_obj(); + last_type = null; + prepatch_text = postpatch_text; + } + } + + // Update the current character count. + if (diff_type != 1) + char_count1 += diff_text.length; + if (diff_type != -1) + char_count2 += diff_text.length; + } + // Pick up the leftover patch if not empty. + if (patch.diffs.length != 0) { + patch_addcontext(patch, prepatch_text); + patches.push(patch); + } + + return patches; +} + + +function patch_apply(patches, text) { + // Merge a set of patches onto the text. + // Return a patched text, as well as a list of true/false values indicating which patches were applied. + patch_splitmax(patches); + var results = []; + var delta = 0; + var expected_loc, start_loc; + var text1, text2; + var diff, mod, index1, index2; + for (var x=0; x MATCH_MAXBITS) { + bigpatch = patches[x]; + // Remove the big old patch. + patches.splice(x, 1); + patch_size = MATCH_MAXBITS; + start1 = bigpatch.start1; + start2 = bigpatch.start2; + precontext = ''; + while (bigpatch.diffs.length != 0) { + // Create one of several smaller patches. + patch = new patch_obj(); + empty = true; + patch.start1 = start1 - precontext.length; + patch.start2 = start2 - precontext.length; + if (precontext != '') { + patch.length1 = patch.length2 = precontext.length; + patch.diffs.push([0, precontext]); + } + while (bigpatch.diffs.length != 0 && patch.length1 < patch_size - PATCH_MARGIN) { + diff_type = bigpatch.diffs[0][0]; + diff_text = bigpatch.diffs[0][1]; + if (diff_type == 1) { + // Insertions are harmless. + patch.length2 += diff_text.length; + start2 += diff_text.length; + patch.diffs.push(bigpatch.diffs.shift()); + empty = false; + } else { + // Deletion or equality. Only take as much as we can stomach. + diff_text = diff_text.substring(0, patch_size - patch.length1 - PATCH_MARGIN); + patch.length1 += diff_text.length; + start1 += diff_text.length; + if (diff_type == 0) { + patch.length2 += diff_text.length; + start2 += diff_text.length; + } else { + empty = false; + } + patch.diffs.push([diff_type, diff_text]); + if (diff_text == bigpatch.diffs[0][1]) + bigpatch.diffs.shift(); + else + bigpatch.diffs[0][1] = bigpatch.diffs[0][1].substring(diff_text.length); + } + } + // Compute the head context for the next patch. + precontext = patch.text2(); + precontext = precontext.substring(precontext.length - PATCH_MARGIN); + // Append the end context for this patch. + postcontext = bigpatch.text1().substring(0, PATCH_MARGIN); + if (postcontext != '') { + patch.length1 += postcontext.length; + patch.length2 += postcontext.length; + if (patch.diffs.length > 0 && patch.diffs[patch.diffs.length-1][0] == 0) + patch.diffs[patch.diffs.length-1][1] += postcontext; + else + patch.diffs.push([0, postcontext]); + } + if (!empty) + patches.splice(x++, 0, patch); + } + } + } +} + + +function patch_totext(patches) { + // Take a list of patches and return a textual representation. + var text = ''; + for (var x=0; x + + {$lang_emotions_title} + + + + + +
+
{$lang_emotions_title}:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
{$lang_emotions_cool}{$lang_emotions_cry}{$lang_emotions_embarassed}{$lang_emotions_foot_in_mouth}
{$lang_emotions_frown}{$lang_emotions_innocent}{$lang_emotions_kiss}{$lang_emotions_laughing}
{$lang_emotions_money_mouth}{$lang_emotions_sealed}{$lang_emotions_smile}{$lang_emotions_surprised}
{$lang_emotions_tongue-out}{$lang_emotions_undecided}{$lang_emotions_wink}{$lang_emotions_yell}
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/emotions.gif Binary file includes/clientside/tinymce/plugins/emotions/images/emotions.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/emotions/images/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,2 @@ +These emotions where taken from Mozilla Thunderbird. +I hope they don't get angry if I use them here after all this is a open source project aswell. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-cool.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-cool.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-cry.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-cry.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-embarassed.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-embarassed.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-foot-in-mouth.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-foot-in-mouth.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-frown.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-frown.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-innocent.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-innocent.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-kiss.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-kiss.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-laughing.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-laughing.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-money-mouth.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-money-mouth.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-sealed.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-sealed.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-smile.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-smile.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-surprised.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-surprised.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-tongue-out.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-tongue-out.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-undecided.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-undecided.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-wink.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-wink.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/images/smiley-yell.gif Binary file includes/clientside/tinymce/plugins/emotions/images/smiley-yell.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/jscripts/functions.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/emotions/jscripts/functions.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,21 @@ +function init() { + tinyMCEPopup.resizeToInnerSize(); +} + +function insertEmotion(file_name, title) { + title = tinyMCE.getLang(title); + + if (title == null) + title = ""; + + // XML encode + title = title.replace(/&/g, '&'); + title = title.replace(/\"/g, '"'); + title = title.replace(//g, '>'); + + var html = '' + title + ''; + + tinyMCE.execCommand('mceInsertContent', false, html); + tinyMCEPopup.close(); +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/langs/en.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/emotions/langs/en.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,22 @@ +// UK lang variables + +tinyMCE.addToLang('emotions',{ +title : 'Insert emotion', +desc : 'Emotions', +cool : 'Cool', +cry : 'Cry', +embarassed : 'Embarassed', +foot_in_mouth : 'Foot in mouth', +frown : 'Frown', +innocent : 'Innocent', +kiss : 'Kiss', +laughing : 'Laughing', +money_mouth : 'Money mouth', +sealed : 'Sealed', +smile : 'Smile', +surprised : 'Surprised', +tongue_out : 'Tongue out', +undecided : 'Undecided', +wink : 'Wink', +yell : 'Yell' +}); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/emotions/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/emotions/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +Check the TinyMCE documentation for details on this plugin. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/flash/css/content.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/flash/css/content.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,7 @@ +.mceItemFlash { + border: 1px dotted #cc0000; + background-image: url('../images/flash.gif'); + background-position: center; + background-repeat: no-repeat; + background-color: #ffffcc; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/flash/css/flash.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/flash/css/flash.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,11 @@ +.panel_wrapper div.current { + height: 100px; +} + +#width, #height { + width: 50px; +} + +#file { + width: 250px; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/flash/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/flash/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('flash');var TinyMCE_FlashPlugin={getInfo:function(){return{longname:'Flash',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/flash',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},initInstance:function(inst){if(!tinyMCE.settings['flash_skip_plugin_css'])tinyMCE.importCSS(inst.getDoc(),tinyMCE.baseURL+"/plugins/flash/css/content.css")},getControlHTML:function(cn){switch(cn){case"flash":return tinyMCE.getButtonHTML(cn,'lang_flash_desc','{$pluginurl}/images/flash.gif','mceFlash')}return""},execCommand:function(editor_id,element,command,user_interface,value){switch(command){case"mceFlash":var name="",swffile="",swfwidth="",swfheight="",action="insert";var template=new Array();var inst=tinyMCE.getInstanceById(editor_id);var focusElm=inst.getFocusElement();template['file']='../../plugins/flash/flash.htm';template['width']=430;template['height']=175;template['width']+=tinyMCE.getLang('lang_flash_delta_width',0);template['height']+=tinyMCE.getLang('lang_flash_delta_height',0);if(focusElm!=null&&focusElm.nodeName.toLowerCase()=="img"){name=tinyMCE.getAttrib(focusElm,'class');if(name.indexOf('mceItemFlash')==-1)return true;swffile=tinyMCE.getAttrib(focusElm,'alt');if(tinyMCE.getParam('convert_urls'))swffile=eval(tinyMCE.settings['urlconverter_callback']+"(swffile, null, true);");swfwidth=tinyMCE.getAttrib(focusElm,'width');swfheight=tinyMCE.getAttrib(focusElm,'height');action="update"}tinyMCE.openWindow(template,{editor_id:editor_id,inline:"yes",swffile:swffile,swfwidth:swfwidth,swfheight:swfheight,action:action});return true}return false},cleanup:function(type,content){switch(type){case"insert_to_editor_dom":if(tinyMCE.getParam('convert_urls')){var imgs=content.getElementsByTagName("img");for(var i=0;i','gi'),'');content=content.replace(new RegExp('<[ ]*object','gi'),'','gi'),'');while((startPos=content.indexOf('',startPos);var attribs=TinyMCE_FlashPlugin._parseAttributes(content.substring(startPos+6,endPos));embedList[embedList.length]=attribs}var index=0;while((startPos=content.indexOf('=embedList.length)break;var attribs=embedList[index];endPos=content.indexOf('',startPos);endPos+=9;var contentAfter=content.substring(endPos);content=content.substring(0,startPos);content+=''+content.substring(endPos);content+=contentAfter;index++;startPos++}var index=0;while((startPos=content.indexOf('=embedList.length)break;var attribs=embedList[index];endPos=content.indexOf('>',startPos);endPos+=9;var contentAfter=content.substring(endPos);content=content.substring(0,startPos);content+=''+content.substring(endPos);content+=contentAfter;index++;startPos++}break;case"get_from_editor":var startPos=-1;while((startPos=content.indexOf('',startPos);var attribs=TinyMCE_FlashPlugin._parseAttributes(content.substring(startPos+4,endPos));if(attribs['class']!="mceItemFlash")continue;endPos+=2;var embedHTML='';var wmode=tinyMCE.getParam("flash_wmode","");var quality=tinyMCE.getParam("flash_quality","high");var menu=tinyMCE.getParam("flash_menu","false");embedHTML+='';embedHTML+='';embedHTML+='';embedHTML+='';embedHTML+='';embedHTML+='';chunkBefore=content.substring(0,startPos);chunkAfter=content.substring(endPos);content=chunkBefore+embedHTML+chunkAfter}break}return content},handleNodeChange:function(editor_id,node,undo_index,undo_levels,visual_aid,any_selection){if(node==null)return;do{if(node.nodeName=="IMG"&&tinyMCE.getAttrib(node,'class').indexOf('mceItemFlash')==0){tinyMCE.switchClass(editor_id+'_flash','mceButtonSelected');return true}}while((node=node.parentNode));tinyMCE.switchClass(editor_id+'_flash','mceButtonNormal');return true},_parseAttributes:function(attribute_string){var attributeName="";var attributeValue="";var withInName;var withInValue;var attributes=new Array();var whiteSpaceRegExp=new RegExp('^[ \n\r\t]+','g');if(attribute_string==null||attribute_string.length<2)return null;withInName=withInValue=false;for(var i=0;i','gi'),''); + content = content.replace(new RegExp('<[ ]*object','gi'),'','gi'),''); + + // Parse all embed tags + while ((startPos = content.indexOf('', startPos); + var attribs = TinyMCE_FlashPlugin._parseAttributes(content.substring(startPos + 6, endPos)); + embedList[embedList.length] = attribs; + } + + // Parse all object tags and replace them with images from the embed data + var index = 0; + while ((startPos = content.indexOf('= embedList.length) + break; + + var attribs = embedList[index]; + + // Find end of object + endPos = content.indexOf('', startPos); + endPos += 9; + + // Insert image + var contentAfter = content.substring(endPos); + content = content.substring(0, startPos); + content += '' + content.substring(endPos); + content += contentAfter; + index++; + + startPos++; + } + + // Parse all embed tags and replace them with images from the embed data + var index = 0; + while ((startPos = content.indexOf('= embedList.length) + break; + + var attribs = embedList[index]; + + // Find end of embed + endPos = content.indexOf('>', startPos); + endPos += 9; + + // Insert image + var contentAfter = content.substring(endPos); + content = content.substring(0, startPos); + content += '' + content.substring(endPos); + content += contentAfter; + index++; + + startPos++; + } + + break; + + case "get_from_editor": + // Parse all img tags and replace them with object+embed + var startPos = -1; + + while ((startPos = content.indexOf('', startPos); + var attribs = TinyMCE_FlashPlugin._parseAttributes(content.substring(startPos + 4, endPos)); + + // Is not flash, skip it + if (attribs['class'] != "mceItemFlash") + continue; + + endPos += 2; + + var embedHTML = ''; + var wmode = tinyMCE.getParam("flash_wmode", ""); + var quality = tinyMCE.getParam("flash_quality", "high"); + var menu = tinyMCE.getParam("flash_menu", "false"); + + // Insert object + embed + embedHTML += ''; + embedHTML += ''; + embedHTML += ''; + embedHTML += ''; + embedHTML += ''; + embedHTML += ''; + + // Insert embed/object chunk + chunkBefore = content.substring(0, startPos); + chunkAfter = content.substring(endPos); + content = chunkBefore + embedHTML + chunkAfter; + } + break; + } + + // Pass through to next handler in chain + return content; + }, + + handleNodeChange : function(editor_id, node, undo_index, undo_levels, visual_aid, any_selection) { + if (node == null) + return; + + do { + if (node.nodeName == "IMG" && tinyMCE.getAttrib(node, 'class').indexOf('mceItemFlash') == 0) { + tinyMCE.switchClass(editor_id + '_flash', 'mceButtonSelected'); + return true; + } + } while ((node = node.parentNode)); + + tinyMCE.switchClass(editor_id + '_flash', 'mceButtonNormal'); + + return true; + }, + + // Private plugin internal functions + + _parseAttributes : function(attribute_string) { + var attributeName = ""; + var attributeValue = ""; + var withInName; + var withInValue; + var attributes = new Array(); + var whiteSpaceRegExp = new RegExp('^[ \n\r\t]+', 'g'); + + if (attribute_string == null || attribute_string.length < 2) + return null; + + withInName = withInValue = false; + + for (var i=0; i +
+ + +
+
+
+ {$lang_flash_general} + + + + + + + + + + + + + + +
+ + + + + +
 
+
 
+ +  x  + +
+
+
+
+ +
+
+ +
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/flash/images/flash.gif Binary file includes/clientside/tinymce/plugins/flash/images/flash.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/flash/jscripts/flash.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/flash/jscripts/flash.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,107 @@ +var url = tinyMCE.getParam("flash_external_list_url"); +if (url != null) { + // Fix relative + if (url.charAt(0) != '/' && url.indexOf('://') == -1) + url = tinyMCE.documentBasePath + "/" + url; + + document.write(''); +} + +function init() { + tinyMCEPopup.resizeToInnerSize(); + + document.getElementById("filebrowsercontainer").innerHTML = getBrowserHTML('filebrowser','file','flash','flash'); + + // Image list outsrc + var html = getFlashListHTML('filebrowser','file','flash','flash'); + if (html == "") + document.getElementById("linklistrow").style.display = 'none'; + else + document.getElementById("linklistcontainer").innerHTML = html; + + var formObj = document.forms[0]; + var swffile = tinyMCE.getWindowArg('swffile'); + var swfwidth = '' + tinyMCE.getWindowArg('swfwidth'); + var swfheight = '' + tinyMCE.getWindowArg('swfheight'); + + if (swfwidth.indexOf('%')!=-1) { + formObj.width2.value = "%"; + formObj.width.value = swfwidth.substring(0,swfwidth.length-1); + } else { + formObj.width2.value = "px"; + formObj.width.value = swfwidth; + } + + if (swfheight.indexOf('%')!=-1) { + formObj.height2.value = "%"; + formObj.height.value = swfheight.substring(0,swfheight.length-1); + } else { + formObj.height2.value = "px"; + formObj.height.value = swfheight; + } + + formObj.file.value = swffile; + formObj.insert.value = tinyMCE.getLang('lang_' + tinyMCE.getWindowArg('action'), 'Insert', true); + + selectByValue(formObj, 'linklist', swffile); + + // Handle file browser + if (isVisible('filebrowser')) + document.getElementById('file').style.width = '230px'; + + // Auto select flash in list + if (typeof(tinyMCEFlashList) != "undefined" && tinyMCEFlashList.length > 0) { + for (var i=0; i 0) { + var html = ""; + + html += ''; + + return html; + } + + return ""; +} + +function insertFlash() { + var formObj = document.forms[0]; + var html = ''; + var file = formObj.file.value; + var width = formObj.width.value; + var height = formObj.height.value; + if (formObj.width2.value=='%') { + width = width + '%'; + } + if (formObj.height2.value=='%') { + height = height + '%'; + } + + if (width == "") + width = 100; + + if (height == "") + height = 100; + + html += '' + + '' + file + ''; + + tinyMCEPopup.execCommand("mceInsertContent", true, html); + tinyMCE.selectedInstance.repaint(); + + tinyMCEPopup.close(); +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/flash/langs/en.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/flash/langs/en.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,11 @@ +// UK lang variables + +tinyMCE.addToLang('flash',{ +title : 'Insert / edit Flash Movie', +desc : 'Insert / edit Flash Movie', +file : 'Flash-File (.swf)', +size : 'Size', +list : 'Flash files', +props : 'Flash properties', +general : 'General' +}); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/flash/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/flash/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +Check the TinyMCE documentation for details on this plugin. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/fullpage/blank.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/fullpage/blank.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,9 @@ + + + blank_page + + + + + + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/fullpage/css/fullpage.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/fullpage/css/fullpage.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,172 @@ +/* Hide the advanced tab */ +#advanced_tab { + display: none; +} + +#metatitle, #metakeywords, #metadescription, #metaauthor, #metacopyright { + width: 280px; +} + +#doctype, #docencoding { + width: 200px; +} + +#langcode { + width: 30px; +} + +#bgimage { + width: 220px; +} + +#fontface { + width: 240px; +} + +#leftmargin, #rightmargin, #topmargin, #bottommargin { + width: 50px; +} + +.panel_wrapper div.current { + height: 400px; +} + +#stylesheet, #style { + width: 240px; +} + +/* Head list classes */ + +.headlistwrapper { + width: 100%; +} + +.addbutton, .removebutton, .moveupbutton, .movedownbutton { + border-top: 1px solid; + border-left: 1px solid; + border-bottom: 1px solid; + border-right: 1px solid; + border-color: #F0F0EE; + cursor: default; + display: block; + width: 20px; + height: 20px; +} + +.addbutton:hover, .removebutton:hover, .moveupbutton:hover, .movedownbutton:hover { + border: 1px solid #0A246A; + background-color: #B6BDD2; +} + +.addbutton { + background-image: url('../images/add.gif'); + float: left; + margin-right: 3px; +} + +.removebutton { + background-image: url('../images/remove.gif'); + float: left; +} + +.moveupbutton { + background-image: url('../images/move_up.gif'); + float: left; + margin-right: 3px; +} + +.movedownbutton { + background-image: url('../images/move_down.gif'); + float: left; +} + +.selected { + border: 1px solid #0A246A; + background-color: #B6BDD2; +} + +.toolbar { + width: 100%; +} + +#headlist { + width: 100%; + margin-top: 3px; + font-size: 11px; +} + +#info, #title_element, #meta_element, #script_element, #style_element, #base_element, #link_element, #comment_element, #unknown_element { + display: none; +} + +#addmenu { + position: absolute; + border: 1px solid gray; + display: none; + z-index: 100; + background-color: white; +} + +#addmenu a { + display: block; + width: 100%; + line-height: 20px; + text-decoration: none; + background-color: white; +} + +#addmenu a:hover { + background-color: #B6BDD2; + color: black; +} + +#addmenu span { + padding-left: 10px; + padding-right: 10px; +} + +#updateElementPanel { + display: none; +} + +#script_element .panel_wrapper div.current { + height: 108px; +} + +#style_element .panel_wrapper div.current { + height: 108px; +} + +#link_element .panel_wrapper div.current { + height: 140px; +} + +#element_script_value { + width: 100%; + height: 100px; +} + +#element_comment_value { + width: 100%; + height: 120px; +} + +#element_style_value { + width: 100%; + height: 100px; +} + +#element_title, #element_script_src, #element_meta_name, #element_meta_content, #element_base_href, #element_link_href, #element_link_title { + width: 250px; +} + +.updateElementButton { + margin-top: 3px; +} + +/* MSIE specific styles */ + +* html .addbutton, * html .removebutton, * html .moveupbutton, * html .movedownbutton { + width: 22px; + height: 22px; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/fullpage/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/fullpage/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('fullpage');var TinyMCE_FullPagePlugin={getInfo:function(){return{longname:'Fullpage',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/fullpage',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},getControlHTML:function(cn){switch(cn){case"fullpage":return tinyMCE.getButtonHTML(cn,'lang_fullpage_desc','{$pluginurl}/images/fullpage.gif','mceFullPageProperties')}return""},execCommand:function(editor_id,element,command,user_interface,value){switch(command){case"mceFullPageProperties":var template=new Array();template['file']='../../plugins/fullpage/fullpage.htm';template['width']=430;template['height']=485+(tinyMCE.isOpera?5:0);template['width']+=tinyMCE.getLang('lang_fullpage_delta_width',0);template['height']+=tinyMCE.getLang('lang_fullpage_delta_height',0);tinyMCE.openWindow(template,{editor_id:editor_id,inline:"yes"});return true;case"mceFullPageUpdate":TinyMCE_FullPagePlugin._addToHead(tinyMCE.getInstanceById(editor_id));return true}return false},cleanup:function(type,content,inst){switch(type){case"insert_to_editor":var tmp=content.toLowerCase();var pos=tmp.indexOf('',pos);pos2=tmp.lastIndexOf('');inst.fullpageTopContent=content.substring(0,pos+1);content=content.substring(pos+1,pos2);}else{if(!inst.fullpageTopContent){var docType=tinyMCE.getParam("fullpage_default_doctype",'');var enc=tinyMCE.getParam("fullpage_default_encoding",'utf-8');var title=tinyMCE.getParam("fullpage_default_title",'Untitled document');var lang=tinyMCE.getParam("fullpage_default_langcode",'en');var pi=tinyMCE.getParam("fullpage_default_xml_pi",true);var ff=tinyMCE.getParam("fullpage_default_font_family","");var fz=tinyMCE.getParam("fullpage_default_font_size","");var ds=tinyMCE.getParam("fullpage_default_style","");var dtc=tinyMCE.getParam("fullpage_default_text_color","");title=title.replace(/&/g,'&');title=title.replace(/\"/g,'"');title=title.replace(//g,'>');tmp='';if(pi)tmp+='\n';tmp+=docType+'\n';tmp+='\n';tmp+='\n';tmp+='\t'+title+'\n';tmp+='\t\n';tmp+='\n';tmp+='\n";break}return content},_addToHead:function(inst){var doc=inst.getDoc();var head=doc.getElementsByTagName("head")[0];var body=doc.body;var h=inst.fullpageTopContent;var e=doc.createElement("body");var nl,i,le,tmp;h=h.replace(/(\r|\n)/gi,'');h=h.replace(/<\?[^\>]*\>/gi,'');h=h.replace(/<\/?(!DOCTYPE|head|html)[^\>]*\>/gi,'');h=h.replace(//gi,'');h=h.replace(//gi,'');h=h.replace(/<(meta|base)[^>]*>/gi,'');h=h.replace(/]*)\/>/gi,'
');h=h.replace(/0){body.style.cssText=tinyMCE.getAttrib(nl[0],'style');if((tmp=tinyMCE.getAttrib(nl[0],'leftmargin'))!=''&&body.style.marginLeft=='')body.style.marginLeft=tmp+"px";if((tmp=tinyMCE.getAttrib(nl[0],'rightmargin'))!=''&&body.style.marginRight=='')body.style.marginRight=tmp+"px";if((tmp=tinyMCE.getAttrib(nl[0],'topmargin'))!=''&&body.style.marginTop=='')body.style.marginTop=tmp+"px";if((tmp=tinyMCE.getAttrib(nl[0],'bottommargin'))!=''&&body.style.marginBottom=='')body.style.marginBottom=tmp+"px";body.dir=tinyMCE.getAttrib(nl[0],'dir');body.vLink=tinyMCE.getAttrib(nl[0],'vlink');body.aLink=tinyMCE.getAttrib(nl[0],'alink');body.link=tinyMCE.getAttrib(nl[0],'link');body.text=tinyMCE.getAttrib(nl[0],'text');if((tmp=tinyMCE.getAttrib(nl[0],'background'))!='')body.style.backgroundImage="url('"+tmp+"')";if((tmp=tinyMCE.getAttrib(nl[0],'bgcolor'))!='')body.style.backgroundColor=tmp}}};tinyMCE.addPlugin("fullpage",TinyMCE_FullPagePlugin);
\ No newline at end of file
diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/fullpage/editor_plugin_src.js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/includes/clientside/tinymce/plugins/fullpage/editor_plugin_src.js	Wed Jun 13 16:07:17 2007 -0400
@@ -0,0 +1,227 @@
+/**
+ * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $
+ *
+ * @author Moxiecode
+ * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved.
+ */
+
+/* Import plugin specific language pack */
+tinyMCE.importPluginLanguagePack('fullpage');
+
+var TinyMCE_FullPagePlugin = {
+	getInfo : function() {
+		return {
+			longname : 'Fullpage',
+			author : 'Moxiecode Systems AB',
+			authorurl : 'http://tinymce.moxiecode.com',
+			infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/fullpage',
+			version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion
+		};
+	},
+
+	getControlHTML : function(cn) {
+		switch (cn) {
+			case "fullpage":
+				return tinyMCE.getButtonHTML(cn, 'lang_fullpage_desc', '{$pluginurl}/images/fullpage.gif', 'mceFullPageProperties');
+		}
+
+		return "";
+	},
+
+	execCommand : function(editor_id, element, command, user_interface, value) {
+		// Handle commands
+		switch (command) {
+			case "mceFullPageProperties":
+				var template = new Array();
+
+				template['file']   = '../../plugins/fullpage/fullpage.htm';
+				template['width']  = 430;
+				template['height'] = 485 + (tinyMCE.isOpera ? 5 : 0);
+
+				template['width'] += tinyMCE.getLang('lang_fullpage_delta_width', 0);
+				template['height'] += tinyMCE.getLang('lang_fullpage_delta_height', 0);
+
+				tinyMCE.openWindow(template, {editor_id : editor_id, inline : "yes"});
+			return true;
+
+			case "mceFullPageUpdate":
+				TinyMCE_FullPagePlugin._addToHead(tinyMCE.getInstanceById(editor_id));
+				return true;
+	   }
+
+	   // Pass to next handler in chain
+	   return false;
+	},
+
+	cleanup : function(type, content, inst) {
+		switch (type) {
+			case "insert_to_editor":
+				var tmp = content.toLowerCase();
+				var pos = tmp.indexOf('', pos);
+					pos2 = tmp.lastIndexOf('');
+					inst.fullpageTopContent = content.substring(0, pos + 1);
+					content = content.substring(pos + 1, pos2);
+					// tinyMCE.debug(inst.fullpageTopContent, content);
+				} else {
+					if (!inst.fullpageTopContent) {
+						var docType = tinyMCE.getParam("fullpage_default_doctype", '');
+						var enc = tinyMCE.getParam("fullpage_default_encoding", 'utf-8');
+						var title = tinyMCE.getParam("fullpage_default_title", 'Untitled document');
+						var lang = tinyMCE.getParam("fullpage_default_langcode", 'en');
+						var pi = tinyMCE.getParam("fullpage_default_xml_pi", true);
+						var ff = tinyMCE.getParam("fullpage_default_font_family", "");
+						var fz = tinyMCE.getParam("fullpage_default_font_size", "");
+						var ds = tinyMCE.getParam("fullpage_default_style", "");
+						var dtc = tinyMCE.getParam("fullpage_default_text_color", "");
+
+						// Xml encode it
+						title = title.replace(/&/g, '&');
+						title = title.replace(/\"/g, '"');
+						title = title.replace(//g, '>');
+
+						tmp = '';
+
+						// Make default chunk
+						if (pi)
+							tmp += '\n';
+
+						tmp += docType + '\n';
+						tmp += '\n';
+						tmp += '\n';
+						tmp += '\t' + title + '\n';
+						tmp += '\t\n';
+						tmp += '\n';
+						tmp += '\n";
+
+				break;
+		}
+
+		// Pass through to next handler in chain
+		return content;
+	},
+
+	// Private plugin internal methods
+
+	_addToHead : function(inst) {
+		var doc = inst.getDoc();
+		var head = doc.getElementsByTagName("head")[0];
+		var body = doc.body;
+		var h = inst.fullpageTopContent;
+		var e = doc.createElement("body");
+		var nl, i, le, tmp;
+
+		// Remove stuff we don't want
+		h = h.replace(/(\r|\n)/gi, '');
+		h = h.replace(/<\?[^\>]*\>/gi, '');
+		h = h.replace(/<\/?(!DOCTYPE|head|html)[^\>]*\>/gi, '');
+		h = h.replace(//gi, '');
+		h = h.replace(//gi, '');
+		h = h.replace(/<(meta|base)[^>]*>/gi, '');
+
+		// Make link and style elements into pre
+		h = h.replace(/]*)\/>/gi, '
');
+		//h = h.replace(/]*)>(.*?)<\/style>/gi, '
$2
'); + + // Make body a div + h = h.replace(/ 0) { + body.style.cssText = tinyMCE.getAttrib(nl[0], 'style'); + + if ((tmp = tinyMCE.getAttrib(nl[0], 'leftmargin')) != '' && body.style.marginLeft == '') + body.style.marginLeft = tmp + "px"; + + if ((tmp = tinyMCE.getAttrib(nl[0], 'rightmargin')) != '' && body.style.marginRight == '') + body.style.marginRight = tmp + "px"; + + if ((tmp = tinyMCE.getAttrib(nl[0], 'topmargin')) != '' && body.style.marginTop == '') + body.style.marginTop = tmp + "px"; + + if ((tmp = tinyMCE.getAttrib(nl[0], 'bottommargin')) != '' && body.style.marginBottom == '') + body.style.marginBottom = tmp + "px"; + + body.dir = tinyMCE.getAttrib(nl[0], 'dir'); + body.vLink = tinyMCE.getAttrib(nl[0], 'vlink'); + body.aLink = tinyMCE.getAttrib(nl[0], 'alink'); + body.link = tinyMCE.getAttrib(nl[0], 'link'); + body.text = tinyMCE.getAttrib(nl[0], 'text'); + + if ((tmp = tinyMCE.getAttrib(nl[0], 'background')) != '') + body.style.backgroundImage = "url('" + tmp + "')"; + + if ((tmp = tinyMCE.getAttrib(nl[0], 'bgcolor')) != '') + body.style.backgroundColor = tmp; + } + } +}; + +tinyMCE.addPlugin("fullpage", TinyMCE_FullPagePlugin); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/fullpage/fullpage.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/fullpage/fullpage.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,577 @@ + + + {$lang_fullpage_title} + + + + + + + + + +
+ + +
+
+
+ {$lang_fullpage_meta_props} + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
 
 
 
 
  + +
+
+ +
+ {$lang_fullpage_langprops} + + + + + + + + + + + + + + + + + + + + + + +
+ +
  + +
 
+ +
 
+
+
+ +
+
+ {$lang_fullpage_appearance_textprops} + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + + + +
 
+
+
+ +
+ {$lang_fullpage_appearance_bgprops} + + + + + + + + + + +
+ + + + + +
 
+
+ + + + + +
 
+
+
+ +
+ {$lang_fullpage_appearance_marginprops} + + + + + + + + + + + + + + +
+
+ +
+ {$lang_fullpage_appearance_linkprops} + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + + +
 
+
+ + + + + +
 
+
  
+
+ +
+ {$lang_fullpage_appearance_style} + + + + + + + + + + +
+ + + + +
 
+
+
+ +
+ + +
+ {$lang_fullpage_head_elements} + +
+
+
+ + +
+
+ + +
+
+
+ +
+
+ +
+ {$lang_fullpage_meta_element} + + + + + + + + + + + + + + +
+ + +
+ +
+ {$lang_fullpage_title_element} + + + + + + +
+ + +
+ +
+ {$lang_fullpage_script_element} + + + +
+ +
+
+ + + + + + + + + + + + + + + + + +
+ + + + +
 
+
+ +
+ +
+
+ + +
+ +
+ {$lang_fullpage_style_element} + + + +
+ +
+
+ + + + + + + + + +
+
+ +
+ +
+
+ + +
+ +
+ {$lang_fullpage_base_element} + + + + + + + + + + +
+ + +
+ + + +
+ {$lang_fullpage_comment_element} + + + + +
+
+
+ +
+
+ +
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/fullpage/images/add.gif Binary file includes/clientside/tinymce/plugins/fullpage/images/add.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/fullpage/images/fullpage.gif Binary file includes/clientside/tinymce/plugins/fullpage/images/fullpage.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/fullpage/images/move_down.gif Binary file includes/clientside/tinymce/plugins/fullpage/images/move_down.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/fullpage/images/move_up.gif Binary file includes/clientside/tinymce/plugins/fullpage/images/move_up.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/fullpage/images/remove.gif Binary file includes/clientside/tinymce/plugins/fullpage/images/remove.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/fullpage/jscripts/fullpage.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/fullpage/jscripts/fullpage.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,580 @@ +// + +var defaultDocTypes = + 'XHTML 1.0 Transitional=,' + + 'XHTML 1.0 Frameset=,' + + 'XHTML 1.0 Strict=,' + + 'XHTML 1.1=">,' + + 'HTML 4.01 Transitional=,' + + 'HTML 4.01 Strict=,' + + 'HTML 4.01 Frameset='; + +var defaultEncodings = + 'Western european (iso-8859-1)=iso-8859-1,' + + 'Central European (iso-8859-2)=iso-8859-2,' + + 'Unicode (UTF-8)=utf-8,' + + 'Chinese traditional (Big5)=big5,' + + 'Cyrillic (iso-8859-5)=iso-8859-5,' + + 'Japanese (iso-2022-jp)=iso-2022-jp,' + + 'Greek (iso-8859-7)=iso-8859-7,' + + 'Korean (iso-2022-kr)=iso-2022-kr,' + + 'ASCII (us-ascii)=us-ascii'; + +var defaultMediaTypes = + 'all=all,' + + 'screen=screen,' + + 'print=print,' + + 'tty=tty,' + + 'tv=tv,' + + 'projection=projection,' + + 'handheld=handheld,' + + 'braille=braille,' + + 'aural=aural'; + +var defaultFontNames = 'Arial=arial,helvetica,sans-serif;Courier New=courier new,courier,monospace;Georgia=georgia,times new roman,times,serif;Tahoma=tahoma,arial,helvetica,sans-serif;Times New Roman=times new roman,times,serif;Verdana=verdana,arial,helvetica,sans-serif;Impact=impact;WingDings=wingdings'; +var defaultFontSizes = '10px,11px,12px,13px,14px,15px,16px'; + +var addMenuLayer = new MCLayer("addmenu"); +var lastElementType = null; +var topDoc; + +function init() { + var f = document.forms['fullpage']; + var i, p, doctypes, encodings, mediaTypes, fonts; + var inst = tinyMCE.getInstanceById(tinyMCE.getWindowArg('editor_id')); + + // Setup doctype select box + doctypes = tinyMCE.getParam("fullpage_doctypes", defaultDocTypes).split(','); + for (i=0; i 1) + addSelectValue(f, 'doctypes', p[0], p[1]); + } + + // Setup fonts select box + fonts = tinyMCE.getParam("fullpage_fonts", defaultFontNames).split(';'); + for (i=0; i 1) + addSelectValue(f, 'fontface', p[0], p[1]); + } + + // Setup fontsize select box + fonts = tinyMCE.getParam("fullpage_fontsizes", defaultFontSizes).split(','); + for (i=0; i 1) { + addSelectValue(f, 'element_style_media', p[0], p[1]); + addSelectValue(f, 'element_link_media', p[0], p[1]); + } + } + + // Setup encodings select box + encodings = tinyMCE.getParam("fullpage_encodings", defaultEncodings).split(','); + for (i=0; i 1) { + addSelectValue(f, 'docencoding', p[0], p[1]); + addSelectValue(f, 'element_script_charset', p[0], p[1]); + addSelectValue(f, 'element_link_charset', p[0], p[1]); + } + } + + document.getElementById('bgcolor_pickcontainer').innerHTML = getColorPickerHTML('bgcolor_pick','bgcolor'); + document.getElementById('link_color_pickcontainer').innerHTML = getColorPickerHTML('link_color_pick','link_color'); + //document.getElementById('hover_color_pickcontainer').innerHTML = getColorPickerHTML('hover_color_pick','hover_color'); + document.getElementById('visited_color_pickcontainer').innerHTML = getColorPickerHTML('visited_color_pick','visited_color'); + document.getElementById('active_color_pickcontainer').innerHTML = getColorPickerHTML('active_color_pick','active_color'); + document.getElementById('textcolor_pickcontainer').innerHTML = getColorPickerHTML('textcolor_pick','textcolor'); + document.getElementById('stylesheet_browsercontainer').innerHTML = getBrowserHTML('stylesheetbrowser','stylesheet','file','fullpage'); + document.getElementById('link_href_pickcontainer').innerHTML = getBrowserHTML('link_href_browser','element_link_href','file','fullpage'); + document.getElementById('script_src_pickcontainer').innerHTML = getBrowserHTML('script_src_browser','element_script_src','file','fullpage'); + document.getElementById('bgimage_pickcontainer').innerHTML = getBrowserHTML('bgimage_browser','bgimage','image','fullpage'); + + // Resize some elements + if (isVisible('stylesheetbrowser')) + document.getElementById('stylesheet').style.width = '220px'; + + if (isVisible('link_href_browser')) + document.getElementById('element_link_href').style.width = '230px'; + + if (isVisible('bgimage_browser')) + document.getElementById('bgimage').style.width = '210px'; + + // Create iframe + var iframe = document.createElement('iframe'); + + iframe.id = 'tempFrame'; + iframe.style.display = 'none'; + iframe.src = tinyMCE.baseURL + "/plugins/fullpage/blank.htm"; + + document.body.appendChild(iframe); + + tinyMCEPopup.resizeToInnerSize(); +} + +function setupIframe(doc) { + var inst = tinyMCE.getInstanceById(tinyMCE.getWindowArg('editor_id')); + var hc = inst.fullpageTopContent; + var f = document.forms[0]; + var xmlVer, xmlEnc, docType; + var nodes, i, x, name, value, tmp, l; + + // Keep it from not loading/executing stuff + hc = hc.replace(/ + + + + +
+ +
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/fullscreen/images/fullscreen.gif Binary file includes/clientside/tinymce/plugins/fullscreen/images/fullscreen.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/fullscreen/langs/en.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/fullscreen/langs/en.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,5 @@ +// UK lang variables + +tinyMCE.addToLang('',{ +fullscreen_desc : 'Toggle fullscreen mode' +}); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/fullscreen/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/fullscreen/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +Check the TinyMCE documentation for details on this plugin. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/iespell/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/iespell/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('iespell');var TinyMCE_IESpellPlugin={getInfo:function(){return{longname:'IESpell (MSIE Only)',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/iespell',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},getControlHTML:function(cn){if(cn=="iespell"&&(tinyMCE.isMSIE&&!tinyMCE.isOpera))return tinyMCE.getButtonHTML(cn,'lang_iespell_desc','{$pluginurl}/images/iespell.gif','mceIESpell');return""},execCommand:function(editor_id,element,command,user_interface,value){if(command=="mceIESpell"){try{var ieSpell=new ActiveXObject("ieSpell.ieSpellExtension");ieSpell.CheckDocumentNode(tinyMCE.getInstanceById(editor_id).contentDocument.documentElement)}catch(e){if(e.number==-2146827859){if(confirm(tinyMCE.getLang("lang_iespell_download","",true)))window.open('http://www.iespell.com/download.php','ieSpellDownload','')}else alert("Error Loading ieSpell: Exception "+e.number)}return true}return false}};tinyMCE.addPlugin("iespell",TinyMCE_IESpellPlugin); \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/iespell/editor_plugin_src.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/iespell/editor_plugin_src.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,58 @@ +/** + * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $ + * + * @author Moxiecode + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +/* Import plugin specific language pack */ +tinyMCE.importPluginLanguagePack('iespell'); + +var TinyMCE_IESpellPlugin = { + getInfo : function() { + return { + longname : 'IESpell (MSIE Only)', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/iespell', + version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion + }; + }, + + /** + * Returns the HTML contents of the iespell control. + */ + getControlHTML : function(cn) { + // Is it the iespell control and is the brower MSIE. + if (cn == "iespell" && (tinyMCE.isMSIE && !tinyMCE.isOpera)) + return tinyMCE.getButtonHTML(cn, 'lang_iespell_desc', '{$pluginurl}/images/iespell.gif', 'mceIESpell'); + + return ""; + }, + + /** + * Executes the mceIESpell command. + */ + execCommand : function(editor_id, element, command, user_interface, value) { + // Handle ieSpellCommand + if (command == "mceIESpell") { + try { + var ieSpell = new ActiveXObject("ieSpell.ieSpellExtension"); + ieSpell.CheckDocumentNode(tinyMCE.getInstanceById(editor_id).contentDocument.documentElement); + } catch (e) { + if (e.number == -2146827859) { + if (confirm(tinyMCE.getLang("lang_iespell_download", "", true))) + window.open('http://www.iespell.com/download.php', 'ieSpellDownload', ''); + } else + alert("Error Loading ieSpell: Exception " + e.number); + } + + return true; + } + + // Pass to next handler in chain + return false; + } +}; + +tinyMCE.addPlugin("iespell", TinyMCE_IESpellPlugin); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/iespell/images/iespell.gif Binary file includes/clientside/tinymce/plugins/iespell/images/iespell.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/iespell/langs/en.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/iespell/langs/en.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,7 @@ +// UK lang variables + +tinyMCE.addToLang('',{ +iespell_desc : 'Run spell checking', +iespell_download : "ieSpell not detected. Click OK to go to download page." +}); + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/iespell/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/iespell/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +Check the TinyMCE documentation for details on this plugin. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/inlinepopups/css/inlinepopup.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/inlinepopups/css/inlinepopup.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,69 @@ +/* Window classes */ + +.mceWindow { + position: absolute; + left: 0; + top: 0; + border: 1px solid black; + background-color: #D4D0C8; +} + +.mceWindowHead { + background-color: #334F8D; + width: 100%; + height: 18px; + cursor: move; + overflow: hidden; +} + +.mceWindowBody { + clear: both; + background-color: white; +} + +.mceWindowStatusbar { + background-color: #D4D0C8; + height: 12px; + border-top: 1px solid black; +} + +.mceWindowTitle { + float: left; + font-family: "MS Sans Serif"; + font-size: 9pt; + font-weight: bold; + line-height: 18px; + color: white; + margin-left: 2px; + overflow: hidden; +} + +.mceWindowHeadTools { + margin-right: 2px; +} + +.mceWindowClose, .mceWindowMinimize, .mceWindowMaximize { + display: block; + float: right; + overflow: hidden; + margin-top: 2px; +} + +.mceWindowClose { + margin-left: 2px; +} + +.mceWindowMinimize { +} + +.mceWindowMaximize { +} + +.mceWindowResize { + display: block; + float: right; + overflow: hidden; + cursor: se-resize; + width: 12px; + height: 12px; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/inlinepopups/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/inlinepopups/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +var TinyMCE_InlinePopupsPlugin={getInfo:function(){return{longname:'Inline Popups',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/inlinepopups',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}}};tinyMCE.addPlugin("inlinepopups",TinyMCE_InlinePopupsPlugin);TinyMCE_Engine.prototype.orgOpenWindow=TinyMCE_Engine.prototype.openWindow;TinyMCE_Engine.prototype.orgCloseWindow=TinyMCE_Engine.prototype.closeWindow;TinyMCE_Engine.prototype.openWindow=function(template,args){if(args['inline']!="yes"||tinyMCE.isOpera||tinyMCE.getParam("plugins").indexOf('inlinepopups')==-1){mcWindows.selectedWindow=null;args['mce_inside_iframe']=false;this.orgOpenWindow(template,args);return}var url,resizable,scrollbars;args['mce_inside_iframe']=true;tinyMCE.windowArgs=args;if(template['file'].charAt(0)!='/'&&template['file'].indexOf('://')==-1)url=tinyMCE.baseURL+"/themes/"+tinyMCE.getParam("theme")+"/"+template['file'];else url=template['file'];if(!(width=parseInt(template['width'])))width=320;if(!(height=parseInt(template['height'])))height=200;if(!(minWidth=parseInt(template['minWidth'])))minWidth=100;if(!(minHeight=parseInt(template['minHeight'])))minHeight=100;resizable=(args&&args['resizable'])?args['resizable']:"no";scrollbars=(args&&args['scrollbars'])?args['scrollbars']:"no";height+=18;for(var name in args){if(typeof(args[name])=='function')continue;url=tinyMCE.replaceVar(url,name,escape(args[name]))}var elm=document.getElementById(this.selectedInstance.editorId+'_parent');if(tinyMCE.hasPlugin('fullscreen')&&this.selectedInstance.getData('fullscreen').enabled)pos={absLeft:0,absTop:0};else pos=tinyMCE.getAbsPosition(elm);pos.absLeft+=Math.round((elm.firstChild.clientWidth/ 2) - (width /2));pos.absTop+=Math.round((elm.firstChild.clientHeight/ 2) - (height /2));mcWindows.open(url,mcWindows.idCounter++,"modal=yes,width="+width+",height="+height+",resizable="+resizable+",scrollbars="+scrollbars+",statusbar="+resizable+",left="+pos.absLeft+",top="+pos.absTop+",minWidth="+minWidth+",minHeight="+minHeight)};TinyMCE_Engine.prototype.closeWindow=function(win){var gotit=false,n,w;for(n in mcWindows.windows){w=mcWindows.windows[n];if(typeof(w)=='function')continue;if(win.name==w.id+'_iframe'){w.close();gotit=true}}if(!gotit)this.orgCloseWindow(win);tinyMCE.selectedInstance.getWin().focus()};TinyMCE_Engine.prototype.setWindowTitle=function(win_ref,title){for(var n in mcWindows.windows){var win=mcWindows.windows[n];if(typeof(win)=='function')continue;if(win_ref.name==win.id+"_iframe")window.frames[win.id+"_iframe"].document.getElementById(win.id+'_title').innerHTML=title}};function TinyMCE_Windows(){this.settings=new Array();this.windows=new Array();this.isMSIE=(navigator.appName=="Microsoft Internet Explorer");this.isGecko=navigator.userAgent.indexOf('Gecko')!=-1;this.isSafari=navigator.userAgent.indexOf('Safari')!=-1;this.isMac=navigator.userAgent.indexOf('Mac')!=-1;this.isMSIE5_0=this.isMSIE&&(navigator.userAgent.indexOf('MSIE 5.0')!=-1);this.action="none";this.selectedWindow=null;this.lastSelectedWindow=null;this.zindex=1001;this.mouseDownScreenX=0;this.mouseDownScreenY=0;this.mouseDownLayerX=0;this.mouseDownLayerY=0;this.mouseDownWidth=0;this.mouseDownHeight=0;this.idCounter=0};TinyMCE_Windows.prototype.init=function(settings){this.settings=settings;if(this.isMSIE)this.addEvent(document,"mousemove",mcWindows.eventDispatcher);else this.addEvent(window,"mousemove",mcWindows.eventDispatcher);this.addEvent(document,"mouseup",mcWindows.eventDispatcher);this.addEvent(window,"resize",mcWindows.eventDispatcher);this.addEvent(document,"scroll",mcWindows.eventDispatcher);this.doc=document};TinyMCE_Windows.prototype.getBounds=function(){if(!this.bounds){var vp=tinyMCE.getViewPort(window);var top,left,bottom,right,docEl=this.doc.documentElement;top=vp.top;left=vp.left;bottom=vp.height+top-2;right=vp.width+left-22;this.bounds=[left,top,right,bottom]}return this.bounds};TinyMCE_Windows.prototype.clampBoxPosition=function(x,y,w,h,minW,minH){var bounds=this.getBounds();x=Math.max(bounds[0],Math.min(bounds[2],x+w)-w);y=Math.max(bounds[1],Math.min(bounds[3],y+h)-h);return this.clampBoxSize(x,y,w,h,minW,minH)};TinyMCE_Windows.prototype.clampBoxSize=function(x,y,w,h,minW,minH){var bounds=this.getBounds();return[x,y,Math.max(minW,Math.min(bounds[2],x+w)-x),Math.max(minH,Math.min(bounds[3],y+h)-y)]};TinyMCE_Windows.prototype.getParam=function(name,default_value){var value=null;value=(typeof(this.settings[name])=="undefined")?default_value:this.settings[name];if(value=="true"||value=="false")return(value=="true");return value};TinyMCE_Windows.prototype.eventDispatcher=function(e){e=typeof(e)=="undefined"?window.event:e;if(mcWindows.selectedWindow==null)return;if(mcWindows.isGecko&&e.type=="mousedown"){var elm=e.currentTarget;for(var n in mcWindows.windows){var win=mcWindows.windows[n];if(win.headElement==elm||win.resizeElement==elm){win.focus();break}}}switch(e.type){case"mousemove":mcWindows.selectedWindow.onMouseMove(e);break;case"mouseup":mcWindows.selectedWindow.onMouseUp(e);break;case"mousedown":mcWindows.selectedWindow.onMouseDown(e);break;case"focus":mcWindows.selectedWindow.onFocus(e);break;case"scroll":case"resize":if(mcWindows.clampUpdateTimeout)clearTimeout(mcWindows.clampUpdateTimeout);mcWindows.clampEventType=e.type;mcWindows.clampUpdateTimeout=setTimeout(function(){mcWindows.updateClamping()},100);break}};TinyMCE_Windows.prototype.updateClamping=function(){var clamp,oversize,etype=mcWindows.clampEventType;this.bounds=null;this.clampUpdateTimeout=null;for(var n in this.windows){win=this.windows[n];if(typeof(win)=='function'||!win.winElement)continue;clamp=mcWindows.clampBoxPosition(win.left,win.top,win.winElement.scrollWidth,win.winElement.scrollHeight,win.features.minWidth,win.features.minHeight);oversize=(clamp[2]!=win.winElement.scrollWidth||clamp[3]!=win.winElement.scrollHeight)?true:false;if(!oversize||win.features.resizable=="yes"||etype!="scroll")win.moveTo(clamp[0],clamp[1]);if(oversize&&win.features.resizable=="yes")win.resizeTo(clamp[2],clamp[3])}};TinyMCE_Windows.prototype.addEvent=function(obj,name,handler){if(this.isMSIE)obj.attachEvent("on"+name,handler);else obj.addEventListener(name,handler,false)};TinyMCE_Windows.prototype.cancelEvent=function(e){if(this.isMSIE){e.returnValue=false;e.cancelBubble=true}else e.preventDefault()};TinyMCE_Windows.prototype.parseFeatures=function(opts){opts=opts.toLowerCase();opts=opts.replace(/;/g,",");opts=opts.replace(/[^0-9a-z=,]/g,"");var optionChunks=opts.split(',');var options=new Array();options['left']="10";options['top']="10";options['width']="300";options['height']="300";options['minwidth']="100";options['minheight']="100";options['resizable']="yes";options['minimizable']="yes";options['maximizable']="yes";options['close']="yes";options['movable']="yes";options['statusbar']="yes";options['scrollbars']="auto";options['modal']="no";if(opts=="")return options;for(var i=0;i';html+='';html+='';html+='Wrapper iframe';html+='';html+='';html+='';html+='';html+='
';html+='
';html+='
';if(features['resizable']=="yes"&&features['maximizable']=="yes")html+=' ';html+='
';html+='
';html+='
';if(features['statusbar']=="yes"){html+='
';if(features['resizable']=="yes"){if(this.isGecko)html+='
';else html+='
'}html+='
'}html+='
';html+='';html+='';this.createFloatingIFrame(id,features['left'],features['top'],features['width'],features['height'],html)};TinyMCE_Windows.prototype.setDocumentLock=function(state){var elm=document.getElementById('mcWindowEventBlocker');if(state){if(elm==null){elm=document.createElement("div");elm.id="mcWindowEventBlocker";elm.style.position="absolute";elm.style.left="0";elm.style.top="0";document.body.appendChild(elm)}elm.style.display="none";var imgPath=this.getParam("images_path");var width=document.body.clientWidth;var height=document.body.clientHeight;elm.style.width=width;elm.style.height=height;elm.innerHTML='';elm.style.zIndex=mcWindows.zindex-1;elm.style.display="block"}else if(elm!=null){if(mcWindows.windows.length==0)elm.parentNode.removeChild(elm);else elm.style.zIndex=mcWindows.zindex-1}};TinyMCE_Windows.prototype.onLoad=function(name){var win=mcWindows.windows[name];var id="mcWindow_"+name;var wrapperIframe=window.frames[id+"_iframe"].frames[0];var wrapperDoc=window.frames[id+"_iframe"].document;var doc=window.frames[id+"_iframe"].document;var winDiv=document.getElementById("mcWindow_"+name+"_div");var realIframe=window.frames[id+"_iframe"].frames[0];win.id="mcWindow_"+name;win.winElement=winDiv;win.bodyElement=doc.getElementById(id+'_body');win.iframeElement=doc.getElementById(id+'_iframe');win.headElement=doc.getElementById(id+'_head');win.titleElement=doc.getElementById(id+'_title');win.resizeElement=doc.getElementById(id+'_resize');win.containerElement=doc.getElementById(id+'_container');win.left=win.features['left'];win.top=win.features['top'];win.frame=window.frames[id+'_iframe'].frames[0];win.wrapperFrame=window.frames[id+'_iframe'];win.wrapperIFrameElement=document.getElementById(id+"_iframe");mcWindows.addEvent(win.headElement,"mousedown",mcWindows.eventDispatcher);if(win.resizeElement!=null)mcWindows.addEvent(win.resizeElement,"mousedown",mcWindows.eventDispatcher);if(mcWindows.isMSIE){mcWindows.addEvent(realIframe.document,"mousemove",mcWindows.eventDispatcher);mcWindows.addEvent(realIframe.document,"mouseup",mcWindows.eventDispatcher)}else{mcWindows.addEvent(realIframe,"mousemove",mcWindows.eventDispatcher);mcWindows.addEvent(realIframe,"mouseup",mcWindows.eventDispatcher);mcWindows.addEvent(realIframe,"focus",mcWindows.eventDispatcher)}for(var i=0;i'; + html += ''; + html += ''; + + html += '
'; + html += '
'; + html += '
'; + if (features['resizable'] == "yes" && features['maximizable'] == "yes") + html += ' '; + // html += ' '; + html += '
'; + html += '
'; + html += '
'; + + if (features['statusbar'] == "yes") { + html += '
'; + + if (features['resizable'] == "yes") { + if (this.isGecko) + html += '
'; + else + html += '
'; + } + + html += '
'; + } + + html += '
'; + + html += ''; + html += ''; + + // Create iframe + this.createFloatingIFrame(id, features['left'], features['top'], features['width'], features['height'], html); +}; + +// Blocks the document events by placing a image over the whole document +TinyMCE_Windows.prototype.setDocumentLock = function(state) { + var elm = document.getElementById('mcWindowEventBlocker'); + + if (state) { + if (elm == null) { + elm = document.createElement("div"); + + elm.id = "mcWindowEventBlocker"; + elm.style.position = "absolute"; + elm.style.left = "0"; + elm.style.top = "0"; + + document.body.appendChild(elm); + } + + elm.style.display = "none"; + + var imgPath = this.getParam("images_path"); + var width = document.body.clientWidth; + var height = document.body.clientHeight; + + elm.style.width = width; + elm.style.height = height; + elm.innerHTML = ''; + + elm.style.zIndex = mcWindows.zindex-1; + elm.style.display = "block"; + } else if (elm != null) { + if (mcWindows.windows.length == 0) + elm.parentNode.removeChild(elm); + else + elm.style.zIndex = mcWindows.zindex-1; + } +}; + +// Gets called when wrapper iframe is initialized +TinyMCE_Windows.prototype.onLoad = function(name) { + var win = mcWindows.windows[name]; + var id = "mcWindow_" + name; + var wrapperIframe = window.frames[id + "_iframe"].frames[0]; + var wrapperDoc = window.frames[id + "_iframe"].document; + var doc = window.frames[id + "_iframe"].document; + var winDiv = document.getElementById("mcWindow_" + name + "_div"); + var realIframe = window.frames[id + "_iframe"].frames[0]; + + // Set window data + win.id = "mcWindow_" + name; + win.winElement = winDiv; + win.bodyElement = doc.getElementById(id + '_body'); + win.iframeElement = doc.getElementById(id + '_iframe'); + win.headElement = doc.getElementById(id + '_head'); + win.titleElement = doc.getElementById(id + '_title'); + win.resizeElement = doc.getElementById(id + '_resize'); + win.containerElement = doc.getElementById(id + '_container'); + win.left = win.features['left']; + win.top = win.features['top']; + win.frame = window.frames[id + '_iframe'].frames[0]; + win.wrapperFrame = window.frames[id + '_iframe']; + win.wrapperIFrameElement = document.getElementById(id + "_iframe"); + + // Add event handlers + mcWindows.addEvent(win.headElement, "mousedown", mcWindows.eventDispatcher); + + if (win.resizeElement != null) + mcWindows.addEvent(win.resizeElement, "mousedown", mcWindows.eventDispatcher); + + if (mcWindows.isMSIE) { + mcWindows.addEvent(realIframe.document, "mousemove", mcWindows.eventDispatcher); + mcWindows.addEvent(realIframe.document, "mouseup", mcWindows.eventDispatcher); + } else { + mcWindows.addEvent(realIframe, "mousemove", mcWindows.eventDispatcher); + mcWindows.addEvent(realIframe, "mouseup", mcWindows.eventDispatcher); + mcWindows.addEvent(realIframe, "focus", mcWindows.eventDispatcher); + } + + for (var i=0; i'; + + html += '
'; + html += '
'; + html += '
'; +// html += ' '; +// html += ' '; + html += '
'; + html += '
'; + html += '
'; + html += '
'; + html += '
'; + html += '
'; + html += '
'; + + html += ''; + html += ''; + + // Create iframe + this.createFloatingIFrame(id, features['left'], features['top'], features['width'], features['height'], html); +}; + +// Gets called when wrapper iframe is initialized +MCWindows.prototype.onLoad = function(name) { + var win = mcWindows.windows[name]; + var id = "mcWindow_" + name; + var wrapperIframe = window.frames[id + "_iframe"].frames[0]; + var wrapperDoc = window.frames[id + "_iframe"].document; + var doc = window.frames[id + "_iframe"].document; + var winDiv = document.getElementById("mcWindow_" + name + "_div"); + var realIframe = window.frames[id + "_iframe"].frames[0]; + + // Set window data + win.id = "mcWindow_" + name + "_iframe"; + win.winElement = winDiv; + win.bodyElement = doc.getElementById(id + '_body'); + win.iframeElement = doc.getElementById(id + '_iframe'); + win.headElement = doc.getElementById(id + '_head'); + win.titleElement = doc.getElementById(id + '_title'); + win.resizeElement = doc.getElementById(id + '_resize'); + win.containerElement = doc.getElementById(id + '_container'); + win.left = win.features['left']; + win.top = win.features['top']; + win.frame = window.frames[id + '_iframe'].frames[0]; + win.wrapperFrame = window.frames[id + '_iframe']; + win.wrapperIFrameElement = document.getElementById(id + "_iframe"); + + // Add event handlers + mcWindows.addEvent(win.headElement, "mousedown", mcWindows.eventDispatcher); + mcWindows.addEvent(win.resizeElement, "mousedown", mcWindows.eventDispatcher); + + if (mcWindows.isMSIE) { + mcWindows.addEvent(realIframe.document, "mousemove", mcWindows.eventDispatcher); + mcWindows.addEvent(realIframe.document, "mouseup", mcWindows.eventDispatcher); + } else { + mcWindows.addEvent(realIframe, "mousemove", mcWindows.eventDispatcher); + mcWindows.addEvent(realIframe, "mouseup", mcWindows.eventDispatcher); + mcWindows.addEvent(realIframe, "focus", mcWindows.eventDispatcher); + } + + for (var i=0; i-1){nl[ci].style.zIndex=z[fi];nl[fi].style.zIndex=z[ci]}else{if(z[ci]>0)nl[ci].style.zIndex=z[ci]-1}}else{for(i=0;iz[ci]){fi=i;break}}if(fi>-1){nl[ci].style.zIndex=z[fi];nl[fi].style.zIndex=z[ci]}else nl[ci].style.zIndex=z[ci]+1}inst.repaint()},_getParentLayer:function(n){return tinyMCE.getParentNode(n,function(n){return n.nodeType==1&&new RegExp('absolute|relative|static','gi').test(n.style.position)})},_insertLayer:function(){var inst=tinyMCE.selectedInstance;var e=tinyMCE.getParentElement(inst.getFocusElement());var p=tinyMCE.getAbsPosition(e);var d=inst.getDoc();var ne=d.createElement('div');var h=inst.selection.getSelectedHTML();ne.style.position='absolute';ne.style.left=p.absLeft+'px';ne.style.top=(p.absTop>20?p.absTop:20)+'px';ne.style.width='100px';ne.style.height='100px';ne.className='mceVisualAid';if(!h)h=tinyMCE.getLang('lang_layer_content');ne.innerHTML=h;d.body.appendChild(ne)},_toggleAbsolute:function(){var inst=tinyMCE.selectedInstance;var le=this._getParentLayer(inst.getFocusElement());if(le==null)le=tinyMCE.getParentElement(inst.getFocusElement(),'div,p,img');if(le){if(le.style.position.toLowerCase()=="absolute"){le.style.position="";le.style.left="";le.style.top=""}else{le.style.position="absolute";if(le.style.left=="")le.style.left=20+'px';if(le.style.top=="")le.style.top=20+'px';if(le.style.width=="")le.style.width=le.width?(le.width+'px'):'100px';if(le.style.height=="")le.style.height=le.height?(le.height+'px'):'100px';tinyMCE.handleVisualAid(inst.getBody(),true,inst.visualAid,inst)}inst.repaint();tinyMCE.triggerNodeChange()}}};tinyMCE.addPlugin("layer",TinyMCE_LayerPlugin); \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/layer/editor_plugin_src.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/layer/editor_plugin_src.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,248 @@ +/** + * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $ + * + * @author Moxiecode + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +/* Import plugin specific language pack */ +tinyMCE.importPluginLanguagePack('layer'); + +var TinyMCE_LayerPlugin = { + getInfo : function() { + return { + longname : 'Layer', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/layer', + version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion + }; + }, + + initInstance : function(inst) { + if (tinyMCE.isMSIE && !tinyMCE.isOpera) + inst.getDoc().execCommand('2D-Position'); + }, + + handleEvent : function(e) { + var inst = tinyMCE.selectedInstance; + var w = inst.getWin(), le = inst._lastStyleElm, e; + + if (tinyMCE.isGecko) { + e = this._getParentLayer(inst.getFocusElement()); + + if (e) { + if (!inst._lastStyleElm) { + e.style.overflow = 'auto'; + inst._lastStyleElm = e; + } + } else if (le) { + le = inst._lastStyleElm; + le.style.width = le.scrollWidth + 'px'; + le.style.height = le.scrollHeight + 'px'; + le.style.overflow = ''; + inst._lastStyleElm = null; + } + } + + return true; + }, + + handleVisualAid : function(el, deep, state, inst) { + var nl = inst.getDoc().getElementsByTagName("div"), i; + + for (i=0; i -1) { + nl[ci].style.zIndex = z[fi]; + nl[fi].style.zIndex = z[ci]; + } else { + if (z[ci] > 0) + nl[ci].style.zIndex = z[ci] - 1; + } + } else { + // Move forward + + // Try find a higher one + for (i=0; i z[ci]) { + fi = i; + break; + } + } + + if (fi > -1) { + nl[ci].style.zIndex = z[fi]; + nl[fi].style.zIndex = z[ci]; + } else + nl[ci].style.zIndex = z[ci] + 1; + } + + inst.repaint(); + }, + + _getParentLayer : function(n) { + return tinyMCE.getParentNode(n, function(n) { + return n.nodeType == 1 && new RegExp('absolute|relative|static', 'gi').test(n.style.position); + }); + }, + + _insertLayer : function() { + var inst = tinyMCE.selectedInstance; + var e = tinyMCE.getParentElement(inst.getFocusElement()); + var p = tinyMCE.getAbsPosition(e); + var d = inst.getDoc(); + var ne = d.createElement('div'); + var h = inst.selection.getSelectedHTML(); + + // Move div + ne.style.position = 'absolute'; + ne.style.left = p.absLeft + 'px'; + ne.style.top = (p.absTop > 20 ? p.absTop : 20) + 'px'; + ne.style.width = '100px'; + ne.style.height = '100px'; + ne.className = 'mceVisualAid'; + + if (!h) + h = tinyMCE.getLang('lang_layer_content'); + + ne.innerHTML = h; + + // Add it + d.body.appendChild(ne); + }, + + _toggleAbsolute : function() { + var inst = tinyMCE.selectedInstance; + var le = this._getParentLayer(inst.getFocusElement()); + + if (le == null) + le = tinyMCE.getParentElement(inst.getFocusElement(), 'div,p,img'); + + if (le) { + if (le.style.position.toLowerCase() == "absolute") { + le.style.position = ""; + le.style.left = ""; + le.style.top = ""; + } else { + le.style.position = "absolute"; + + if (le.style.left == "") + le.style.left = 20 + 'px'; + + if (le.style.top == "") + le.style.top = 20 + 'px'; + + if (le.style.width == "") + le.style.width = le.width ? (le.width + 'px') : '100px'; + + if (le.style.height == "") + le.style.height = le.height ? (le.height + 'px') : '100px'; + + tinyMCE.handleVisualAid(inst.getBody(), true, inst.visualAid, inst); + } + + inst.repaint(); + tinyMCE.triggerNodeChange(); + } + } +}; + +tinyMCE.addPlugin("layer", TinyMCE_LayerPlugin); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/layer/images/absolute.gif Binary file includes/clientside/tinymce/plugins/layer/images/absolute.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/layer/images/backward.gif Binary file includes/clientside/tinymce/plugins/layer/images/backward.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/layer/images/forward.gif Binary file includes/clientside/tinymce/plugins/layer/images/forward.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/layer/images/insert_layer.gif Binary file includes/clientside/tinymce/plugins/layer/images/insert_layer.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/layer/images/insertlayer.gif Binary file includes/clientside/tinymce/plugins/layer/images/insertlayer.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/layer/images/movebackward.gif Binary file includes/clientside/tinymce/plugins/layer/images/movebackward.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/layer/images/moveforward.gif Binary file includes/clientside/tinymce/plugins/layer/images/moveforward.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/layer/langs/en.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/layer/langs/en.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,9 @@ +// UK lang variables + +tinyMCE.addToLang('layer',{ +insertlayer_desc : 'Insert new layer', +forward_desc : 'Move forward', +backward_desc : 'Move backward', +absolute_desc : 'Toggle absolute positioning', +content : 'New layer...' +}); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/layer/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/layer/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +Check the TinyMCE documentation for details on this plugin. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/media/css/content.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/media/css/content.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,26 @@ +.mceItemFlash, .mceItemShockWave, .mceItemQuickTime, .mceItemWindowsMedia, .mceItemRealMedia { + border: 1px dotted #cc0000; + background-position: center; + background-repeat: no-repeat; + background-color: #ffffcc; +} + +.mceItemShockWave { + background-image: url('../images/shockwave.gif'); +} + +.mceItemFlash { + background-image: url('../images/flash.gif'); +} + +.mceItemQuickTime { + background-image: url('../images/quicktime.gif'); +} + +.mceItemWindowsMedia { + background-image: url('../images/windowsmedia.gif'); +} + +.mceItemRealMedia { + background-image: url('../images/realmedia.gif'); +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/media/css/media.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/media/css/media.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,68 @@ +#id, #name, #hspace, #vspace, #class_name, #align { + width: 100px; +} + +#hspace, #vspace { + width: 50px; +} + +#flash_quality, #flash_align, #flash_scale, #flash_salign, #flash_wmode { + width: 100px; +} + +#flash_base, #flash_flashvars { + width: 240px; +} + +#width, #height { + width: 40px; +} + +#src, #media_type { + width: 250px; +} + +#class { + width: 120px; +} + +#prev { + margin: 0; + border: 1px solid black; + width: 99%; + height: 230px; + overflow: auto; +} + +.panel_wrapper div.current { + height: 390px; + overflow: auto; +} + +#flash_options, #shockwave_options, #qt_options, #wmp_options, #rmp_options { + display: none; +} + +.mceAddSelectValue { + background-color: #DDDDDD; +} + +#qt_starttime, #qt_endtime, #qt_fov, #qt_href, #qt_moveid, #qt_moviename, #qt_node, #qt_pan, #qt_qtsrc, #qt_qtsrcchokespeed, #qt_target, #qt_tilt, #qt_urlsubstituten, #qt_volume { + width: 70px; +} + +#wmp_balance, #wmp_baseurl, #wmp_captioningid, #wmp_currentmarker, #wmp_currentposition, #wmp_defaultframe, #wmp_playcount, #wmp_rate, #wmp_uimode, #wmp_volume { + width: 70px; +} + +#rmp_console, #rmp_numloop, #rmp_controls, #rmp_scriptcallbacks { + width: 70px; +} + +#shockwave_swvolume, #shockwave_swframe, #shockwave_swurl, #shockwave_swstretchvalign, #shockwave_swstretchhalign, #shockwave_swstretchstyle { + width: 90px; +} + +#qt_qtsrc { + width: 200px; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/media/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/media/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('media');var TinyMCE_MediaPlugin={getInfo:function(){return{longname:'Media',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/media',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},initInstance:function(inst){if(inst.hasPlugin('flash')&&!tinyMCE.flashWarn){alert('Flash plugin is deprecated and should not be used together with the media plugin.');tinyMCE.flashWarn=true}if(!tinyMCE.settings['media_skip_plugin_css'])tinyMCE.importCSS(inst.getDoc(),tinyMCE.baseURL+"/plugins/media/css/content.css")},getControlHTML:function(cn){switch(cn){case"media":return tinyMCE.getButtonHTML(cn,'lang_media_desc','{$pluginurl}/images/media.gif','mceMedia')}return""},execCommand:function(editor_id,element,command,user_interface,value){switch(command){case"mceMedia":tinyMCE.openWindow({file:'../../plugins/media/media.htm',width:430+tinyMCE.getLang('lang_media_delta_width',0),height:470+tinyMCE.getLang('lang_media_delta_height',0)},{editor_id:editor_id,inline:"yes"});return true}return false},cleanup:function(type,content,inst){var nl,img,i,ne,d,s,ci;switch(type){case"insert_to_editor":img=tinyMCE.getParam("theme_href")+'/images/spacer.gif';content=content.replace(/]*>\s*write(Flash|ShockWave|WindowsMedia|QuickTime|RealMedia)\(\{([^\)]*)\}\);\s*<\/script>/gi,'');content=content.replace(/]*)>/gi,'
');content=content.replace(/]*)>/gi,'
');content=content.replace(/<\/(object|embed)([^>]*)>/gi,'
');content=content.replace(/]*)>/gi,'
');content=content.replace(new RegExp('\\/ class="mceItemParam"><\\/div>','gi'),'class="mceItemParam">
');break;case"insert_to_editor_dom":d=inst.getDoc();nl=content.getElementsByTagName("img");for(i=0;i',startPos);attribs=TinyMCE_MediaPlugin._parseAttributes(content.substring(startPos+4,endPos));if(!/mceItem(Flash|ShockWave|WindowsMedia|QuickTime|RealMedia)/.test(attribs['class']))continue;endPos+=2;at=attribs['title'];if(at){at=at.replace(/&(#39|apos);/g,"'");at=at.replace(/&#quot;/g,'"');try{pl=eval('x={'+at+'};')}catch(ex){pl={}}}if(!tinyMCE.getParam('media_use_script',false)){switch(attribs['class']){case'mceItemFlash':ci='d27cdb6e-ae6d-11cf-96b8-444553540000';cb='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0';mt='application/x-shockwave-flash';break;case'mceItemShockWave':ci='166B1BCA-3F9C-11CF-8075-444553540000';cb='http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0';mt='application/x-director';break;case'mceItemWindowsMedia':ci=tinyMCE.getParam('media_wmp6_compatible')?'05589FA1-C356-11CE-BF01-00AA0055595A':'6BF52A52-394A-11D3-B153-00C04F79FAA6';cb='http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701';mt='application/x-mplayer2';break;case'mceItemQuickTime':ci='02BF25D5-8C17-4B23-BC80-D3488ABDDC6B';cb='http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0';mt='video/quicktime';break;case'mceItemRealMedia':ci='CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA';cb='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0';mt='audio/x-pn-realaudio-plugin';break}if(!tinyMCE.getParam("relative_urls"))pl.src=tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'],pl.src);embedHTML=TinyMCE_MediaPlugin._getEmbed(ci,cb,mt,pl,attribs)}else{switch(attribs['class']){case'mceItemFlash':s='writeFlash';break;case'mceItemShockWave':s='writeShockWave';break;case'mceItemWindowsMedia':s='writeWindowsMedia';break;case'mceItemQuickTime':s='writeQuickTime';break;case'mceItemRealMedia':s='writeRealMedia';break}if(attribs.width)at=at.replace(/width:[^0-9]?[0-9]+%?[^0-9]?/g,"width:'"+attribs.width+"'");if(attribs.height)at=at.replace(/height:[^0-9]?[0-9]+%?[^0-9]?/g,"height:'"+attribs.height+"'");if(!tinyMCE.getParam("relative_urls")){pl.src=tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'],pl.src);at=at.replace(new RegExp("src:'[^']*'","g"),"src:'"+pl.src+"'")}embedHTML=''}chunkBefore=content.substring(0,startPos);chunkAfter=content.substring(endPos);content=chunkBefore+embedHTML+chunkAfter}break}return content},handleNodeChange:function(editor_id,node,undo_index,undo_levels,visual_aid,any_selection){if(node==null)return;do{if(node.nodeName=="IMG"&&/mceItem(Flash|ShockWave|WindowsMedia|QuickTime|RealMedia)/.test(tinyMCE.getAttrib(node,'class'))){tinyMCE.switchClass(editor_id+'_media','mceButtonSelected');return true}}while((node=node.parentNode));tinyMCE.switchClass(editor_id+'_media','mceButtonNormal');return true},_createImgFromEmbed:function(n,d,cl){var ne,at,i,ti='',an;ne=d.createElement('img');ne.src=tinyMCE.getParam("theme_href")+'/images/spacer.gif';ne.width=tinyMCE.getAttrib(n,'width');ne.height=tinyMCE.getAttrib(n,'height');ne.className=cl;at=n.attributes;for(i=0;i0?ti.substring(0,ti.length-1):ti;ne.title=ti;n.parentNode.replaceChild(ne,n)},_createImg:function(cl,d,n){var i,nl,ti="",an,av,al=new Array();ne=d.createElement('img');ne.src=tinyMCE.getParam("theme_href")+'/images/spacer.gif';ne.width=tinyMCE.getAttrib(n,'width');ne.height=tinyMCE.getAttrib(n,'height');ne.className=cl;al.id=tinyMCE.getAttrib(n,'id');al.name=tinyMCE.getAttrib(n,'name');al.width=tinyMCE.getAttrib(n,'width');al.height=tinyMCE.getAttrib(n,'height');al.bgcolor=tinyMCE.getAttrib(n,'bgcolor');al.align=tinyMCE.getAttrib(n,'align');al.class_name=tinyMCE.getAttrib(n,'mce_class');nl=n.getElementsByTagName('div');for(i=0;i0?ti.substring(0,ti.length-1):ti;ne.title=ti;return ne},_getEmbed:function(cls,cb,mt,p,at){var h='',n;p.width=at.width?at.width:p.width;p.height=at.height?at.height:p.height;h+='';if(n=='src'&&p[n].indexOf('://')!=-1&&mt=='application/x-mplayer2')h+=''}}h+=']*>\s*write(Flash|ShockWave|WindowsMedia|QuickTime|RealMedia)\(\{([^\)]*)\}\);\s*<\/script>/gi, ''); + content = content.replace(/]*)>/gi, '
'); + content = content.replace(/]*)>/gi, '
'); + content = content.replace(/<\/(object|embed)([^>]*)>/gi, '
'); + content = content.replace(/]*)>/gi, '
'); + content = content.replace(new RegExp('\\/ class="mceItemParam"><\\/div>', 'gi'), 'class="mceItemParam">
'); + break; + + case "insert_to_editor_dom": + d = inst.getDoc(); + nl = content.getElementsByTagName("img"); + for (i=0; i', startPos); + attribs = TinyMCE_MediaPlugin._parseAttributes(content.substring(startPos + 4, endPos)); + + // Is not flash, skip it + if (!/mceItem(Flash|ShockWave|WindowsMedia|QuickTime|RealMedia)/.test(attribs['class'])) + continue; + + endPos += 2; + + // Parse attributes + at = attribs['title']; + if (at) { + at = at.replace(/&(#39|apos);/g, "'"); + at = at.replace(/&#quot;/g, '"'); + + try { + pl = eval('x={' + at + '};'); + } catch (ex) { + pl = {}; + } + } + + // Use object/embed + if (!tinyMCE.getParam('media_use_script', false)) { + switch (attribs['class']) { + case 'mceItemFlash': + ci = 'd27cdb6e-ae6d-11cf-96b8-444553540000'; + cb = 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0'; + mt = 'application/x-shockwave-flash'; + break; + + case 'mceItemShockWave': + ci = '166B1BCA-3F9C-11CF-8075-444553540000'; + cb = 'http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0'; + mt = 'application/x-director'; + break; + + case 'mceItemWindowsMedia': + ci = tinyMCE.getParam('media_wmp6_compatible') ? '05589FA1-C356-11CE-BF01-00AA0055595A' : '6BF52A52-394A-11D3-B153-00C04F79FAA6'; + cb = 'http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701'; + mt = 'application/x-mplayer2'; + break; + + case 'mceItemQuickTime': + ci = '02BF25D5-8C17-4B23-BC80-D3488ABDDC6B'; + cb = 'http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0'; + mt = 'video/quicktime'; + break; + + case 'mceItemRealMedia': + ci = 'CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA'; + cb = 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0'; + mt = 'audio/x-pn-realaudio-plugin'; + break; + } + + // Force absolute URL + if (!tinyMCE.getParam("relative_urls")) + pl.src = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], pl.src); + + embedHTML = TinyMCE_MediaPlugin._getEmbed(ci, cb, mt, pl, attribs); + } else { + // Use script version + switch (attribs['class']) { + case 'mceItemFlash': + s = 'writeFlash'; + break; + + case 'mceItemShockWave': + s = 'writeShockWave'; + break; + + case 'mceItemWindowsMedia': + s = 'writeWindowsMedia'; + break; + + case 'mceItemQuickTime': + s = 'writeQuickTime'; + break; + + case 'mceItemRealMedia': + s = 'writeRealMedia'; + break; + } + + if (attribs.width) + at = at.replace(/width:[^0-9]?[0-9]+%?[^0-9]?/g, "width:'" + attribs.width + "'"); + + if (attribs.height) + at = at.replace(/height:[^0-9]?[0-9]+%?[^0-9]?/g, "height:'" + attribs.height + "'"); + + // Force absolute URL + if (!tinyMCE.getParam("relative_urls")) { + pl.src = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], pl.src); + at = at.replace(new RegExp("src:'[^']*'", "g"), "src:'" + pl.src + "'"); + } + + embedHTML = ''; + } + + // Insert embed/object chunk + chunkBefore = content.substring(0, startPos); + chunkAfter = content.substring(endPos); + content = chunkBefore + embedHTML + chunkAfter; + } + break; + } + + return content; + }, + + handleNodeChange : function(editor_id, node, undo_index, undo_levels, visual_aid, any_selection) { + if (node == null) + return; + + do { + if (node.nodeName == "IMG" && /mceItem(Flash|ShockWave|WindowsMedia|QuickTime|RealMedia)/.test(tinyMCE.getAttrib(node, 'class'))) { + tinyMCE.switchClass(editor_id + '_media', 'mceButtonSelected'); + return true; + } + } while ((node = node.parentNode)); + + tinyMCE.switchClass(editor_id + '_media', 'mceButtonNormal'); + + return true; + }, + + _createImgFromEmbed : function(n, d, cl) { + var ne, at, i, ti = '', an; + + ne = d.createElement('img'); + ne.src = tinyMCE.getParam("theme_href") + '/images/spacer.gif'; + ne.width = tinyMCE.getAttrib(n, 'width'); + ne.height = tinyMCE.getAttrib(n, 'height'); + ne.className = cl; + + at = n.attributes; + for (i=0; i 0 ? ti.substring(0, ti.length - 1) : ti; + ne.title = ti; + + n.parentNode.replaceChild(ne, n); + }, + + _createImg : function(cl, d, n) { + var i, nl, ti = "", an, av, al = new Array(); + + ne = d.createElement('img'); + ne.src = tinyMCE.getParam("theme_href") + '/images/spacer.gif'; + ne.width = tinyMCE.getAttrib(n, 'width'); + ne.height = tinyMCE.getAttrib(n, 'height'); + ne.className = cl; + + al.id = tinyMCE.getAttrib(n, 'id'); + al.name = tinyMCE.getAttrib(n, 'name'); + al.width = tinyMCE.getAttrib(n, 'width'); + al.height = tinyMCE.getAttrib(n, 'height'); + al.bgcolor = tinyMCE.getAttrib(n, 'bgcolor'); + al.align = tinyMCE.getAttrib(n, 'align'); + al.class_name = tinyMCE.getAttrib(n, 'mce_class'); + + nl = n.getElementsByTagName('div'); + for (i=0; i 0 ? ti.substring(0, ti.length - 1) : ti; + ne.title = ti; + + return ne; + }, + + _getEmbed : function(cls, cb, mt, p, at) { + var h = '', n; + + p.width = at.width ? at.width : p.width; + p.height = at.height ? at.height : p.height; + + h += ''; + + // Add extra url parameter if it's an absolute URL on WMP + if (n == 'src' && p[n].indexOf('://') != -1 && mt == 'application/x-mplayer2') + h += ''; + } + } + + h += ''; + + h += ''); +} + +function init() { + var pl = "", f, val; + var type = "flash", fe, i; + + tinyMCEPopup.resizeToInnerSize(); + f = document.forms[0] + + fe = tinyMCE.selectedInstance.getFocusElement(); + if (/mceItem(Flash|ShockWave|WindowsMedia|QuickTime|RealMedia)/.test(tinyMCE.getAttrib(fe, 'class'))) { + pl = "x={" + fe.title + "};"; + + switch (tinyMCE.getAttrib(fe, 'class')) { + case 'mceItemFlash': + type = 'flash'; + break; + + case 'mceItemShockWave': + type = 'shockwave'; + break; + + case 'mceItemWindowsMedia': + type = 'wmp'; + break; + + case 'mceItemQuickTime': + type = 'qt'; + break; + + case 'mceItemRealMedia': + type = 'rmp'; + break; + } + + document.forms[0].insert.value = tinyMCE.getLang('lang_update', 'Insert', true); + } + + document.getElementById('filebrowsercontainer').innerHTML = getBrowserHTML('filebrowser','src','media','media'); + document.getElementById('qtsrcfilebrowsercontainer').innerHTML = getBrowserHTML('qtsrcfilebrowser','qt_qtsrc','media','media'); + document.getElementById('bgcolor_pickcontainer').innerHTML = getColorPickerHTML('bgcolor_pick','bgcolor'); + + var html = getMediaListHTML('filebrowser','src','media','media'); + if (html == "") + document.getElementById("linklistrow").style.display = 'none'; + else + document.getElementById("linklistcontainer").innerHTML = html; + + // Resize some elements + if (isVisible('filebrowsercontainer')) + document.getElementById('src').style.width = '230px'; + + // Setup form + if (pl != "") { + pl = eval(pl); + + switch (type) { + case "flash": + setBool(pl, 'flash', 'play'); + setBool(pl, 'flash', 'loop'); + setBool(pl, 'flash', 'menu'); + setBool(pl, 'flash', 'swliveconnect'); + setStr(pl, 'flash', 'quality'); + setStr(pl, 'flash', 'scale'); + setStr(pl, 'flash', 'salign'); + setStr(pl, 'flash', 'wmode'); + setStr(pl, 'flash', 'base'); + setStr(pl, 'flash', 'flashvars'); + break; + + case "qt": + setBool(pl, 'qt', 'loop'); + setBool(pl, 'qt', 'autoplay'); + setBool(pl, 'qt', 'cache'); + setBool(pl, 'qt', 'controller'); + setBool(pl, 'qt', 'correction'); + setBool(pl, 'qt', 'enablejavascript'); + setBool(pl, 'qt', 'kioskmode'); + setBool(pl, 'qt', 'autohref'); + setBool(pl, 'qt', 'playeveryframe'); + setBool(pl, 'qt', 'tarsetcache'); + setStr(pl, 'qt', 'scale'); + setStr(pl, 'qt', 'starttime'); + setStr(pl, 'qt', 'endtime'); + setStr(pl, 'qt', 'tarset'); + setStr(pl, 'qt', 'qtsrcchokespeed'); + setStr(pl, 'qt', 'volume'); + setStr(pl, 'qt', 'qtsrc'); + break; + + case "shockwave": + setBool(pl, 'shockwave', 'sound'); + setBool(pl, 'shockwave', 'progress'); + setBool(pl, 'shockwave', 'autostart'); + setBool(pl, 'shockwave', 'swliveconnect'); + setStr(pl, 'shockwave', 'swvolume'); + setStr(pl, 'shockwave', 'swstretchstyle'); + setStr(pl, 'shockwave', 'swstretchhalign'); + setStr(pl, 'shockwave', 'swstretchvalign'); + break; + + case "wmp": + setBool(pl, 'wmp', 'autostart'); + setBool(pl, 'wmp', 'enabled'); + setBool(pl, 'wmp', 'enablecontextmenu'); + setBool(pl, 'wmp', 'fullscreen'); + setBool(pl, 'wmp', 'invokeurls'); + setBool(pl, 'wmp', 'mute'); + setBool(pl, 'wmp', 'stretchtofit'); + setBool(pl, 'wmp', 'windowlessvideo'); + setStr(pl, 'wmp', 'balance'); + setStr(pl, 'wmp', 'baseurl'); + setStr(pl, 'wmp', 'captioningid'); + setStr(pl, 'wmp', 'currentmarker'); + setStr(pl, 'wmp', 'currentposition'); + setStr(pl, 'wmp', 'defaultframe'); + setStr(pl, 'wmp', 'playcount'); + setStr(pl, 'wmp', 'rate'); + setStr(pl, 'wmp', 'uimode'); + setStr(pl, 'wmp', 'volume'); + break; + + case "rmp": + setBool(pl, 'rmp', 'autostart'); + setBool(pl, 'rmp', 'loop'); + setBool(pl, 'rmp', 'autogotourl'); + setBool(pl, 'rmp', 'center'); + setBool(pl, 'rmp', 'imagestatus'); + setBool(pl, 'rmp', 'maintainaspect'); + setBool(pl, 'rmp', 'nojava'); + setBool(pl, 'rmp', 'prefetch'); + setBool(pl, 'rmp', 'shuffle'); + setStr(pl, 'rmp', 'console'); + setStr(pl, 'rmp', 'controls'); + setStr(pl, 'rmp', 'numloop'); + setStr(pl, 'rmp', 'scriptcallbacks'); + break; + } + + setStr(pl, null, 'src'); + setStr(pl, null, 'id'); + setStr(pl, null, 'name'); + setStr(pl, null, 'vspace'); + setStr(pl, null, 'hspace'); + setStr(pl, null, 'bgcolor'); + setStr(pl, null, 'align'); + setStr(pl, null, 'width'); + setStr(pl, null, 'height'); + + if ((val = tinyMCE.getAttrib(fe, "width")) != "") + pl.width = f.width.value = val; + + if ((val = tinyMCE.getAttrib(fe, "height")) != "") + pl.height = f.height.value = val; + + oldWidth = pl.width ? parseInt(pl.width) : 0; + oldHeight = pl.height ? parseInt(pl.height) : 0; + } else + oldWidth = oldHeight = 0; + + selectByValue(f, 'media_type', type); + changedType(type); + updateColor('bgcolor_pick', 'bgcolor'); + + TinyMCE_EditableSelects.init(); + generatePreview(); +} + +function insertMedia() { + var fe, f = document.forms[0], h; + + if (!AutoValidator.validate(f)) { + alert(tinyMCE.getLang('lang_invalid_data')); + return false; + } + + f.width.value = f.width.value == "" ? 100 : f.width.value; + f.height.value = f.height.value == "" ? 100 : f.height.value; + + fe = tinyMCE.selectedInstance.getFocusElement(); + if (fe != null && /mceItem(Flash|ShockWave|WindowsMedia|QuickTime|RealMedia)/.test(tinyMCE.getAttrib(fe, 'class'))) { + switch (f.media_type.options[f.media_type.selectedIndex].value) { + case "flash": + fe.className = "mceItemFlash"; + break; + + case "shockwave": + fe.className = "mceItemShockWave"; + break; + + case "qt": + fe.className = "mceItemQuickTime"; + break; + + case "wmp": + fe.className = "mceItemWindowsMedia"; + break; + + case "rmp": + fe.className = "mceItemRealMedia"; + break; + } + + if (fe.width != f.width.value || fe.height != f.width.height) + tinyMCE.selectedInstance.repaint(); + + fe.title = serializeParameters(); + fe.width = f.width.value; + fe.height = f.height.value; + fe.style.width = f.width.value + (f.width.value.indexOf('%') == -1 ? 'px' : ''); + fe.style.height = f.height.value + (f.height.value.indexOf('%') == -1 ? 'px' : ''); + fe.align = f.align.options[f.align.selectedIndex].value; + } else { + h = ' 0) { + var html = ""; + + html += ''; + + return html; + } + + return ""; +} + +function getType(v) { + var fo = tinyMCE.getParam("media_types", "flash=swf;shockwave=dcr;qt=mov,qt,mpg,mp3,mp4,mpeg;shockwave=dcr;wmp=avi,wmv,wm,asf,asx,wmx,wvx;rmp=rm,ra,ram").split(';'), i, c, el, x; + + for (i=0; i 0 ? s.substring(0, s.length - 1) : s; + + return s; +} + +function setBool(pl, p, n) { + if (typeof(pl[n]) == "undefined") + return; + + document.forms[0].elements[p + "_" + n].checked = pl[n]; +} + +function setStr(pl, p, n) { + var f = document.forms[0], e = f.elements[(p != null ? p + "_" : '') + n]; + + if (typeof(pl[n]) == "undefined") + return; + + if (e.type == "text") + e.value = pl[n]; + else + selectByValue(f, (p != null ? p + "_" : '') + n, pl[n]); +} + +function getBool(p, n, d, tv, fv) { + var v = document.forms[0].elements[p + "_" + n].checked; + + tv = typeof(tv) == 'undefined' ? 'true' : "'" + jsEncode(tv) + "'"; + fv = typeof(fv) == 'undefined' ? 'false' : "'" + jsEncode(fv) + "'"; + + return (v == d) ? '' : n + (v ? ':' + tv + ',' : ':' + fv + ','); +} + +function getStr(p, n, d) { + var e = document.forms[0].elements[(p != null ? p + "_" : "") + n]; + var v = e.type == "text" ? e.value : e.options[e.selectedIndex].value; + + return ((n == d || v == '') ? '' : n + ":'" + jsEncode(v) + "',"); +} + +function getInt(p, n, d) { + var e = document.forms[0].elements[(p != null ? p + "_" : "") + n]; + var v = e.type == "text" ? e.value : e.options[e.selectedIndex].value; + + return ((n == d || v == '') ? '' : n + ":" + v.replace(/[^0-9]+/g, '') + ","); +} + +function jsEncode(s) { + s = s.replace(new RegExp('\\\\', 'g'), '\\\\'); + s = s.replace(new RegExp('"', 'g'), '\\"'); + s = s.replace(new RegExp("'", 'g'), "\\'"); + + return s; +} + +function generatePreview(c) { + var f = document.forms[0], p = document.getElementById('prev'), h = '', cls, pl, n, type, codebase, wp, hp, nw, nh; + + p.innerHTML = ''; + + nw = parseInt(f.width.value); + nh = parseInt(f.height.value); + + if (f.width.value != "" && f.height.value != "") { + if (f.constrain.checked) { + if (c == 'width' && oldWidth != 0) { + wp = nw / oldWidth; + nh = Math.round(wp * nh); + f.height.value = nh; + } else if (c == 'height' && oldHeight != 0) { + hp = nh / oldHeight; + nw = Math.round(hp * nw); + f.width.value = nw; + } + } + } + + if (f.width.value != "") + oldWidth = nw; + + if (f.height.value != "") + oldHeight = nh; + + // After constrain + pl = serializeParameters(); + + switch (f.media_type.options[f.media_type.selectedIndex].value) { + case "flash": + cls = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'; + codebase = 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0'; + type = 'application/x-shockwave-flash'; + break; + + case "shockwave": + cls = 'clsid:166B1BCA-3F9C-11CF-8075-444553540000'; + codebase = 'http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0'; + type = 'application/x-director'; + break; + + case "qt": + cls = 'clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B'; + codebase = 'http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0'; + type = 'video/quicktime'; + break; + + case "wmp": + cls = tinyMCE.getParam('media_wmp6_compatible') ? 'clsid:05589FA1-C356-11CE-BF01-00AA0055595A' : 'clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6'; + codebase = 'http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701'; + type = 'application/x-mplayer2'; + break; + + case "rmp": + cls = 'clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA'; + codebase = 'http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701'; + type = 'audio/x-pn-realaudio-plugin'; + break; + } + + if (pl == '') { + p.innerHTML = ''; + return; + } + + pl = eval('x={' + pl + '};'); + + if (!pl.src) { + p.innerHTML = ''; + return; + } + + pl.src = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], pl.src); + pl.width = !pl.width ? 100 : pl.width; + pl.height = !pl.height ? 100 : pl.height; + pl.id = !pl.id ? 'obj' : pl.id; + pl.name = !pl.name ? 'eobj' : pl.name; + pl.align = !pl.align ? '' : pl.align; + + h += ''; + + for (n in pl) { + h += ''; + + // Add extra url parameter if it's an absolute URL + if (n == 'src' && pl[n].indexOf('://') != -1) + h += ''; + } + + h += ' + + {$lang_media_title} + + + + + + + + + + +
+ + +
+
+
+ {$lang_media_general} + + + + + + + + + + + + + + + + + +
+ +
+ + + + + +
 
+
 
+ + + + + + +
x   
+
+
+ +
+ {$lang_media_preview} + +
+
+ +
+
+ {$lang_media_advanced} + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
 
+
+
+ +
+ {$lang_media_flash_options} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + + + + + + + +
+
+ +
+ {$lang_media_qt_options} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+  
+ + + + + +
 
+
+
+ +
+ {$lang_media_wmp_options} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+
+ +
+ {$lang_media_rmp_options} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+   +
+
+ +
+ {$lang_media_shockwave_options} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+
+
+
+ +
+
+ +
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/nonbreaking/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/nonbreaking/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('nonbreaking');var TinyMCE_NonBreakingPlugin={getInfo:function(){return{longname:'Nonbreaking space',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/nonbreaking',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},getControlHTML:function(cn){switch(cn){case"nonbreaking":return tinyMCE.getButtonHTML(cn,'lang_nonbreaking_desc','{$pluginurl}/images/nonbreaking.gif','mceNonBreaking',false)}return""},execCommand:function(editor_id,element,command,user_interface,value){var inst=tinyMCE.getInstanceById(editor_id),h;switch(command){case"mceNonBreaking":h=(inst.visualChars&&inst.visualChars.state)?'·':' ';tinyMCE.execInstanceCommand(editor_id,'mceInsertContent',false,h);return true}return false},handleEvent:function(e){var inst,h;if(!tinyMCE.isOpera&&e.type=='keydown'&&e.keyCode==9&&tinyMCE.getParam('nonbreaking_force_tab',false)){inst=tinyMCE.selectedInstance;h=(inst.visualChars&&inst.visualChars.state)?'···':'   ';tinyMCE.execInstanceCommand(inst.editorId,'mceInsertContent',false,h);tinyMCE.cancelEvent(e);return false}return true}};tinyMCE.addPlugin("nonbreaking",TinyMCE_NonBreakingPlugin); \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/nonbreaking/editor_plugin_src.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/nonbreaking/editor_plugin_src.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,62 @@ +/** + * $Id: editor_plugin_src.js 42 2006-08-08 14:32:24Z spocke $ + * + * @author Moxiecode + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +/* Import plugin specific language pack */ +tinyMCE.importPluginLanguagePack('nonbreaking'); + +var TinyMCE_NonBreakingPlugin = { + getInfo : function() { + return { + longname : 'Nonbreaking space', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/nonbreaking', + version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion + }; + }, + + getControlHTML : function(cn) { + switch (cn) { + case "nonbreaking": + return tinyMCE.getButtonHTML(cn, 'lang_nonbreaking_desc', '{$pluginurl}/images/nonbreaking.gif', 'mceNonBreaking', false); + } + + return ""; + }, + + + execCommand : function(editor_id, element, command, user_interface, value) { + var inst = tinyMCE.getInstanceById(editor_id), h; + + switch (command) { + case "mceNonBreaking": + h = (inst.visualChars && inst.visualChars.state) ? '·' : ' '; + tinyMCE.execInstanceCommand(editor_id, 'mceInsertContent', false, h); + return true; + } + + return false; + }, + + handleEvent : function(e) { + var inst, h; + + if (!tinyMCE.isOpera && e.type == 'keydown' && e.keyCode == 9 && tinyMCE.getParam('nonbreaking_force_tab', false)) { + inst = tinyMCE.selectedInstance; + + h = (inst.visualChars && inst.visualChars.state) ? '···' : '   '; + tinyMCE.execInstanceCommand(inst.editorId, 'mceInsertContent', false, h); + + tinyMCE.cancelEvent(e); + return false; + } + + return true; + } +}; + +tinyMCE.addPlugin("nonbreaking", TinyMCE_NonBreakingPlugin); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/nonbreaking/images/nonbreaking.gif Binary file includes/clientside/tinymce/plugins/nonbreaking/images/nonbreaking.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/nonbreaking/langs/en.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/nonbreaking/langs/en.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,5 @@ +// UK lang variables + +tinyMCE.addToLang('nonbreaking',{ +desc : 'Insert non-breaking space character' +}); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/noneditable/css/noneditable.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/noneditable/css/noneditable.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,9 @@ +/* This is the CSS file for the noneditable elements plugin */ + +.mceEditable { + /*border: 1px dotted #0000cc;*/ +} + +.mceNonEditable { + /*border: 1px dotted #cc0000;*/ +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/noneditable/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/noneditable/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +var TinyMCE_NonEditablePlugin={getInfo:function(){return{longname:'Non editable elements',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/noneditable',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},initInstance:function(inst){tinyMCE.importCSS(inst.getDoc(),tinyMCE.baseURL+"/plugins/noneditable/css/noneditable.css");if(tinyMCE.isMSIE5_0)tinyMCE.settings['plugins']=tinyMCE.settings['plugins'].replace(/noneditable/gi,'Noneditable')},handleEvent:function(e){return this._moveSelection(e,tinyMCE.selectedInstance)},cleanup:function(type,content,inst){switch(type){case"insert_to_editor_dom":var nodes,i,editClass,nonEditClass,editable,elm;if(tinyMCE.isGecko)return content;nodes=tinyMCE.getNodeTree(content,[],1);editClass=tinyMCE.getParam("noneditable_editable_class","mceEditable");nonEditClass=tinyMCE.getParam("noneditable_noneditable_class","mceNonEditable");for(i=0;i + + blank_page + + + + + + + + + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/paste/css/blank.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/paste/css/blank.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,13 @@ +body { + background-color: #FFFFFF; + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 10px; + scrollbar-3dlight-color: #F0F0EE; + scrollbar-arrow-color: #676662; + scrollbar-base-color: #F0F0EE; + scrollbar-darkshadow-color: #DDDDDD; + scrollbar-face-color: #E0E0DD; + scrollbar-highlight-color: #F0F0EE; + scrollbar-shadow-color: #F0F0EE; + scrollbar-track-color: #F5F5F5; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/paste/css/pasteword.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/paste/css/pasteword.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,3 @@ +.sourceIframe { + border: 1px solid #808080; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/paste/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/paste/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('paste');var TinyMCE_PastePlugin={getInfo:function(){return{longname:'Paste text/word',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/paste',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},initInstance:function(inst){if(tinyMCE.isMSIE&&tinyMCE.getParam("paste_auto_cleanup_on_paste",false))tinyMCE.addEvent(inst.getBody(),"paste",TinyMCE_PastePlugin._handlePasteEvent)},handleEvent:function(e){if(!tinyMCE.isRealIE&&tinyMCE.getParam("paste_auto_cleanup_on_paste",false)&&e.ctrlKey&&e.keyCode==86&&e.type=="keydown"){window.setTimeout('tinyMCE.selectedInstance.execCommand("mcePasteText",true)',1);return tinyMCE.cancelEvent(e)}return true},getControlHTML:function(cn){switch(cn){case"pastetext":return tinyMCE.getButtonHTML(cn,'lang_paste_text_desc','{$pluginurl}/images/pastetext.gif','mcePasteText',true);case"pasteword":return tinyMCE.getButtonHTML(cn,'lang_paste_word_desc','{$pluginurl}/images/pasteword.gif','mcePasteWord',true);case"selectall":return tinyMCE.getButtonHTML(cn,'lang_selectall_desc','{$pluginurl}/images/selectall.gif','mceSelectAll',true)}return''},execCommand:function(editor_id,element,command,user_interface,value){switch(command){case"mcePasteText":if(user_interface){if((tinyMCE.isMSIE&&!tinyMCE.isOpera)&&!tinyMCE.getParam('paste_use_dialog',false))TinyMCE_PastePlugin._insertText(clipboardData.getData("Text"),true);else{var template=new Array();template['file']='../../plugins/paste/pastetext.htm';template['width']=450;template['height']=400;var plain_text="";tinyMCE.openWindow(template,{editor_id:editor_id,plain_text:plain_text,resizable:"yes",scrollbars:"no",inline:"yes",mceDo:'insert'})}}else TinyMCE_PastePlugin._insertText(value['html'],value['linebreaks']);return true;case"mcePasteWord":if(user_interface){if((tinyMCE.isMSIE&&!tinyMCE.isOpera)&&!tinyMCE.getParam('paste_use_dialog',false)){TinyMCE_PastePlugin._insertWordContent(TinyMCE_PastePlugin._clipboardHTML())}else{var template=new Array();template['file']='../../plugins/paste/pasteword.htm';template['width']=450;template['height']=400;var plain_text="";tinyMCE.openWindow(template,{editor_id:editor_id,plain_text:plain_text,resizable:"yes",scrollbars:"no",inline:"yes",mceDo:'insert'})}}else TinyMCE_PastePlugin._insertWordContent(value);return true;case"mceSelectAll":tinyMCE.execInstanceCommand(editor_id,'selectall');return true}return false},_handlePasteEvent:function(e){switch(e.type){case"paste":var html=TinyMCE_PastePlugin._clipboardHTML();var r,inst=tinyMCE.selectedInstance;if(inst&&(r=inst.getRng())&&r.text.length>0)tinyMCE.execCommand('delete');if(html&&html.length>0)tinyMCE.execCommand('mcePasteWord',false,html);tinyMCE.cancelEvent(e);return false}return true},_insertText:function(content,bLinebreaks){if(content&&content.length>0){if(bLinebreaks){if(tinyMCE.getParam("paste_create_paragraphs",true)){var rl=tinyMCE.getParam("paste_replace_list",'\u2122,TM,\u2026,...,\u201c|\u201d,",\u2019,\',\u2013|\u2014|\u2015|\u2212,-').split(',');for(var i=0;i

","gi");content=tinyMCE.regexpReplace(content,"\r\r","

","gi");content=tinyMCE.regexpReplace(content,"\n\n","

","gi");if((pos=content.indexOf('

'))!=-1){tinyMCE.execCommand("Delete");var node=tinyMCE.selectedInstance.getFocusElement();var breakElms=new Array();do{if(node.nodeType==1){if(node.nodeName=="TD"||node.nodeName=="BODY")break;breakElms[breakElms.length]=node}}while(node=node.parentNode);var before="",after="

";before+=content.substring(0,pos);for(var i=0;i";after+="<"+breakElms[(breakElms.length-1)-i].nodeName+">"}before+="

";content=before+content.substring(pos+7)+after}}if(tinyMCE.getParam("paste_create_linebreaks",true)){content=tinyMCE.regexpReplace(content,"\r\n","
","gi");content=tinyMCE.regexpReplace(content,"\r","
","gi");content=tinyMCE.regexpReplace(content,"\n","
","gi")}}tinyMCE.execCommand("mceInsertRawHTML",false,content)}},_insertWordContent:function(content){if(content&&content.length>0){var bull=String.fromCharCode(8226);var middot=String.fromCharCode(183);var cb;if((cb=tinyMCE.getParam("paste_insert_word_content_callback",""))!="")content=eval(cb+"('before', content)");var rl=tinyMCE.getParam("paste_replace_list",'\u2122,TM,\u2026,...,\u201c|\u201d,",\u2019,\',\u2013|\u2014|\u2015|\u2212,-').split(',');for(var i=0;i(.*?)<\/p>','gi'),'

$1

')}content=content.replace(new RegExp('tab-stops: list [0-9]+.0pt">','gi'),'">'+"--list--");content=content.replace(new RegExp(bull+"(.*?)
","gi"),"

"+middot+"$1

");content=content.replace(new RegExp('','gi'),""+bull);content=content.replace(/<\/o:p>/gi,"");content=content.replace(new RegExp('
]*>/gi,"");if(tinyMCE.getParam("paste_remove_styles",true))content=content.replace(new RegExp('<(\\w[^>]*) style="([^"]*)"([^>]*)','gi'),"<$1$3");content=content.replace(/<\/?font[^>]*>/gi,"");switch(tinyMCE.getParam("paste_strip_class_attributes","all")){case"all":content=content.replace(/<(\w[^>]*) class=([^ |>]*)([^>]*)/gi,"<$1$3");break;case"mso":content=content.replace(new RegExp('<(\\w[^>]*) class="?mso([^ |>]*)([^>]*)','gi'),"<$1$3");break}content=content.replace(new RegExp('href="?'+TinyMCE_PastePlugin._reEscape(""+document.location)+'','gi'),'href="'+tinyMCE.settings['document_base_url']);content=content.replace(/<(\w[^>]*) lang=([^ |>]*)([^>]*)/gi,"<$1$3");content=content.replace(/<\\?\?xml[^>]*>/gi,"");content=content.replace(/<\/?\w+:[^>]*>/gi,"");content=content.replace(/-- page break --\s*

 <\/p>/gi,"");content=content.replace(/-- page break --/gi,"");if(!tinyMCE.settings['force_p_newlines']){content=content.replace('','','gi');content=content.replace('

','

','gi')}if(!tinyMCE.isMSIE&&!tinyMCE.settings['force_p_newlines']){content=content.replace(/<\/?p[^>]*>/gi,"")}content=content.replace(/<\/?div[^>]*>/gi,"");if(tinyMCE.getParam("paste_convert_middot_lists",true)){var div=document.createElement("div");div.innerHTML=content;var className=tinyMCE.getParam("paste_unindented_list_class","unIndentedList");while(TinyMCE_PastePlugin._convertMiddots(div,"--list--"));while(TinyMCE_PastePlugin._convertMiddots(div,middot,className));while(TinyMCE_PastePlugin._convertMiddots(div,bull));content=div.innerHTML}if(tinyMCE.getParam("paste_convert_headers_to_strong",false)){content=content.replace(/ <\/h[1-6]>/gi,'

  

');content=content.replace(//gi,'

');content=content.replace(/<\/h[1-6]>/gi,'

');content=content.replace(/ <\/b>/gi,'  ');content=content.replace(/^( )*/gi,'')}content=content.replace(/--list--/gi,"");if((cb=tinyMCE.getParam("paste_insert_word_content_callback",""))!="")content=eval(cb+"('after', content)");tinyMCE.execCommand("mceInsertContent",false,content);if(tinyMCE.getParam('paste_force_cleanup_wordpaste',true))window.setTimeout('tinyMCE.execCommand("mceCleanup");',1);}},_reEscape:function(s){var l="?.\\*[](){}+^$:";var o="";for(var i=0;i 0) + tinyMCE.execCommand('delete'); + + if (html && html.length > 0) + tinyMCE.execCommand('mcePasteWord', false, html); + + tinyMCE.cancelEvent(e); + return false; + } + + return true; + }, + + _insertText : function(content, bLinebreaks) { + if (content && content.length > 0) { + if (bLinebreaks) { + // Special paragraph treatment + if (tinyMCE.getParam("paste_create_paragraphs", true)) { + var rl = tinyMCE.getParam("paste_replace_list", '\u2122,TM,\u2026,...,\u201c|\u201d,",\u2019,\',\u2013|\u2014|\u2015|\u2212,-').split(','); + for (var i=0; i

", "gi"); + content = tinyMCE.regexpReplace(content, "\r\r", "

", "gi"); + content = tinyMCE.regexpReplace(content, "\n\n", "

", "gi"); + + // Has paragraphs + if ((pos = content.indexOf('

')) != -1) { + tinyMCE.execCommand("Delete"); + + var node = tinyMCE.selectedInstance.getFocusElement(); + + // Get list of elements to break + var breakElms = new Array(); + + do { + if (node.nodeType == 1) { + // Don't break tables and break at body + if (node.nodeName == "TD" || node.nodeName == "BODY") + break; + + breakElms[breakElms.length] = node; + } + } while(node = node.parentNode); + + var before = "", after = "

"; + before += content.substring(0, pos); + + for (var i=0; i"; + after += "<" + breakElms[(breakElms.length-1)-i].nodeName + ">"; + } + + before += "

"; + content = before + content.substring(pos+7) + after; + } + } + + if (tinyMCE.getParam("paste_create_linebreaks", true)) { + content = tinyMCE.regexpReplace(content, "\r\n", "
", "gi"); + content = tinyMCE.regexpReplace(content, "\r", "
", "gi"); + content = tinyMCE.regexpReplace(content, "\n", "
", "gi"); + } + } + + tinyMCE.execCommand("mceInsertRawHTML", false, content); + } + }, + + _insertWordContent : function(content) { + if (content && content.length > 0) { + // Cleanup Word content + var bull = String.fromCharCode(8226); + var middot = String.fromCharCode(183); + var cb; + + if ((cb = tinyMCE.getParam("paste_insert_word_content_callback", "")) != "") + content = eval(cb + "('before', content)"); + + var rl = tinyMCE.getParam("paste_replace_list", '\u2122,TM,\u2026,...,\u201c|\u201d,",\u2019,\',\u2013|\u2014|\u2015|\u2212,-').split(','); + for (var i=0; i(.*?)<\/p>', 'gi'), '

$1

'); + } + + content = content.replace(new RegExp('tab-stops: list [0-9]+.0pt">', 'gi'), '">' + "--list--"); + content = content.replace(new RegExp(bull + "(.*?)
", "gi"), "

" + middot + "$1

"); + content = content.replace(new RegExp('', 'gi'), "" + bull); // Covert to bull list + content = content.replace(/<\/o:p>/gi, ""); + content = content.replace(new RegExp('
]*>/gi, ""); + + if (tinyMCE.getParam("paste_remove_styles", true)) + content = content.replace(new RegExp('<(\\w[^>]*) style="([^"]*)"([^>]*)', 'gi'), "<$1$3"); + + content = content.replace(/<\/?font[^>]*>/gi, ""); + + // Strips class attributes. + switch (tinyMCE.getParam("paste_strip_class_attributes", "all")) { + case "all": + content = content.replace(/<(\w[^>]*) class=([^ |>]*)([^>]*)/gi, "<$1$3"); + break; + + case "mso": + content = content.replace(new RegExp('<(\\w[^>]*) class="?mso([^ |>]*)([^>]*)', 'gi'), "<$1$3"); + break; + } + + content = content.replace(new RegExp('href="?' + TinyMCE_PastePlugin._reEscape("" + document.location) + '', 'gi'), 'href="' + tinyMCE.settings['document_base_url']); + content = content.replace(/<(\w[^>]*) lang=([^ |>]*)([^>]*)/gi, "<$1$3"); + content = content.replace(/<\\?\?xml[^>]*>/gi, ""); + content = content.replace(/<\/?\w+:[^>]*>/gi, ""); + content = content.replace(/-- page break --\s*

 <\/p>/gi, ""); // Remove pagebreaks + content = content.replace(/-- page break --/gi, ""); // Remove pagebreaks + + // content = content.replace(/\/? */gi, "");   + // content = content.replace(/

 <\/p>/gi, ''); + + if (!tinyMCE.settings['force_p_newlines']) { + content = content.replace('', '' ,'gi'); + content = content.replace('

', '

' ,'gi'); + } + + if (!tinyMCE.isMSIE && !tinyMCE.settings['force_p_newlines']) { + content = content.replace(/<\/?p[^>]*>/gi, ""); + } + + content = content.replace(/<\/?div[^>]*>/gi, ""); + + // Convert all middlot lists to UL lists + if (tinyMCE.getParam("paste_convert_middot_lists", true)) { + var div = document.createElement("div"); + div.innerHTML = content; + + // Convert all middot paragraphs to li elements + var className = tinyMCE.getParam("paste_unindented_list_class", "unIndentedList"); + + while (TinyMCE_PastePlugin._convertMiddots(div, "--list--")) ; // bull + while (TinyMCE_PastePlugin._convertMiddots(div, middot, className)) ; // Middot + while (TinyMCE_PastePlugin._convertMiddots(div, bull)) ; // bull + + content = div.innerHTML; + } + + // Replace all headers with strong and fix some other issues + if (tinyMCE.getParam("paste_convert_headers_to_strong", false)) { + content = content.replace(/ <\/h[1-6]>/gi, '

  

'); + content = content.replace(//gi, '

'); + content = content.replace(/<\/h[1-6]>/gi, '

'); + content = content.replace(/ <\/b>/gi, '  '); + content = content.replace(/^( )*/gi, ''); + } + + content = content.replace(/--list--/gi, ""); // Remove --list-- + + if ((cb = tinyMCE.getParam("paste_insert_word_content_callback", "")) != "") + content = eval(cb + "('after', content)"); + + // Insert cleaned content + tinyMCE.execCommand("mceInsertContent", false, content); + + if (tinyMCE.getParam('paste_force_cleanup_wordpaste', true)) + window.setTimeout('tinyMCE.execCommand("mceCleanup");', 1); // Do normal cleanup detached from this thread + } + }, + + _reEscape : function(s) { + var l = "?.\\*[](){}+^$:"; + var o = ""; + + for (var i=0; i + + {$lang_paste_text_desc} + + + + + + +
+
{$lang_paste_text_desc}
+ +
+ +
+ +
+ +
{$lang_paste_text_title}
+ + + +
+
+ +
+ +
+ +
+
+
+ + \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/paste/pasteword.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/paste/pasteword.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,29 @@ + + + + {$lang_paste_word_desc} + + + + + + +
+
{$lang_paste_word_desc}
+ +
{$lang_paste_word_title}
+ +
+ +
+
+ +
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/paste/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/paste/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +Check the TinyMCE documentation for details on this plugin. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/preview/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/preview/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('preview');var TinyMCE_PreviewPlugin={getInfo:function(){return{longname:'Preview',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/preview',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},getControlHTML:function(cn){switch(cn){case"preview":return tinyMCE.getButtonHTML(cn,'lang_preview_desc','{$pluginurl}/images/preview.gif','mcePreview')}return""},execCommand:function(editor_id,element,command,user_interface,value){switch(command){case"mcePreview":var previewPage=tinyMCE.getParam("plugin_preview_pageurl",null);var previewWidth=tinyMCE.getParam("plugin_preview_width","550");var previewHeight=tinyMCE.getParam("plugin_preview_height","600");if(previewPage){var template=new Array();template['file']=previewPage;template['width']=previewWidth;template['height']=previewHeight;tinyMCE.openWindow(template,{editor_id:editor_id,resizable:"yes",scrollbars:"yes",inline:"yes",content:tinyMCE.getContent(),content_css:tinyMCE.getParam("content_css")})}else{var win=window.open("","mcePreview","menubar=no,toolbar=no,scrollbars=yes,resizable=yes,left=20,top=20,width="+previewWidth+",height="+previewHeight);var html="",i;var c=tinyMCE.getContent();var pos=c.indexOf('',pos);pos2=c.lastIndexOf('');c=c.substring(pos+1,pos2)}html+=tinyMCE.getParam('doctype');html+='';html+='';html+=''+tinyMCE.getLang('lang_preview_desc')+'';html+='';html+='';for(i=0;i';html+='';html+='';html+='';html+=c;html+='';html+='';win.document.write(html);win.document.close()}return true}return false},_setDoc:function(d){TinyMCE_PreviewPlugin._doc=d;d._embeds=new Array()},_setWin:function(d){TinyMCE_PreviewPlugin._win=d},_onLoad:function(){var nl,i,el=new Array(),d=TinyMCE_PreviewPlugin._doc,sv,ne;nl=d.getElementsByTagName("script");for(i=0;i';for(n in p)h+='';h+='', pos); + pos2 = c.lastIndexOf(''); + c = c.substring(pos + 1, pos2); + } + + html += tinyMCE.getParam('doctype'); + html += ''; + html += ''; + html += '' + tinyMCE.getLang('lang_preview_desc') + ''; + html += ''; + html += ''; + + for (i=0; i'; + + html += ''; + html += ''; + html += ''; + html += c; + html += ''; + html += ''; + + win.document.write(html); + win.document.close(); + } + + return true; + } + + return false; + }, + + _setDoc : function(d) { + TinyMCE_PreviewPlugin._doc = d; + d._embeds = new Array(); + }, + + _setWin : function(d) { + TinyMCE_PreviewPlugin._win = d; + }, + + _onLoad : function() { + var nl, i, el = new Array(), d = TinyMCE_PreviewPlugin._doc, sv, ne; + + nl = d.getElementsByTagName("script"); + for (i=0; i'; + + h += ' + + + +Example of a custom preview page + + + + +Editor contents:
+{$content} + + + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/preview/images/preview.gif Binary file includes/clientside/tinymce/plugins/preview/images/preview.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/preview/jscripts/embed.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/preview/jscripts/embed.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,73 @@ +/** + * This script contains embed functions for common plugins. This scripts are complety free to use for any purpose. + */ + +function writeFlash(p) { + writeEmbed( + 'D27CDB6E-AE6D-11cf-96B8-444553540000', + 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0', + 'application/x-shockwave-flash', + p + ); +} + +function writeShockWave(p) { + writeEmbed( + '166B1BCA-3F9C-11CF-8075-444553540000', + 'http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0', + 'application/x-director', + p + ); +} + +function writeQuickTime(p) { + writeEmbed( + '02BF25D5-8C17-4B23-BC80-D3488ABDDC6B', + 'http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0', + 'video/quicktime', + p + ); +} + +function writeRealMedia(p) { + writeEmbed( + 'CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA', + 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0', + 'audio/x-pn-realaudio-plugin', + p + ); +} + +function writeWindowsMedia(p) { + p.url = p.src; + writeEmbed( + '6BF52A52-394A-11D3-B153-00C04F79FAA6', + 'http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701', + 'application/x-mplayer2', + p + ); +} + +function writeEmbed(cls, cb, mt, p) { + var h = '', n; + + h += ''; + + h += ' no need to copy any values! + + // copy values from one panel to the other (if they exist there) + var from_panel_name = tab.id.match(/^search/i) ? "replace_panel" : "search_panel"; + var to_panel_name = (from_panel_name == "search_panel") ? "replace_panel" : "search_panel"; + + // find all elements with IDs to copy their values + var elms = document.getElementById(from_panel_name).getElementsByTagName("*"); + for (var i = 0; i < elms.length; i++) { + if (elms[i].id && elms[i].id != "") { + var checked = "undefined"; + if (elms[i].type.toLowerCase() == "checkbox" || elms[i].type.toLowerCase() == "radio") + checked = elms[i].checked; + + // copy values if element exists in other panel + var to_elm_name = to_panel_name + elms[i].id.substring(from_panel_name.length, elms[i].id.length); + var to_elm = document.getElementById(to_elm_name); + if (to_elm) { + if (checked != "undefined") + to_elm.checked = checked; + else + to_elm.value = elms[i].value; + } + } + } + + return false; +} \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/searchreplace/langs/en.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/searchreplace/langs/en.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,21 @@ +// UK lang variables + +tinyMCE.addToLang('',{ +searchreplace_search_desc : 'Find', +searchreplace_searchnext_desc : 'Find again', +searchreplace_replace_desc : 'Find/Replace', +searchreplace_notfound : 'The search has been completed. The search string could not be found.', +searchreplace_search_title : 'Find', +searchreplace_replace_title : 'Find/Replace', +searchreplace_allreplaced : 'All occurrences of the search string were replaced.', +searchreplace_findwhat : 'Find what', +searchreplace_replacewith : 'Replace with', +searchreplace_direction : 'Direction', +searchreplace_up : 'Up', +searchreplace_down : 'Down', +searchreplace_case : 'Match case', +searchreplace_findnext : 'Find next', +searchreplace_replace : 'Replace', +searchreplace_replaceall : 'Replace all', +searchreplace_cancel : 'Cancel' +}); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/searchreplace/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/searchreplace/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +Check the TinyMCE documentation for details on this plugin. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/searchreplace/searchreplace.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/searchreplace/searchreplace.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,107 @@ + + + {$lang_searchreplace_replace_title} + + + + + + + + +
+ + +
+ +
+ + + + + + + + + + + +
+ + + + + + + + +
+
+ + + + + +
+
+
+ +
+ + + + + + + + + + + + + + + +
+ + + + + + + + +
+
+ + + + + +
+
+
+ +
+ +
+
+ + + + + +
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/style/css/props.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/style/css/props.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,62 @@ +#text_font { + width: 250px; +} + +#text_size { + width: 70px; +} + +.mceAddSelectValue { + background-color: #DDDDDD; +} + +select, #block_text_indent, #box_width, #box_height, #box_padding_top, #box_padding_right, #box_padding_bottom, #box_padding_left { + width: 70px; +} + +#box_margin_top, #box_margin_right, #box_margin_bottom, #box_margin_left, #positioning_width, #positioning_height, #positioning_zindex { + width: 70px; +} + +#positioning_placement_top, #positioning_placement_right, #positioning_placement_bottom, #positioning_placement_left { + width: 70px; +} + +#positioning_clip_top, #positioning_clip_right, #positioning_clip_bottom, #positioning_clip_left { + width: 70px; +} + +.panel_wrapper div.current { + padding-top: 10px; + height: 230px; +} + +.delim { + border-left: 1px solid gray; +} + +.tdelim { + border-bottom: 1px solid gray; +} + +#block_display { + width: 145px; +} + +#list_type { + width: 115px; +} + +.disabled { + background-color: #EEEEEE; +} + +#apply { + font-weight: bold; + width: 78px; + height: 21px; + border: 0; + background-image: url('../images/apply_button_bg.gif'); + cursor: pointer; +} + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/style/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/style/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('style');var TinyMCE_StylePlugin={getInfo:function(){return{longname:'Style',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/style',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},getControlHTML:function(cn){switch(cn){case"styleprops":return tinyMCE.getButtonHTML(cn,'lang_style_styleinfo_desc','{$pluginurl}/images/styleprops.gif','mceStyleProps',true)}return""},execCommand:function(editor_id,element,command,user_interface,value){var e,inst;switch(command){case"mceStyleProps":TinyMCE_StylePlugin._styleProps();return true;case"mceSetElementStyle":inst=tinyMCE.getInstanceById(editor_id);e=inst.selection.getFocusElement();if(e){e.style.cssText=value;inst.repaint()}return true}return false},handleNodeChange:function(editor_id,node,undo_index,undo_levels,visual_aid,any_selection){if(node.nodeName=='BODY')tinyMCE.switchClass(editor_id+'_styleprops','mceButtonDisabled');else tinyMCE.switchClass(editor_id+'_styleprops','mceButtonNormal')},_styleProps:function(){var e=tinyMCE.selectedInstance.selection.getFocusElement();if(!e||e.nodeName=='BODY')return;tinyMCE.openWindow({file:'../../plugins/style/props.htm',width:480+tinyMCE.getLang('lang_style_props_delta_width',0),height:320+tinyMCE.getLang('lang_style_props_delta_height',0)},{editor_id:tinyMCE.selectedInstance.editorId,inline:"yes",style_text:e.style.cssText})}};tinyMCE.addPlugin("style",TinyMCE_StylePlugin); \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/style/editor_plugin_src.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/style/editor_plugin_src.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,83 @@ +/** + * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $ + * + * @author Moxiecode + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +/* Import plugin specific language pack */ +tinyMCE.importPluginLanguagePack('style'); + +var TinyMCE_StylePlugin = { + getInfo : function() { + return { + longname : 'Style', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/style', + version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion + }; + }, + + getControlHTML : function(cn) { + switch (cn) { + case "styleprops": + return tinyMCE.getButtonHTML(cn, 'lang_style_styleinfo_desc', '{$pluginurl}/images/styleprops.gif', 'mceStyleProps', true); + } + + return ""; + }, + + execCommand : function(editor_id, element, command, user_interface, value) { + var e, inst; + + // Handle commands + switch (command) { + case "mceStyleProps": + TinyMCE_StylePlugin._styleProps(); + return true; + + case "mceSetElementStyle": + inst = tinyMCE.getInstanceById(editor_id); + e = inst.selection.getFocusElement(); + + if (e) { + e.style.cssText = value; + inst.repaint(); + } + + return true; + } + + // Pass to next handler in chain + return false; + }, + + handleNodeChange : function(editor_id, node, undo_index, undo_levels, visual_aid, any_selection) { + if (node.nodeName == 'BODY') + tinyMCE.switchClass(editor_id + '_styleprops', 'mceButtonDisabled'); + else + tinyMCE.switchClass(editor_id + '_styleprops', 'mceButtonNormal'); + }, + + // Private plugin specific methods + + _styleProps : function() { + var e = tinyMCE.selectedInstance.selection.getFocusElement(); + + if (!e || e.nodeName == 'BODY') + return; + + tinyMCE.openWindow({ + file : '../../plugins/style/props.htm', + width : 480 + tinyMCE.getLang('lang_style_props_delta_width', 0), + height : 320 + tinyMCE.getLang('lang_style_props_delta_height', 0) + }, { + editor_id : tinyMCE.selectedInstance.editorId, + inline : "yes", + style_text : e.style.cssText + }); + } +}; + +tinyMCE.addPlugin("style", TinyMCE_StylePlugin); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/style/images/apply_button_bg.gif Binary file includes/clientside/tinymce/plugins/style/images/apply_button_bg.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/style/images/style_info.gif Binary file includes/clientside/tinymce/plugins/style/images/style_info.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/style/images/styleprops.gif Binary file includes/clientside/tinymce/plugins/style/images/styleprops.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/style/jscripts/props.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/style/jscripts/props.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,633 @@ +var defaultFonts = "" + + "Arial, Helvetica, sans-serif=Arial, Helvetica, sans-serif;" + + "Times New Roman, Times, serif=Times New Roman, Times, serif;" + + "Courier New, Courier, mono=Courier New, Courier, mono;" + + "Times New Roman, Times, serif=Times New Roman, Times, serif;" + + "Georgia, Times New Roman, Times, serif=Georgia, Times New Roman, Times, serif;" + + "Verdana, Arial, Helvetica, sans-serif=Verdana, Arial, Helvetica, sans-serif;" + + "Geneva, Arial, Helvetica, sans-serif=Geneva, Arial, Helvetica, sans-serif"; + +var defaultSizes = "9;10;12;14;16;18;24;xx-small;x-small;small;medium;large;x-large;xx-large;smaller;larger"; +var defaultMeasurement = "+pixels=px;points=pt;in;cm;mm;picas;ems;exs;%"; +var defaultSpacingMeasurement = "pixels=px;points=pt;in;cm;mm;picas;+ems;exs;%"; +var defaultIndentMeasurement = "pixels=px;+points=pt;in;cm;mm;picas;ems;exs;%"; +var defaultWeight = "normal;bold;bolder;lighter;100;200;300;400;500;600;700;800;900"; +var defaultTextStyle = "normal;italic;oblique"; +var defaultVariant = "normal;small-caps"; +var defaultLineHeight = "normal"; +var defaultAttachment = "fixed;scroll"; +var defaultRepeat = "no-repeat;repeat;repeat-x;repeat-y"; +var defaultPosH = "left;center;right"; +var defaultPosV = "top;center;bottom"; +var defaultVAlign = "baseline;sub;super;top;text-top;middle;bottom;text-bottom"; +var defaultDisplay = "inline;block;list-item;run-in;compact;marker;table;inline-table;table-row-group;table-header-group;table-footer-group;table-row;table-column-group;table-column;table-cell;table-caption;none"; +var defaultBorderStyle = "none;solid;dashed;dotted;double;groove;ridge;inset;outset"; +var defaultBorderWidth = "thin;medium;thick"; +var defaultListType = "disc;circle;square;decimal;lower-roman;upper-roman;lower-alpha;upper-alpha;none"; + +function init() { + var ce = document.getElementById('container'), h; + + ce.style.cssText = tinyMCEPopup.getWindowArg('style_text'); + + h = getBrowserHTML('background_image_browser','background_image','image','advimage'); + document.getElementById("background_image_browser").innerHTML = h; + + tinyMCEPopup.resizeToInnerSize(); + + document.getElementById('text_color_pickcontainer').innerHTML = getColorPickerHTML('text_color_pick','text_color'); + document.getElementById('background_color_pickcontainer').innerHTML = getColorPickerHTML('background_color_pick','background_color'); + document.getElementById('border_color_top_pickcontainer').innerHTML = getColorPickerHTML('border_color_top_pick','border_color_top'); + document.getElementById('border_color_right_pickcontainer').innerHTML = getColorPickerHTML('border_color_right_pick','border_color_right'); + document.getElementById('border_color_bottom_pickcontainer').innerHTML = getColorPickerHTML('border_color_bottom_pick','border_color_bottom'); + document.getElementById('border_color_left_pickcontainer').innerHTML = getColorPickerHTML('border_color_left_pick','border_color_left'); + + fillSelect(0, 'text_font', 'style_font', defaultFonts, ';', true); + fillSelect(0, 'text_size', 'style_font_size', defaultSizes, ';', true); + fillSelect(0, 'text_size_measurement', 'style_font_size_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'text_case', 'style_text_case', "capitalize;uppercase;lowercase", ';', true); + fillSelect(0, 'text_weight', 'style_font_weight', defaultWeight, ';', true); + fillSelect(0, 'text_style', 'style_font_style', defaultTextStyle, ';', true); + fillSelect(0, 'text_variant', 'style_font_variant', defaultVariant, ';', true); + fillSelect(0, 'text_lineheight', 'style_font_line_height', defaultLineHeight, ';', true); + fillSelect(0, 'text_lineheight_measurement', 'style_font_line_height_measurement', defaultMeasurement, ';', true); + + fillSelect(0, 'background_attachment', 'style_background_attachment', defaultAttachment, ';', true); + fillSelect(0, 'background_repeat', 'style_background_repeat', defaultRepeat, ';', true); + + fillSelect(0, 'background_hpos_measurement', 'style_background_hpos_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'background_vpos_measurement', 'style_background_vpos_measurement', defaultMeasurement, ';', true); + + fillSelect(0, 'background_hpos', 'style_background_hpos', defaultPosH, ';', true); + fillSelect(0, 'background_vpos', 'style_background_vpos', defaultPosV, ';', true); + + fillSelect(0, 'block_wordspacing', 'style_wordspacing', 'normal', ';', true); + fillSelect(0, 'block_wordspacing_measurement', 'style_wordspacing_measurement', defaultSpacingMeasurement, ';', true); + fillSelect(0, 'block_letterspacing', 'style_letterspacing', 'normal', ';', true); + fillSelect(0, 'block_letterspacing_measurement', 'style_letterspacing_measurement', defaultSpacingMeasurement, ';', true); + fillSelect(0, 'block_vertical_alignment', 'style_vertical_alignment', defaultVAlign, ';', true); + fillSelect(0, 'block_text_align', 'style_text_align', "left;right;center;justify", ';', true); + fillSelect(0, 'block_whitespace', 'style_whitespace', "normal;pre;nowrap", ';', true); + fillSelect(0, 'block_display', 'style_display', defaultDisplay, ';', true); + fillSelect(0, 'block_text_indent_measurement', 'style_text_indent_measurement', defaultIndentMeasurement, ';', true); + + fillSelect(0, 'box_width_measurement', 'style_box_width_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_height_measurement', 'style_box_height_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_float', 'style_float', 'left;right;none', ';', true); + fillSelect(0, 'box_clear', 'style_clear', 'left;right;both;none', ';', true); + fillSelect(0, 'box_padding_left_measurement', 'style_padding_left_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_padding_top_measurement', 'style_padding_top_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_padding_bottom_measurement', 'style_padding_bottom_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_padding_right_measurement', 'style_padding_right_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_margin_left_measurement', 'style_margin_left_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_margin_top_measurement', 'style_margin_top_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_margin_bottom_measurement', 'style_margin_bottom_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_margin_right_measurement', 'style_margin_right_measurement', defaultMeasurement, ';', true); + + fillSelect(0, 'border_style_top', 'style_border_style_top', defaultBorderStyle, ';', true); + fillSelect(0, 'border_style_right', 'style_border_style_right', defaultBorderStyle, ';', true); + fillSelect(0, 'border_style_bottom', 'style_border_style_bottom', defaultBorderStyle, ';', true); + fillSelect(0, 'border_style_left', 'style_border_style_left', defaultBorderStyle, ';', true); + + fillSelect(0, 'border_width_top', 'style_border_width_top', defaultBorderWidth, ';', true); + fillSelect(0, 'border_width_right', 'style_border_width_right', defaultBorderWidth, ';', true); + fillSelect(0, 'border_width_bottom', 'style_border_width_bottom', defaultBorderWidth, ';', true); + fillSelect(0, 'border_width_left', 'style_border_width_left', defaultBorderWidth, ';', true); + + fillSelect(0, 'border_width_top_measurement', 'style_border_width_top_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'border_width_right_measurement', 'style_border_width_right_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'border_width_bottom_measurement', 'style_border_width_bottom_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'border_width_left_measurement', 'style_border_width_left_measurement', defaultMeasurement, ';', true); + + fillSelect(0, 'list_type', 'style_list_type', defaultListType, ';', true); + fillSelect(0, 'list_position', 'style_list_position', "inside;outside", ';', true); + + fillSelect(0, 'positioning_type', 'style_positioning_type', "absolute;relative;static", ';', true); + fillSelect(0, 'positioning_visibility', 'style_positioning_visibility', "inherit;visible;hidden", ';', true); + + fillSelect(0, 'positioning_width_measurement', 'style_positioning_width_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_height_measurement', 'style_positioning_height_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_overflow', 'style_positioning_overflow', "visible;hidden;scroll;auto", ';', true); + + fillSelect(0, 'positioning_placement_top_measurement', 'style_positioning_placement_top_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_placement_right_measurement', 'style_positioning_placement_right_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_placement_bottom_measurement', 'style_positioning_placement_bottom_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_placement_left_measurement', 'style_positioning_placement_left_measurement', defaultMeasurement, ';', true); + + fillSelect(0, 'positioning_clip_top_measurement', 'style_positioning_clip_top_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_clip_right_measurement', 'style_positioning_clip_right_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_clip_bottom_measurement', 'style_positioning_clip_bottom_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_clip_left_measurement', 'style_positioning_clip_left_measurement', defaultMeasurement, ';', true); + + TinyMCE_EditableSelects.init(); + setupFormData(); + showDisabledControls(); +} + +function setupFormData() { + var ce = document.getElementById('container'), f = document.forms[0], s, b, i; + + // Setup text fields + + selectByValue(f, 'text_font', ce.style.fontFamily, true, true); + selectByValue(f, 'text_size', getNum(ce.style.fontSize), true, true); + selectByValue(f, 'text_size_measurement', getMeasurement(ce.style.fontSize)); + selectByValue(f, 'text_weight', ce.style.fontWeight, true, true); + selectByValue(f, 'text_style', ce.style.fontStyle, true, true); + selectByValue(f, 'text_lineheight', getNum(ce.style.lineHeight), true, true); + selectByValue(f, 'text_lineheight_measurement', getMeasurement(ce.style.lineHeight)); + selectByValue(f, 'text_case', ce.style.textTransform, true, true); + selectByValue(f, 'text_variant', ce.style.fontVariant, true, true); + f.text_color.value = ce.style.color; + updateColor('text_color_pick', 'text_color'); + f.text_underline.checked = inStr(ce.style.textDecoration, 'underline'); + f.text_overline.checked = inStr(ce.style.textDecoration, 'overline'); + f.text_linethrough.checked = inStr(ce.style.textDecoration, 'line-through'); + f.text_blink.checked = inStr(ce.style.textDecoration, 'blink'); + + // Setup background fields + + f.background_color.value = ce.style.backgroundColor; + updateColor('background_color_pick', 'background_color'); + f.background_image.value = ce.style.backgroundImage.replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1"); + selectByValue(f, 'background_repeat', ce.style.backgroundRepeat, true, true); + selectByValue(f, 'background_attachment', ce.style.backgroundAttachment, true, true); + selectByValue(f, 'background_hpos', getNum(getVal(ce.style.backgroundPosition, 0)), true, true); + selectByValue(f, 'background_hpos_measurement', getMeasurement(getVal(ce.style.backgroundPosition, 0))); + selectByValue(f, 'background_vpos', getNum(getVal(ce.style.backgroundPosition, 1)), true, true); + selectByValue(f, 'background_vpos_measurement', getMeasurement(getVal(ce.style.backgroundPosition, 1))); + + // Setup block fields + + selectByValue(f, 'block_wordspacing', getNum(ce.style.wordSpacing), true, true); + selectByValue(f, 'block_wordspacing_measurement', getMeasurement(ce.style.wordSpacing)); + selectByValue(f, 'block_letterspacing', getNum(ce.style.letterSpacing), true, true); + selectByValue(f, 'block_letterspacing_measurement', getMeasurement(ce.style.letterSpacing)); + selectByValue(f, 'block_vertical_alignment', ce.style.verticalAlign, true, true); + selectByValue(f, 'block_text_align', ce.style.textAlign, true, true); + f.block_text_indent.value = getNum(ce.style.textIndent); + selectByValue(f, 'block_text_indent_measurement', getMeasurement(ce.style.textIndent)); + selectByValue(f, 'block_whitespace', ce.style.whiteSpace, true, true); + selectByValue(f, 'block_display', ce.style.display, true, true); + + // Setup box fields + + f.box_width.value = getNum(ce.style.width); + selectByValue(f, 'box_width_measurement', getMeasurement(ce.style.width)); + + f.box_height.value = getNum(ce.style.height); + selectByValue(f, 'box_height_measurement', getMeasurement(ce.style.height)); + + if (tinyMCE.isGecko) + selectByValue(f, 'box_float', ce.style.cssFloat, true, true); + else + selectByValue(f, 'box_float', ce.style.styleFloat, true, true); + + selectByValue(f, 'box_clear', ce.style.clear, true, true); + + setupBox(f, ce, 'box_padding', 'padding', ''); + setupBox(f, ce, 'box_margin', 'margin', ''); + + // Setup border fields + + setupBox(f, ce, 'border_style', 'border', 'Style'); + setupBox(f, ce, 'border_width', 'border', 'Width'); + setupBox(f, ce, 'border_color', 'border', 'Color'); + + updateColor('border_color_top_pick', 'border_color_top'); + updateColor('border_color_right_pick', 'border_color_right'); + updateColor('border_color_bottom_pick', 'border_color_bottom'); + updateColor('border_color_left_pick', 'border_color_left'); + + // Setup list fields + + selectByValue(f, 'list_type', ce.style.listStyleType, true, true); + selectByValue(f, 'list_position', ce.style.listStylePosition, true, true); + f.list_bullet_image.value = ce.style.listStyleImage.replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1"); + + // Setup box fields + + selectByValue(f, 'positioning_type', ce.style.position, true, true); + selectByValue(f, 'positioning_visibility', ce.style.visibility, true, true); + selectByValue(f, 'positioning_overflow', ce.style.overflow, true, true); + f.positioning_zindex.value = ce.style.zIndex ? ce.style.zIndex : ""; + + f.positioning_width.value = getNum(ce.style.width); + selectByValue(f, 'positioning_width_measurement', getMeasurement(ce.style.width)); + + f.positioning_height.value = getNum(ce.style.height); + selectByValue(f, 'positioning_height_measurement', getMeasurement(ce.style.height)); + + setupBox(f, ce, 'positioning_placement', '', '', new Array('top', 'right', 'bottom', 'left')); + + s = ce.style.clip.replace(new RegExp("rect\\('?([^']*)'?\\)", 'gi'), "$1"); + s = s.replace(/,/g, ' '); + + if (!hasEqualValues(new Array(getVal(s, 0), getVal(s, 1), getVal(s, 2), getVal(s, 3)))) { + f.positioning_clip_top.value = getNum(getVal(s, 0)); + selectByValue(f, 'positioning_clip_top_measurement', getMeasurement(getVal(s, 0))); + f.positioning_clip_right.value = getNum(getVal(s, 1)); + selectByValue(f, 'positioning_clip_right_measurement', getMeasurement(getVal(s, 1))); + f.positioning_clip_bottom.value = getNum(getVal(s, 2)); + selectByValue(f, 'positioning_clip_bottom_measurement', getMeasurement(getVal(s, 2))); + f.positioning_clip_left.value = getNum(getVal(s, 3)); + selectByValue(f, 'positioning_clip_left_measurement', getMeasurement(getVal(s, 3))); + } else { + f.positioning_clip_top.value = getNum(getVal(s, 0)); + selectByValue(f, 'positioning_clip_top_measurement', getMeasurement(getVal(s, 0))); + f.positioning_clip_right.value = f.positioning_clip_bottom.value = f.positioning_clip_left.value; + } + +// setupBox(f, ce, '', 'border', 'Color'); +} + +function getMeasurement(s) { + return s.replace(/^([0-9]+)(.*)$/, "$2"); +} + +function getNum(s) { + if (new RegExp('^[0-9]+[a-z%]+$', 'gi').test(s)) + return s.replace(/[^0-9]/g, ''); + + return s; +} + +function inStr(s, n) { + return new RegExp(n, 'gi').test(s); +} + +function getVal(s, i) { + var a = tinyMCE.explode(' ', s); + + if (a.length > 1) + return a[i]; + + return ""; +} + +function setValue(f, n, v) { + if (f.elements[n].type == "text") + f.elements[n].value = v; + else + selectByValue(f, n, v, true, true); +} + +function setupBox(f, ce, fp, pr, sf, b) { + if (typeof(b) == "undefined") + b = new Array('Top', 'Right', 'Bottom', 'Left'); + + if (isSame(ce, pr, sf, b)) { + f.elements[fp + "_same"].checked = true; + + setValue(f, fp + "_top", getNum(ce.style[pr + b[0] + sf])); + f.elements[fp + "_top"].disabled = false; + + f.elements[fp + "_right"].value = ""; + f.elements[fp + "_right"].disabled = true; + f.elements[fp + "_bottom"].value = ""; + f.elements[fp + "_bottom"].disabled = true; + f.elements[fp + "_left"].value = ""; + f.elements[fp + "_left"].disabled = true; + + if (f.elements[fp + "_top_measurement"]) { + selectByValue(f, fp + '_top_measurement', getMeasurement(ce.style[pr + b[0] + sf])); + f.elements[fp + "_left_measurement"].disabled = true; + f.elements[fp + "_bottom_measurement"].disabled = true; + f.elements[fp + "_right_measurement"].disabled = true; + } + } else { + f.elements[fp + "_same"].checked = false; + + setValue(f, fp + "_top", getNum(ce.style[pr + b[0] + sf])); + f.elements[fp + "_top"].disabled = false; + + setValue(f, fp + "_right", getNum(ce.style[pr + b[1] + sf])); + f.elements[fp + "_right"].disabled = false; + + setValue(f, fp + "_bottom", getNum(ce.style[pr + b[2] + sf])); + f.elements[fp + "_bottom"].disabled = false; + + setValue(f, fp + "_left", getNum(ce.style[pr + b[3] + sf])); + f.elements[fp + "_left"].disabled = false; + + if (f.elements[fp + "_top_measurement"]) { + selectByValue(f, fp + '_top_measurement', getMeasurement(ce.style[pr + b[0] + sf])); + selectByValue(f, fp + '_right_measurement', getMeasurement(ce.style[pr + b[1] + sf])); + selectByValue(f, fp + '_bottom_measurement', getMeasurement(ce.style[pr + b[2] + sf])); + selectByValue(f, fp + '_left_measurement', getMeasurement(ce.style[pr + b[3] + sf])); + f.elements[fp + "_left_measurement"].disabled = false; + f.elements[fp + "_bottom_measurement"].disabled = false; + f.elements[fp + "_right_measurement"].disabled = false; + } + } +} + +function isSame(e, pr, sf, b) { + var a = new Array(), i, x; + + if (typeof(b) == "undefined") + b = new Array('Top', 'Right', 'Bottom', 'Left'); + + if (typeof(sf) == "undefined" || sf == null) + sf = ""; + + a[0] = e.style[pr + b[0] + sf]; + a[1] = e.style[pr + b[1] + sf]; + a[2] = e.style[pr + b[2] + sf]; + a[3] = e.style[pr + b[3] + sf]; + + for (i=0; i 0 ? s.substring(1) : s; + + if (f.text_none.checked) + s = "none"; + + ce.style.textDecoration = s; + + // Build background styles + + ce.style.backgroundColor = f.background_color.value; + ce.style.backgroundImage = f.background_image.value != "" ? "url(" + f.background_image.value + ")" : ""; + ce.style.backgroundRepeat = f.background_repeat.value; + ce.style.backgroundAttachment = f.background_attachment.value; + + if (f.background_hpos.value != "") { + s = ""; + s += f.background_hpos.value + (isNum(f.background_hpos.value) ? f.background_hpos_measurement.value : "") + " "; + s += f.background_vpos.value + (isNum(f.background_vpos.value) ? f.background_vpos_measurement.value : ""); + ce.style.backgroundPosition = s; + } + + // Build block styles + + ce.style.wordSpacing = f.block_wordspacing.value + (isNum(f.block_wordspacing.value) ? f.block_wordspacing_measurement.value : ""); + ce.style.letterSpacing = f.block_letterspacing.value + (isNum(f.block_letterspacing.value) ? f.block_letterspacing_measurement.value : ""); + ce.style.verticalAlign = f.block_vertical_alignment.value; + ce.style.textAlign = f.block_text_align.value; + ce.style.textIndent = f.block_text_indent.value + (isNum(f.block_text_indent.value) ? f.block_text_indent_measurement.value : ""); + ce.style.whiteSpace = f.block_whitespace.value; + ce.style.display = f.block_display.value; + + // Build box styles + + ce.style.width = f.box_width.value + (isNum(f.box_width.value) ? f.box_width_measurement.value : ""); + ce.style.height = f.box_height.value + (isNum(f.box_height.value) ? f.box_height_measurement.value : ""); + ce.style.styleFloat = f.box_float.value; + + if (tinyMCE.isGecko) + ce.style.cssFloat = f.box_float.value; + + ce.style.clear = f.box_clear.value; + + if (!f.box_padding_same.checked) { + ce.style.paddingTop = f.box_padding_top.value + (isNum(f.box_padding_top.value) ? f.box_padding_top_measurement.value : ""); + ce.style.paddingRight = f.box_padding_right.value + (isNum(f.box_padding_right.value) ? f.box_padding_right_measurement.value : ""); + ce.style.paddingBottom = f.box_padding_bottom.value + (isNum(f.box_padding_bottom.value) ? f.box_padding_bottom_measurement.value : ""); + ce.style.paddingLeft = f.box_padding_left.value + (isNum(f.box_padding_left.value) ? f.box_padding_left_measurement.value : ""); + } else + ce.style.padding = f.box_padding_top.value + (isNum(f.box_padding_top.value) ? f.box_padding_top_measurement.value : ""); + + if (!f.box_margin_same.checked) { + ce.style.marginTop = f.box_margin_top.value + (isNum(f.box_margin_top.value) ? f.box_margin_top_measurement.value : ""); + ce.style.marginRight = f.box_margin_right.value + (isNum(f.box_margin_right.value) ? f.box_margin_right_measurement.value : ""); + ce.style.marginBottom = f.box_margin_bottom.value + (isNum(f.box_margin_bottom.value) ? f.box_margin_bottom_measurement.value : ""); + ce.style.marginLeft = f.box_margin_left.value + (isNum(f.box_margin_left.value) ? f.box_margin_left_measurement.value : ""); + } else + ce.style.margin = f.box_margin_top.value + (isNum(f.box_margin_top.value) ? f.box_margin_top_measurement.value : ""); + + // Build border styles + + if (!f.border_style_same.checked) { + ce.style.borderTopStyle = f.border_style_top.value; + ce.style.borderRightStyle = f.border_style_right.value; + ce.style.borderBottomStyle = f.border_style_bottom.value; + ce.style.borderLeftStyle = f.border_style_left.value; + } else + ce.style.borderStyle = f.border_style_top.value; + + if (!f.border_width_same.checked) { + ce.style.borderTopWidth = f.border_width_top.value + (isNum(f.border_width_top.value) ? f.border_width_top_measurement.value : ""); + ce.style.borderRightWidth = f.border_width_right.value + (isNum(f.border_width_right.value) ? f.border_width_right_measurement.value : ""); + ce.style.borderBottomWidth = f.border_width_bottom.value + (isNum(f.border_width_bottom.value) ? f.border_width_bottom_measurement.value : ""); + ce.style.borderLeftWidth = f.border_width_left.value + (isNum(f.border_width_left.value) ? f.border_width_left_measurement.value : ""); + } else + ce.style.borderWidth = f.border_width_top.value; + + if (!f.border_color_same.checked) { + ce.style.borderTopColor = f.border_color_top.value; + ce.style.borderRightColor = f.border_color_right.value; + ce.style.borderBottomColor = f.border_color_bottom.value; + ce.style.borderLeftColor = f.border_color_left.value; + } else + ce.style.borderColor = f.border_color_top.value; + + // Build list styles + + ce.style.listStyleType = f.list_type.value; + ce.style.listStylePosition = f.list_position.value; + ce.style.listStyleImage = f.list_bullet_image.value != "" ? "url(" + f.list_bullet_image.value + ")" : ""; + + // Build positioning styles + + ce.style.position = f.positioning_type.value; + ce.style.visibility = f.positioning_visibility.value; + + if (ce.style.width == "") + ce.style.width = f.positioning_width.value + (isNum(f.positioning_width.value) ? f.positioning_width_measurement.value : ""); + + if (ce.style.height == "") + ce.style.height = f.positioning_height.value + (isNum(f.positioning_height.value) ? f.positioning_height_measurement.value : ""); + + ce.style.zIndex = f.positioning_zindex.value; + ce.style.overflow = f.positioning_overflow.value; + + if (!f.positioning_placement_same.checked) { + ce.style.top = f.positioning_placement_top.value + (isNum(f.positioning_placement_top.value) ? f.positioning_placement_top_measurement.value : ""); + ce.style.right = f.positioning_placement_right.value + (isNum(f.positioning_placement_right.value) ? f.positioning_placement_right_measurement.value : ""); + ce.style.bottom = f.positioning_placement_bottom.value + (isNum(f.positioning_placement_bottom.value) ? f.positioning_placement_bottom_measurement.value : ""); + ce.style.left = f.positioning_placement_left.value + (isNum(f.positioning_placement_left.value) ? f.positioning_placement_left_measurement.value : ""); + } else { + s = f.positioning_placement_top.value + (isNum(f.positioning_placement_top.value) ? f.positioning_placement_top_measurement.value : ""); + ce.style.top = s; + ce.style.right = s; + ce.style.bottom = s; + ce.style.left = s; + } + + if (!f.positioning_clip_same.checked) { + s = "rect("; + s += (isNum(f.positioning_clip_top.value) ? f.positioning_clip_top.value + f.positioning_clip_top_measurement.value : "auto") + " "; + s += (isNum(f.positioning_clip_right.value) ? f.positioning_clip_right.value + f.positioning_clip_right_measurement.value : "auto") + " "; + s += (isNum(f.positioning_clip_bottom.value) ? f.positioning_clip_bottom.value + f.positioning_clip_bottom_measurement.value : "auto") + " "; + s += (isNum(f.positioning_clip_left.value) ? f.positioning_clip_left.value + f.positioning_clip_left_measurement.value : "auto"); + s += ")"; + + if (s != "rect(auto auto auto auto)") + ce.style.clip = s; + } else { + s = "rect("; + t = isNum(f.positioning_clip_top.value) ? f.positioning_clip_top.value + f.positioning_clip_top_measurement.value : "auto"; + s += t + " "; + s += t + " "; + s += t + " "; + s += t + ")"; + + if (s != "rect(auto auto auto auto)") + ce.style.clip = s; + } + + ce.style.cssText = tinyMCE.serializeStyle(tinyMCE.parseStyle(ce.style.cssText)); +} + +function isNum(s) { + return new RegExp('[0-9]+', 'g').test(s); +} + +function showDisabledControls() { + var f = document.forms, i, a; + + for (i=0; i 1) { + addSelectValue(f, s, p[0], p[1]); + + if (se) + selectByValue(f, s, p[1]); + } else { + addSelectValue(f, s, p[0], p[0]); + + if (se) + selectByValue(f, s, p[0]); + } + } +} + +function toggleSame(ce, pre) { + var el = document.forms[0].elements, i; + + if (ce.checked) { + el[pre + "_top"].disabled = false; + el[pre + "_right"].disabled = true; + el[pre + "_bottom"].disabled = true; + el[pre + "_left"].disabled = true; + + if (el[pre + "_top_measurement"]) { + el[pre + "_top_measurement"].disabled = false; + el[pre + "_right_measurement"].disabled = true; + el[pre + "_bottom_measurement"].disabled = true; + el[pre + "_left_measurement"].disabled = true; + } + } else { + el[pre + "_top"].disabled = false; + el[pre + "_right"].disabled = false; + el[pre + "_bottom"].disabled = false; + el[pre + "_left"].disabled = false; + + if (el[pre + "_top_measurement"]) { + el[pre + "_top_measurement"].disabled = false; + el[pre + "_right_measurement"].disabled = false; + el[pre + "_bottom_measurement"].disabled = false; + el[pre + "_left_measurement"].disabled = false; + } + } + + showDisabledControls(); +} + +function synch(fr, to) { + var f = document.forms[0]; + + f.elements[to].value = f.elements[fr].value; + + if (f.elements[fr + "_measurement"]) + selectByValue(f, to + "_measurement", f.elements[fr + "_measurement"].value); +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/style/langs/en.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/style/langs/en.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,66 @@ +// UK lang variables + +tinyMCE.addToLang('style',{ +title : 'Edit CSS Style', +styleinfo_desc : 'Edit CSS Style', +apply : 'Apply', +text_tab : 'Text', +background_tab : 'Background', +block_tab : 'Block', +box_tab : 'Box', +border_tab : 'Border', +list_tab : 'List', +positioning_tab : 'Positioning', +text_props : 'Text', +text_font : 'Font', +text_size : 'Size', +text_weight : 'Weight', +text_style : 'Style', +text_variant : 'Variant', +text_lineheight : 'Line height', +text_case : 'Case', +text_color : 'Color', +text_decoration : 'Decoration', +text_overline : 'overline', +text_underline : 'underline', +text_striketrough : 'strikethrough', +text_blink : 'blink', +text_none : 'none', +background_color : 'Background color', +background_image : 'Background image', +background_repeat : 'Repeat', +background_attachment : 'Attachment', +background_hpos : 'Horizontal position', +background_vpos : 'Vertical position', +block_wordspacing : 'Word spacing', +block_letterspacing : 'Letter spacing', +block_vertical_alignment : 'Vertical alignment', +block_text_align : 'Text align', +block_text_indent : 'Text indent', +block_whitespace : 'Whitespace', +block_display : 'Display', +box_width : 'Width', +box_height : 'Height', +box_float : 'Float', +box_clear : 'Clear', +padding : 'Padding', +same : 'Same for all', +top : 'Top', +right : 'Right', +bottom : 'Bottom', +left : 'Left', +margin : 'Margin', +style : 'Style', +width : 'Width', +height : 'Height', +color : 'Color', +list_type : 'Type', +bullet_image : 'Bullet image', +position : 'Position', +positioning_type : 'Type', +visibility : 'Visibility', +zindex : 'Z-index', +overflow : 'Overflow', +placement : 'Placement', +clip : 'Clip' +}); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/style/props.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/style/props.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,729 @@ + + + {$lang_style_title} + + + + + + + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
 
+
+ +
+ + + +
+ + + + + + +
+ +  
+
+ +
+ + + + + +
 
+
{$lang_style_text_decoration} + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
 
+
+ + + + +
 
+
+ + + + + + +
 
+
+ + + + + + +
 
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
 
+
+ + + + + + +
 
+
+ + + + + + +
 
+
+
+ +
+ + + + + + + + + + + + + + +
+ + + + + + +
 
+
   
+ + + + + + +
 
+
   
+
+
+ {$lang_style_padding} + + + + + + + + + + + + + + + + + + + + + + +
 
+ + + + + + +
 
+
+ + + + + + +
 
+
+ + + + + + +
 
+
+ + + + + + +
 
+
+
+
+ +
+
+ {$lang_style_margin} + + + + + + + + + + + + + + + + + + + + + + +
 
+ + + + + + +
 
+
+ + + + + + +
 
+
+ + + + + + +
 
+
+ + + + + + +
 
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  {$lang_style_style} {$lang_style_width} {$lang_style_color}
      
{$lang_style_top}   + + + + + + +
 
+
  + + + + + +
 
+
{$lang_style_right}   + + + + + + +
 
+
  + + + + + +
 
+
{$lang_style_bottom}   + + + + + + +
 
+
  + + + + + +
 
+
{$lang_style_left}   + + + + + + +
 
+
  + + + + + +
 
+
+
+ +
+ + + + + + + + + + + + + + + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
   
+ + + + + + +
 
+
   
+ + + + + + +
 
+
   
+ +
+
+ {$lang_style_placement} + + + + + + + + + + + + + + + + + + + + + + +
 
{$lang_style_top} + + + + + + +
 
+
{$lang_style_right} + + + + + + +
 
+
{$lang_style_bottom} + + + + + + +
 
+
{$lang_style_left} + + + + + + +
 
+
+
+
+ +
+
+ {$lang_style_clip} + + + + + + + + + + + + + + + + + + + + + + +
 
{$lang_style_top} + + + + + + +
 
+
{$lang_style_right} + + + + + + +
 
+
{$lang_style_bottom} + + + + + + +
 
+
{$lang_style_left} + + + + + + +
 
+
+
+
+
+
+
+ +
+
+
+ +
 
+
+
+ +
+ +
+
+
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/style/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/style/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +Check the TinyMCE documentation for details on this plugin. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/cell.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/table/cell.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,182 @@ + + + {$lang_table_cell_title} + + + + + + + + +
+ + +
+
+
+ {$lang_table_general_props} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ +
+
+
+ +
+
+ {$lang_table_advanced_props} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + + + +
 
+
+ + + + + +
 
+
+ + + + + +
 
+
+
+
+
+ +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/css/cell.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/table/css/cell.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,17 @@ +/* CSS file for cell dialog in the table plugin */ + +.panel_wrapper div.current { + height: 200px; +} + +.advfield { + width: 200px; +} + +#action { + margin-bottom: 3px; +} + +#class { + width: 150px; +} \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/css/row.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/table/css/row.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,25 @@ +/* CSS file for row dialog in the table plugin */ + +.panel_wrapper div.current { + height: 200px; +} + +.advfield { + width: 200px; +} + +#action { + margin-bottom: 3px; +} + +#rowtype,#align,#valign,#class,#height { + width: 150px; +} + +#height { + width: 50px; +} + +.col2 { + padding-left: 20px; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/css/table.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/table/css/table.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,13 @@ +/* CSS file for table dialog in the table plugin */ + +.panel_wrapper div.current { + height: 220px; +} + +.advfield { + width: 200px; +} + +#class { + width: 150px; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/table/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('table');var TinyMCE_TablePlugin={getInfo:function(){return{longname:'Tables',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/table',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},initInstance:function(inst){if(tinyMCE.isGecko){var doc=inst.getDoc();tinyMCE.addEvent(doc,"mouseup",TinyMCE_TablePlugin._mouseDownHandler)}inst.tableRowClipboard=null},getControlHTML:function(control_name){var controls=new Array(['table','table.gif','lang_table_desc','mceInsertTable',true],['delete_table','table_delete.gif','lang_table_del','mceTableDelete'],['delete_col','table_delete_col.gif','lang_table_delete_col_desc','mceTableDeleteCol'],['delete_row','table_delete_row.gif','lang_table_delete_row_desc','mceTableDeleteRow'],['col_after','table_insert_col_after.gif','lang_table_col_after_desc','mceTableInsertColAfter'],['col_before','table_insert_col_before.gif','lang_table_col_before_desc','mceTableInsertColBefore'],['row_after','table_insert_row_after.gif','lang_table_row_after_desc','mceTableInsertRowAfter'],['row_before','table_insert_row_before.gif','lang_table_row_before_desc','mceTableInsertRowBefore'],['row_props','table_row_props.gif','lang_table_row_desc','mceTableRowProps',true],['cell_props','table_cell_props.gif','lang_table_cell_desc','mceTableCellProps',true],['split_cells','table_split_cells.gif','lang_table_split_cells_desc','mceTableSplitCells',true],['merge_cells','table_merge_cells.gif','lang_table_merge_cells_desc','mceTableMergeCells',true]);for(var i=0;i4?but[4]:false)+(but.length>5?', \''+but[5]+'\'':'')+');return false;';if(but[0]==control_name)return tinyMCE.getButtonHTML(control_name,but[2],'{$pluginurl}/images/'+but[1],but[3],(but.length>4?but[4]:false))}if(control_name=="tablecontrols"){var html="";html+=tinyMCE.getControlHTML("table");html+=tinyMCE.getControlHTML("separator");html+=tinyMCE.getControlHTML("row_props");html+=tinyMCE.getControlHTML("cell_props");html+=tinyMCE.getControlHTML("separator");html+=tinyMCE.getControlHTML("row_before");html+=tinyMCE.getControlHTML("row_after");html+=tinyMCE.getControlHTML("delete_row");html+=tinyMCE.getControlHTML("separator");html+=tinyMCE.getControlHTML("col_before");html+=tinyMCE.getControlHTML("col_after");html+=tinyMCE.getControlHTML("delete_col");html+=tinyMCE.getControlHTML("separator");html+=tinyMCE.getControlHTML("split_cells");html+=tinyMCE.getControlHTML("merge_cells");return html}return""},execCommand:function(editor_id,element,command,user_interface,value){switch(command){case"mceInsertTable":case"mceTableRowProps":case"mceTableCellProps":case"mceTableSplitCells":case"mceTableMergeCells":case"mceTableInsertRowBefore":case"mceTableInsertRowAfter":case"mceTableDeleteRow":case"mceTableInsertColBefore":case"mceTableInsertColAfter":case"mceTableDeleteCol":case"mceTableCutRow":case"mceTableCopyRow":case"mceTablePasteRowBefore":case"mceTablePasteRowAfter":case"mceTableDelete":var inst=tinyMCE.getInstanceById(editor_id);inst.execCommand('mceBeginUndoLevel');TinyMCE_TablePlugin._doExecCommand(editor_id,element,command,user_interface,value);inst.execCommand('mceEndUndoLevel');return true}return false},handleNodeChange:function(editor_id,node,undo_index,undo_levels,visual_aid,any_selection){var colspan="1",rowspan="1",tdElm;var inst=tinyMCE.getInstanceById(editor_id);tinyMCE.switchClass(editor_id+'_table','mceButtonNormal');tinyMCE.switchClass(editor_id+'_delete_table','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_row_props','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_cell_props','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_row_before','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_row_after','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_delete_row','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_col_before','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_col_after','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_delete_col','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_split_cells','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_merge_cells','mceButtonDisabled');if(tdElm=tinyMCE.getParentElement(node,"td,th")){tinyMCE.switchClass(editor_id+'_cell_props','mceButtonSelected');tinyMCE.switchClass(editor_id+'_delete_table','mceButtonNormal');tinyMCE.switchClass(editor_id+'_row_before','mceButtonNormal');tinyMCE.switchClass(editor_id+'_row_after','mceButtonNormal');tinyMCE.switchClass(editor_id+'_delete_row','mceButtonNormal');tinyMCE.switchClass(editor_id+'_col_before','mceButtonNormal');tinyMCE.switchClass(editor_id+'_col_after','mceButtonNormal');tinyMCE.switchClass(editor_id+'_delete_col','mceButtonNormal');colspan=tinyMCE.getAttrib(tdElm,"colspan");rowspan=tinyMCE.getAttrib(tdElm,"rowspan");colspan=colspan==""?"1":colspan;rowspan=rowspan==""?"1":rowspan;if(colspan!="1"||rowspan!="1")tinyMCE.switchClass(editor_id+'_split_cells','mceButtonNormal')}if(tinyMCE.getParentElement(node,"tr"))tinyMCE.switchClass(editor_id+'_row_props','mceButtonSelected');if(tinyMCE.getParentElement(node,"table")){tinyMCE.switchClass(editor_id+'_table','mceButtonSelected');tinyMCE.switchClass(editor_id+'_merge_cells','mceButtonNormal')}},_mouseDownHandler:function(e){var elm=tinyMCE.isMSIE?event.srcElement:e.target;var focusElm=tinyMCE.selectedInstance.getFocusElement();if(elm.nodeName=="BODY"&&(focusElm.nodeName=="TD"||focusElm.nodeName=="TH"||(focusElm.parentNode&&focusElm.parentNode.nodeName=="TD")||(focusElm.parentNode&&focusElm.parentNode.nodeName=="TH"))){window.setTimeout(function(){var tableElm=tinyMCE.getParentElement(focusElm,"table");tinyMCE.handleVisualAid(tableElm,true,tinyMCE.settings['visual'],tinyMCE.selectedInstance)},10)}},_doExecCommand:function(editor_id,element,command,user_interface,value){var inst=tinyMCE.getInstanceById(editor_id);var focusElm=inst.getFocusElement();var trElm=tinyMCE.getParentElement(focusElm,"tr");var tdElm=tinyMCE.getParentElement(focusElm,"td,th");var tableElm=tinyMCE.getParentElement(focusElm,"table");var doc=inst.contentWindow.document;var tableBorder=tableElm?tableElm.getAttribute("border"):"";if(trElm&&tdElm==null)tdElm=trElm.cells[0];function inArray(ar,v){for(var i=0;i0&&inArray(ar[i],v))return true;if(ar[i]==v)return true}return false}function makeTD(){var newTD=doc.createElement("td");newTD.innerHTML=" "}function getColRowSpan(td){var colspan=tinyMCE.getAttrib(td,"colspan");var rowspan=tinyMCE.getAttrib(td,"rowspan");colspan=colspan==""?1:parseInt(colspan);rowspan=rowspan==""?1:parseInt(rowspan);return{colspan:colspan,rowspan:rowspan}}function getCellPos(grid,td){var x,y;for(y=0;y1){for(var i=x;i1)td.rowSpan=sd.rowspan+1;lastElm=td}deleteMarked(tableElm)}}function prevElm(node,name){while((node=node.previousSibling)!=null){if(node.nodeName==name)return node}return null}function nextElm(node,names){var namesAr=names.split(',');while((node=node.nextSibling)!=null){for(var i=0;i1){do{var nexttd=nextElm(td,"TD,TH");if(td._delete)td.parentNode.removeChild(td)}while((td=nexttd)!=null)}}while((tr=next)!=null)}function addRows(td_elm,tr_elm,rowspan){td_elm.rowSpan=1;var trNext=nextElm(tr_elm,"TR");for(var i=1;i1){var newTD=cells[x].cloneNode(true);var sd=getColRowSpan(cells[x]);newTD.rowSpan=sd.rowspan-1;var nextTD=nextTR.cells[x];if(nextTD==null)nextTR.appendChild(newTD);else nextTR.insertBefore(newTD,nextTD)}}var lastTDElm=null;for(var x=0;tdElm=getCell(grid,cpos.rowindex,x);x++){if(tdElm!=lastTDElm){var sd=getColRowSpan(tdElm);if(sd.rowspan>1){tdElm.rowSpan=sd.rowspan-1}else{trElm=tdElm.parentNode;if(trElm.parentNode)trElm._delete=true}lastTDElm=tdElm}}deleteMarked(tableElm);cpos.rowindex--;if(cpos.rowindex<0)cpos.rowindex=0;grid=getTableGrid(tableElm);inst.selection.selectNode(getCell(grid,cpos.rowindex,0),tinyMCE.isGecko,true);break;case"mceTableInsertColBefore":if(!trElm||!tdElm)return true;var grid=getTableGrid(tableElm);var cpos=getCellPos(grid,tdElm);var lastTDElm=null;for(var y=0;tdElm=getCell(grid,y,cpos.cellindex);y++){if(tdElm!=lastTDElm){var sd=getColRowSpan(tdElm);if(sd['colspan']==1){var newTD=doc.createElement(tdElm.nodeName);newTD.innerHTML=" ";newTD.rowSpan=tdElm.rowSpan;tdElm.parentNode.insertBefore(newTD,tdElm)}else tdElm.colSpan++;lastTDElm=tdElm}}grid=getTableGrid(tableElm);inst.selection.selectNode(getCell(grid,cpos.rowindex,cpos.cellindex+1),tinyMCE.isGecko,true);break;case"mceTableInsertColAfter":if(!trElm||!tdElm)return true;var grid=getTableGrid(tableElm);var cpos=getCellPos(grid,tdElm);var lastTDElm=null;for(var y=0;tdElm=getCell(grid,y,cpos.cellindex);y++){if(tdElm!=lastTDElm){var sd=getColRowSpan(tdElm);if(sd['colspan']==1){var newTD=doc.createElement(tdElm.nodeName);newTD.innerHTML=" ";newTD.rowSpan=tdElm.rowSpan;var nextTD=nextElm(tdElm,"TD,TH");if(nextTD==null)tdElm.parentNode.appendChild(newTD);else nextTD.parentNode.insertBefore(newTD,nextTD)}else tdElm.colSpan++;lastTDElm=tdElm}}grid=getTableGrid(tableElm);inst.selection.selectNode(getCell(grid,cpos.rowindex,cpos.cellindex),tinyMCE.isGecko,true);break;case"mceTableDeleteCol":if(!trElm||!tdElm)return true;var grid=getTableGrid(tableElm);var cpos=getCellPos(grid,tdElm);var lastTDElm=null;if(grid.length>1&&grid[0].length<=1){tableElm=tinyMCE.getParentElement(tableElm,"table");tableElm.parentNode.removeChild(tableElm);return true}for(var y=0;tdElm=getCell(grid,y,cpos.cellindex);y++){if(tdElm!=lastTDElm){var sd=getColRowSpan(tdElm);if(sd['colspan']>1)tdElm.colSpan=sd['colspan']-1;else{if(tdElm.parentNode)tdElm.parentNode.removeChild(tdElm)}lastTDElm=tdElm}}cpos.cellindex--;if(cpos.cellindex<0)cpos.cellindex=0;grid=getTableGrid(tableElm);inst.selection.selectNode(getCell(grid,cpos.rowindex,0),tinyMCE.isGecko,true);break;case"mceTableSplitCells":if(!trElm||!tdElm)return true;var spandata=getColRowSpan(tdElm);var colspan=spandata["colspan"];var rowspan=spandata["rowspan"];if(colspan>1||rowspan>1){tdElm.colSpan=1;for(var i=1;i1)addRows(newTD,trElm,rowspan)}addRows(tdElm,trElm,rowspan)}tableElm=tinyMCE.getParentElement(inst.getFocusElement(),"table");break;case"mceTableMergeCells":var rows=new Array();var sel=inst.getSel();var grid=getTableGrid(tableElm);if(tinyMCE.isMSIE||sel.rangeCount==1){if(user_interface){var template=new Array();var sp=getColRowSpan(tdElm);template['file']='../../plugins/table/merge_cells.htm';template['width']=250;template['height']=105+(tinyMCE.isNS7?25:0);template['width']+=tinyMCE.getLang('lang_table_merge_cells_delta_width',0);template['height']+=tinyMCE.getLang('lang_table_merge_cells_delta_height',0);tinyMCE.openWindow(template,{editor_id:inst.editorId,inline:"yes",action:"update",numcols:sp.colspan,numrows:sp.rowspan});return true}else{var numRows=parseInt(value['numrows']);var numCols=parseInt(value['numcols']);var cpos=getCellPos(grid,tdElm);if((""+numRows)=="NaN")numRows=1;if((""+numCols)=="NaN")numCols=1;var tRows=tableElm.rows;for(var y=cpos.rowindex;y0)rows[rows.length]=rowCells}}}else{var cells=new Array();var sel=inst.getSel();var lastTR=null;var curRow=null;var x1=-1,y1=-1,x2,y2;if(sel.rangeCount<2)return true;for(var i=0;i0)rows[rows.length]=rowCells}var curRow=new Array();var lastTR=null;for(var y=0;ycolSpan)colSpan=rowColSpan;lastRowSpan=-1}var lastColSpan=-1;for(var x=0;xrowSpan)rowSpan=colRowSpan;lastColSpan=-1}tdElm=rows[0][0];tdElm.rowSpan=rowSpan;tdElm.colSpan=colSpan;for(var y=0;y0))tdElm.innerHTML+=html;if(rows[y][x]!=tdElm&&!rows[y][x]._deleted){var cpos=getCellPos(grid,rows[y][x]);var tr=rows[y][x].parentNode;tr.removeChild(rows[y][x]);rows[y][x]._deleted=true;if(!tr.hasChildNodes()){tr.parentNode.removeChild(tr);var lastCell=null;for(var x=0;cellElm=getCell(grid,cpos.rowindex,x);x++){if(cellElm!=lastCell&&cellElm.rowSpan>1)cellElm.rowSpan--;lastCell=cellElm}if(tdElm.rowSpan>1)tdElm.rowSpan--}}}}break}tableElm=tinyMCE.getParentElement(inst.getFocusElement(),"table");tinyMCE.handleVisualAid(tableElm,true,tinyMCE.settings['visual'],tinyMCE.selectedInstance);tinyMCE.triggerNodeChange();inst.repaint()}return true}return false}};tinyMCE.addPlugin("table",TinyMCE_TablePlugin); \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/editor_plugin_src.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/table/editor_plugin_src.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1073 @@ +/** + * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $ + * + * @author Moxiecode + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +/* Import plugin specific language pack */ +tinyMCE.importPluginLanguagePack('table'); + +var TinyMCE_TablePlugin = { + getInfo : function() { + return { + longname : 'Tables', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/table', + version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion + }; + }, + + initInstance : function(inst) { + if (tinyMCE.isGecko) { + var doc = inst.getDoc(); + tinyMCE.addEvent(doc, "mouseup", TinyMCE_TablePlugin._mouseDownHandler); + } + + inst.tableRowClipboard = null; + }, + + /** + * Returns the HTML contents of the table control. + */ + getControlHTML : function(control_name) { + var controls = new Array( + ['table', 'table.gif', 'lang_table_desc', 'mceInsertTable', true], + ['delete_table', 'table_delete.gif', 'lang_table_del', 'mceTableDelete'], + ['delete_col', 'table_delete_col.gif', 'lang_table_delete_col_desc', 'mceTableDeleteCol'], + ['delete_row', 'table_delete_row.gif', 'lang_table_delete_row_desc', 'mceTableDeleteRow'], + ['col_after', 'table_insert_col_after.gif', 'lang_table_col_after_desc', 'mceTableInsertColAfter'], + ['col_before', 'table_insert_col_before.gif', 'lang_table_col_before_desc', 'mceTableInsertColBefore'], + ['row_after', 'table_insert_row_after.gif', 'lang_table_row_after_desc', 'mceTableInsertRowAfter'], + ['row_before', 'table_insert_row_before.gif', 'lang_table_row_before_desc', 'mceTableInsertRowBefore'], + ['row_props', 'table_row_props.gif', 'lang_table_row_desc', 'mceTableRowProps', true], + ['cell_props', 'table_cell_props.gif', 'lang_table_cell_desc', 'mceTableCellProps', true], + ['split_cells', 'table_split_cells.gif', 'lang_table_split_cells_desc', 'mceTableSplitCells', true], + ['merge_cells', 'table_merge_cells.gif', 'lang_table_merge_cells_desc', 'mceTableMergeCells', true]); + + // Render table control + for (var i=0; i 4 ? but[4] : false) + (but.length > 5 ? ', \'' + but[5] + '\'' : '') + ');return false;'; + + if (but[0] == control_name) + return tinyMCE.getButtonHTML(control_name, but[2], '{$pluginurl}/images/'+ but[1], but[3], (but.length > 4 ? but[4] : false)); + } + + // Special tablecontrols + if (control_name == "tablecontrols") { + var html = ""; + + html += tinyMCE.getControlHTML("table"); + html += tinyMCE.getControlHTML("separator"); + html += tinyMCE.getControlHTML("row_props"); + html += tinyMCE.getControlHTML("cell_props"); + html += tinyMCE.getControlHTML("separator"); + html += tinyMCE.getControlHTML("row_before"); + html += tinyMCE.getControlHTML("row_after"); + html += tinyMCE.getControlHTML("delete_row"); + html += tinyMCE.getControlHTML("separator"); + html += tinyMCE.getControlHTML("col_before"); + html += tinyMCE.getControlHTML("col_after"); + html += tinyMCE.getControlHTML("delete_col"); + html += tinyMCE.getControlHTML("separator"); + html += tinyMCE.getControlHTML("split_cells"); + html += tinyMCE.getControlHTML("merge_cells"); + + return html; + } + + return ""; + }, + + /** + * Executes the table commands. + */ + execCommand : function(editor_id, element, command, user_interface, value) { + // Is table command + switch (command) { + case "mceInsertTable": + case "mceTableRowProps": + case "mceTableCellProps": + case "mceTableSplitCells": + case "mceTableMergeCells": + case "mceTableInsertRowBefore": + case "mceTableInsertRowAfter": + case "mceTableDeleteRow": + case "mceTableInsertColBefore": + case "mceTableInsertColAfter": + case "mceTableDeleteCol": + case "mceTableCutRow": + case "mceTableCopyRow": + case "mceTablePasteRowBefore": + case "mceTablePasteRowAfter": + case "mceTableDelete": + var inst = tinyMCE.getInstanceById(editor_id); + + inst.execCommand('mceBeginUndoLevel'); + TinyMCE_TablePlugin._doExecCommand(editor_id, element, command, user_interface, value); + inst.execCommand('mceEndUndoLevel'); + + return true; + } + + // Pass to next handler in chain + return false; + }, + + handleNodeChange : function(editor_id, node, undo_index, undo_levels, visual_aid, any_selection) { + var colspan = "1", rowspan = "1", tdElm; + + var inst = tinyMCE.getInstanceById(editor_id); + + // Reset table controls + tinyMCE.switchClass(editor_id + '_table', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_delete_table', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_row_props', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_cell_props', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_row_before', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_row_after', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_delete_row', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_col_before', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_col_after', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_delete_col', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_split_cells', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_merge_cells', 'mceButtonDisabled'); + + // Within a td element + if (tdElm = tinyMCE.getParentElement(node, "td,th")) { + tinyMCE.switchClass(editor_id + '_cell_props', 'mceButtonSelected'); + tinyMCE.switchClass(editor_id + '_delete_table', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_row_before', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_row_after', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_delete_row', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_col_before', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_col_after', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_delete_col', 'mceButtonNormal'); + + colspan = tinyMCE.getAttrib(tdElm, "colspan"); + rowspan = tinyMCE.getAttrib(tdElm, "rowspan"); + + colspan = colspan == "" ? "1" : colspan; + rowspan = rowspan == "" ? "1" : rowspan; + + if (colspan != "1" || rowspan != "1") + tinyMCE.switchClass(editor_id + '_split_cells', 'mceButtonNormal'); + } + + // Within a tr element + if (tinyMCE.getParentElement(node, "tr")) + tinyMCE.switchClass(editor_id + '_row_props', 'mceButtonSelected'); + + // Within table + if (tinyMCE.getParentElement(node, "table")) { + tinyMCE.switchClass(editor_id + '_table', 'mceButtonSelected'); + tinyMCE.switchClass(editor_id + '_merge_cells', 'mceButtonNormal'); + } + }, + + // Private plugin internal methods + + _mouseDownHandler : function(e) { + var elm = tinyMCE.isMSIE ? event.srcElement : e.target; + var focusElm = tinyMCE.selectedInstance.getFocusElement(); + + // If press on special Mozilla create TD/TR thingie + if (elm.nodeName == "BODY" && (focusElm.nodeName == "TD" || focusElm.nodeName == "TH" || (focusElm.parentNode && focusElm.parentNode.nodeName == "TD") ||(focusElm.parentNode && focusElm.parentNode.nodeName == "TH") )) { + window.setTimeout(function() { + var tableElm = tinyMCE.getParentElement(focusElm, "table"); + tinyMCE.handleVisualAid(tableElm, true, tinyMCE.settings['visual'], tinyMCE.selectedInstance); + }, 10); + } + }, + + /** + * Executes the table commands. + */ + _doExecCommand : function(editor_id, element, command, user_interface, value) { + var inst = tinyMCE.getInstanceById(editor_id); + var focusElm = inst.getFocusElement(); + var trElm = tinyMCE.getParentElement(focusElm, "tr"); + var tdElm = tinyMCE.getParentElement(focusElm, "td,th"); + var tableElm = tinyMCE.getParentElement(focusElm, "table"); + var doc = inst.contentWindow.document; + var tableBorder = tableElm ? tableElm.getAttribute("border") : ""; + + // Get first TD if no TD found + if (trElm && tdElm == null) + tdElm = trElm.cells[0]; + + // ------- Inner functions --------- + function inArray(ar, v) { + for (var i=0; i 0 && inArray(ar[i], v)) + return true; + + // Found value + if (ar[i] == v) + return true; + } + + return false; + } + + function makeTD() { + var newTD = doc.createElement("td"); + newTD.innerHTML = " "; + } + + function getColRowSpan(td) { + var colspan = tinyMCE.getAttrib(td, "colspan"); + var rowspan = tinyMCE.getAttrib(td, "rowspan"); + + colspan = colspan == "" ? 1 : parseInt(colspan); + rowspan = rowspan == "" ? 1 : parseInt(rowspan); + + return {colspan : colspan, rowspan : rowspan}; + } + + function getCellPos(grid, td) { + var x, y; + + for (y=0; y 1) { // Remove due to colspan + for (var i=x; i 1) + td.rowSpan = sd.rowspan + 1; + + lastElm = td; + } + + deleteMarked(tableElm); + } + } + + function prevElm(node, name) { + while ((node = node.previousSibling) != null) { + if (node.nodeName == name) + return node; + } + + return null; + } + + function nextElm(node, names) { + var namesAr = names.split(','); + + while ((node = node.nextSibling) != null) { + for (var i=0; i 1) { + do { + var nexttd = nextElm(td, "TD,TH"); + + if (td._delete) + td.parentNode.removeChild(td); + } while ((td = nexttd) != null); + } + } while ((tr = next) != null); + } + + function addRows(td_elm, tr_elm, rowspan) { + // Add rows + td_elm.rowSpan = 1; + var trNext = nextElm(tr_elm, "TR"); + for (var i=1; i 1) { + var newTD = cells[x].cloneNode(true); + var sd = getColRowSpan(cells[x]); + + newTD.rowSpan = sd.rowspan - 1; + + var nextTD = nextTR.cells[x]; + + if (nextTD == null) + nextTR.appendChild(newTD); + else + nextTR.insertBefore(newTD, nextTD); + } + } + + // Delete cells + var lastTDElm = null; + for (var x=0; tdElm = getCell(grid, cpos.rowindex, x); x++) { + if (tdElm != lastTDElm) { + var sd = getColRowSpan(tdElm); + + if (sd.rowspan > 1) { + tdElm.rowSpan = sd.rowspan - 1; + } else { + trElm = tdElm.parentNode; + + if (trElm.parentNode) + trElm._delete = true; + } + + lastTDElm = tdElm; + } + } + + deleteMarked(tableElm); + + cpos.rowindex--; + if (cpos.rowindex < 0) + cpos.rowindex = 0; + + // Recalculate grid and select + grid = getTableGrid(tableElm); + inst.selection.selectNode(getCell(grid, cpos.rowindex, 0), tinyMCE.isGecko, true); // Only collape on gecko + break; + + case "mceTableInsertColBefore": + if (!trElm || !tdElm) + return true; + + var grid = getTableGrid(tableElm); + var cpos = getCellPos(grid, tdElm); + var lastTDElm = null; + + for (var y=0; tdElm = getCell(grid, y, cpos.cellindex); y++) { + if (tdElm != lastTDElm) { + var sd = getColRowSpan(tdElm); + + if (sd['colspan'] == 1) { + var newTD = doc.createElement(tdElm.nodeName); + + newTD.innerHTML = " "; + newTD.rowSpan = tdElm.rowSpan; + + tdElm.parentNode.insertBefore(newTD, tdElm); + } else + tdElm.colSpan++; + + lastTDElm = tdElm; + } + } + + grid = getTableGrid(tableElm); + inst.selection.selectNode(getCell(grid, cpos.rowindex, cpos.cellindex + 1), tinyMCE.isGecko, true); // Only collape on gecko + break; + + case "mceTableInsertColAfter": + if (!trElm || !tdElm) + return true; + + var grid = getTableGrid(tableElm); + var cpos = getCellPos(grid, tdElm); + var lastTDElm = null; + + for (var y=0; tdElm = getCell(grid, y, cpos.cellindex); y++) { + if (tdElm != lastTDElm) { + var sd = getColRowSpan(tdElm); + + if (sd['colspan'] == 1) { + var newTD = doc.createElement(tdElm.nodeName); + + newTD.innerHTML = " "; + newTD.rowSpan = tdElm.rowSpan; + + var nextTD = nextElm(tdElm, "TD,TH"); + if (nextTD == null) + tdElm.parentNode.appendChild(newTD); + else + nextTD.parentNode.insertBefore(newTD, nextTD); + } else + tdElm.colSpan++; + + lastTDElm = tdElm; + } + } + + grid = getTableGrid(tableElm); + inst.selection.selectNode(getCell(grid, cpos.rowindex, cpos.cellindex), tinyMCE.isGecko, true); // Only collape on gecko + break; + + case "mceTableDeleteCol": + if (!trElm || !tdElm) + return true; + + var grid = getTableGrid(tableElm); + var cpos = getCellPos(grid, tdElm); + var lastTDElm = null; + + // Only one col, remove whole table + if (grid.length > 1 && grid[0].length <= 1) { + tableElm = tinyMCE.getParentElement(tableElm, "table"); // Look for table instead of tbody + tableElm.parentNode.removeChild(tableElm); + return true; + } + + // Delete cells + for (var y=0; tdElm = getCell(grid, y, cpos.cellindex); y++) { + if (tdElm != lastTDElm) { + var sd = getColRowSpan(tdElm); + + if (sd['colspan'] > 1) + tdElm.colSpan = sd['colspan'] - 1; + else { + if (tdElm.parentNode) + tdElm.parentNode.removeChild(tdElm); + } + + lastTDElm = tdElm; + } + } + + cpos.cellindex--; + if (cpos.cellindex < 0) + cpos.cellindex = 0; + + // Recalculate grid and select + grid = getTableGrid(tableElm); + inst.selection.selectNode(getCell(grid, cpos.rowindex, 0), tinyMCE.isGecko, true); // Only collape on gecko + break; + + case "mceTableSplitCells": + if (!trElm || !tdElm) + return true; + + var spandata = getColRowSpan(tdElm); + + var colspan = spandata["colspan"]; + var rowspan = spandata["rowspan"]; + + // Needs splitting + if (colspan > 1 || rowspan > 1) { + // Generate cols + tdElm.colSpan = 1; + for (var i=1; i 1) + addRows(newTD, trElm, rowspan); + } + + addRows(tdElm, trElm, rowspan); + } + + // Apply visual aids + tableElm = tinyMCE.getParentElement(inst.getFocusElement(), "table"); + break; + + case "mceTableMergeCells": + var rows = new Array(); + var sel = inst.getSel(); + var grid = getTableGrid(tableElm); + + if (tinyMCE.isMSIE || sel.rangeCount == 1) { + if (user_interface) { + // Setup template + var template = new Array(); + var sp = getColRowSpan(tdElm); + + template['file'] = '../../plugins/table/merge_cells.htm'; + template['width'] = 250; + template['height'] = 105 + (tinyMCE.isNS7 ? 25 : 0); + + // Language specific width and height addons + template['width'] += tinyMCE.getLang('lang_table_merge_cells_delta_width', 0); + template['height'] += tinyMCE.getLang('lang_table_merge_cells_delta_height', 0); + + // Open window + tinyMCE.openWindow(template, {editor_id : inst.editorId, inline : "yes", action : "update", numcols : sp.colspan, numrows : sp.rowspan}); + + return true; + } else { + var numRows = parseInt(value['numrows']); + var numCols = parseInt(value['numcols']); + var cpos = getCellPos(grid, tdElm); + + if (("" + numRows) == "NaN") + numRows = 1; + + if (("" + numCols) == "NaN") + numCols = 1; + + // Get rows and cells + var tRows = tableElm.rows; + for (var y=cpos.rowindex; y 0) + rows[rows.length] = rowCells; + } + + //return true; + } + } else { + var cells = new Array(); + var sel = inst.getSel(); + var lastTR = null; + var curRow = null; + var x1 = -1, y1 = -1, x2, y2; + + // Only one cell selected, whats the point? + if (sel.rangeCount < 2) + return true; + + // Get all selected cells + for (var i=0; i 0) + rows[rows.length] = rowCells; + } + + // Find selected cells in grid and box + var curRow = new Array(); + var lastTR = null; + for (var y=0; y colSpan) + colSpan = rowColSpan; + + lastRowSpan = -1; + } + + // Validate vertical and get total rowspan + var lastColSpan = -1; + for (var x=0; x rowSpan) + rowSpan = colRowSpan; + + lastColSpan = -1; + } + + // Setup td + tdElm = rows[0][0]; + tdElm.rowSpan = rowSpan; + tdElm.colSpan = colSpan; + + // Merge cells + for (var y=0; y 0)) + tdElm.innerHTML += html; + + // Not current cell + if (rows[y][x] != tdElm && !rows[y][x]._deleted) { + var cpos = getCellPos(grid, rows[y][x]); + var tr = rows[y][x].parentNode; + + tr.removeChild(rows[y][x]); + rows[y][x]._deleted = true; + + // Empty TR, remove it + if (!tr.hasChildNodes()) { + tr.parentNode.removeChild(tr); + + var lastCell = null; + for (var x=0; cellElm = getCell(grid, cpos.rowindex, x); x++) { + if (cellElm != lastCell && cellElm.rowSpan > 1) + cellElm.rowSpan--; + + lastCell = cellElm; + } + + if (tdElm.rowSpan > 1) + tdElm.rowSpan--; + } + } + } + } + + break; + } + + tableElm = tinyMCE.getParentElement(inst.getFocusElement(), "table"); + tinyMCE.handleVisualAid(tableElm, true, tinyMCE.settings['visual'], tinyMCE.selectedInstance); + tinyMCE.triggerNodeChange(); + inst.repaint(); + } + + return true; + } + + // Pass to next handler in chain + return false; + } +}; + +tinyMCE.addPlugin("table", TinyMCE_TablePlugin); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/images/buttons.gif Binary file includes/clientside/tinymce/plugins/table/images/buttons.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/images/table.gif Binary file includes/clientside/tinymce/plugins/table/images/table.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/images/table_cell_props.gif Binary file includes/clientside/tinymce/plugins/table/images/table_cell_props.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/images/table_delete.gif Binary file includes/clientside/tinymce/plugins/table/images/table_delete.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/images/table_delete_col.gif Binary file includes/clientside/tinymce/plugins/table/images/table_delete_col.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/images/table_delete_row.gif Binary file includes/clientside/tinymce/plugins/table/images/table_delete_row.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/images/table_insert_col_after.gif Binary file includes/clientside/tinymce/plugins/table/images/table_insert_col_after.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/images/table_insert_col_before.gif Binary file includes/clientside/tinymce/plugins/table/images/table_insert_col_before.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/images/table_insert_row_after.gif Binary file includes/clientside/tinymce/plugins/table/images/table_insert_row_after.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/images/table_insert_row_before.gif Binary file includes/clientside/tinymce/plugins/table/images/table_insert_row_before.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/images/table_merge_cells.gif Binary file includes/clientside/tinymce/plugins/table/images/table_merge_cells.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/images/table_row_props.gif Binary file includes/clientside/tinymce/plugins/table/images/table_row_props.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/images/table_split_cells.gif Binary file includes/clientside/tinymce/plugins/table/images/table_split_cells.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/jscripts/cell.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/table/jscripts/cell.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,249 @@ +function init() { + tinyMCEPopup.resizeToInnerSize(); + + document.getElementById('backgroundimagebrowsercontainer').innerHTML = getBrowserHTML('backgroundimagebrowser','backgroundimage','image','table'); + document.getElementById('bordercolor_pickcontainer').innerHTML = getColorPickerHTML('bordercolor_pick','bordercolor'); + document.getElementById('bgcolor_pickcontainer').innerHTML = getColorPickerHTML('bgcolor_pick','bgcolor') + + var inst = tinyMCE.selectedInstance; + var tdElm = tinyMCE.getParentElement(inst.getFocusElement(), "td,th"); + var formObj = document.forms[0]; + var st = tinyMCE.parseStyle(tinyMCE.getAttrib(tdElm, "style")); + + // Get table cell data + var celltype = tdElm.nodeName.toLowerCase(); + var align = tinyMCE.getAttrib(tdElm, 'align'); + var valign = tinyMCE.getAttrib(tdElm, 'valign'); + var width = trimSize(getStyle(tdElm, 'width', 'width')); + var height = trimSize(getStyle(tdElm, 'height', 'height')); + var bordercolor = convertRGBToHex(getStyle(tdElm, 'bordercolor', 'borderLeftColor')); + var bgcolor = convertRGBToHex(getStyle(tdElm, 'bgcolor', 'backgroundColor')); + var className = tinyMCE.getVisualAidClass(tinyMCE.getAttrib(tdElm, 'class'), false); + var backgroundimage = getStyle(tdElm, 'background', 'backgroundImage').replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1");; + var id = tinyMCE.getAttrib(tdElm, 'id'); + var lang = tinyMCE.getAttrib(tdElm, 'lang'); + var dir = tinyMCE.getAttrib(tdElm, 'dir'); + var scope = tinyMCE.getAttrib(tdElm, 'scope'); + + // Setup form + addClassesToList('class', 'table_cell_styles'); + formObj.bordercolor.value = bordercolor; + formObj.bgcolor.value = bgcolor; + formObj.backgroundimage.value = backgroundimage; + formObj.width.value = width; + formObj.height.value = height; + formObj.id.value = id; + formObj.lang.value = lang; + formObj.style.value = tinyMCE.serializeStyle(st); + selectByValue(formObj, 'align', align); + selectByValue(formObj, 'valign', valign); + selectByValue(formObj, 'class', className); + selectByValue(formObj, 'celltype', celltype); + selectByValue(formObj, 'dir', dir); + selectByValue(formObj, 'scope', scope); + + // Resize some elements + if (isVisible('backgroundimagebrowser')) + document.getElementById('backgroundimage').style.width = '180px'; + + updateColor('bordercolor_pick', 'bordercolor'); + updateColor('bgcolor_pick', 'bgcolor'); +} + +function updateAction() { + tinyMCEPopup.restoreSelection(); + + var inst = tinyMCE.selectedInstance; + var tdElm = tinyMCE.getParentElement(inst.getFocusElement(), "td,th"); + var trElm = tinyMCE.getParentElement(inst.getFocusElement(), "tr"); + var tableElm = tinyMCE.getParentElement(inst.getFocusElement(), "table"); + var formObj = document.forms[0]; + + inst.execCommand('mceBeginUndoLevel'); + + switch (getSelectValue(formObj, 'action')) { + case "cell": + var celltype = getSelectValue(formObj, 'celltype'); + var scope = getSelectValue(formObj, 'scope'); + + if (tinyMCE.getParam("accessibility_warnings")) { + if (celltype == "th" && scope == "") + var answer = confirm(tinyMCE.getLang('lang_table_missing_scope', '', true)); + else + var answer = true; + + if (!answer) + return; + } + + updateCell(tdElm); + break; + + case "row": + var cell = trElm.firstChild; + + if (cell.nodeName != "TD" && cell.nodeName != "TH") + cell = nextCell(cell); + + do { + cell = updateCell(cell, true); + } while ((cell = nextCell(cell)) != null); + + break; + + case "all": + var rows = tableElm.getElementsByTagName("tr"); + + for (var i=0; i colLimit) { + alert(tinyMCE.getLang('lang_table_col_limit', '', true, {cols : colLimit})); + return false; + } else if (rowLimit && rows > rowLimit) { + alert(tinyMCE.getLang('lang_table_row_limit', '', true, {rows : rowLimit})); + return false; + } else if (cellLimit && cols * rows > cellLimit) { + alert(tinyMCE.getLang('lang_table_cell_limit', '', true, {cells : cellLimit})); + return false; + } + + // Update table + if (action == "update") { + inst.execCommand('mceBeginUndoLevel'); + + tinyMCE.setAttrib(elm, 'cellPadding', cellpadding, true); + tinyMCE.setAttrib(elm, 'cellSpacing', cellspacing, true); + tinyMCE.setAttrib(elm, 'border', border, true); + tinyMCE.setAttrib(elm, 'align', align); + tinyMCE.setAttrib(elm, 'class', className); + tinyMCE.setAttrib(elm, 'style', style); + tinyMCE.setAttrib(elm, 'id', id); + tinyMCE.setAttrib(elm, 'summary', summary); + tinyMCE.setAttrib(elm, 'dir', dir); + tinyMCE.setAttrib(elm, 'lang', lang); + + capEl = elm.getElementsByTagName('caption')[0]; + + if (capEl && !caption) + capEl.parentNode.removeChild(capEl); + + if (!capEl && caption) { + capEl = elm.ownerDocument.createElement('caption'); + capEl.innerHTML = ' '; + elm.insertBefore(capEl, elm.firstChild); + } + + // Not inline styles + if (!tinyMCE.getParam("inline_styles")) + tinyMCE.setAttrib(elm, 'width', width, true); + + // Remove these since they are not valid XHTML + tinyMCE.setAttrib(elm, 'borderColor', ''); + tinyMCE.setAttrib(elm, 'bgColor', ''); + tinyMCE.setAttrib(elm, 'background', ''); + tinyMCE.setAttrib(elm, 'height', ''); + + if (background != '') + elm.style.backgroundImage = "url('" + background + "')"; + else + elm.style.backgroundImage = ''; + + if (tinyMCE.getParam("inline_styles")) + elm.style.borderWidth = border + "px"; + + if (tinyMCE.getParam("inline_styles")) { + if (width != '') + elm.style.width = getCSSSize(width); + } + + if (bordercolor != "") { + elm.style.borderColor = bordercolor; + elm.style.borderStyle = elm.style.borderStyle == "" ? "solid" : elm.style.borderStyle; + elm.style.borderWidth = border == "" ? "1px" : border; + } else + elm.style.borderColor = ''; + + elm.style.backgroundColor = bgcolor; + elm.style.height = getCSSSize(height); + + tinyMCE.handleVisualAid(tinyMCE.tableElm, false, inst.visualAid, inst); + + // Fix for stange MSIE align bug + tinyMCE.tableElm.outerHTML = tinyMCE.tableElm.outerHTML; + + tinyMCE.handleVisualAid(inst.getBody(), true, inst.visualAid, inst); + tinyMCE.triggerNodeChange(); + inst.execCommand('mceEndUndoLevel'); + + // Repaint if dimensions changed + if (formObj.width.value != orgTableWidth || formObj.height.value != orgTableHeight) + inst.repaint(); + + tinyMCEPopup.close(); + return true; + } + + // Create new table + html += '/g, '>'); + + return ' ' + attrib + '="' + value + '"'; +} + +function init() { + tinyMCEPopup.resizeToInnerSize(); + + document.getElementById('backgroundimagebrowsercontainer').innerHTML = getBrowserHTML('backgroundimagebrowser','backgroundimage','image','table'); + document.getElementById('backgroundimagebrowsercontainer').innerHTML = getBrowserHTML('backgroundimagebrowser','backgroundimage','image','table'); + document.getElementById('bordercolor_pickcontainer').innerHTML = getColorPickerHTML('bordercolor_pick','bordercolor'); + document.getElementById('bgcolor_pickcontainer').innerHTML = getColorPickerHTML('bgcolor_pick','bgcolor'); + + var cols = 2, rows = 2, border = tinyMCE.getParam('table_default_border', '0'), cellpadding = tinyMCE.getParam('table_default_cellpadding', ''), cellspacing = tinyMCE.getParam('table_default_cellspacing', ''); + var align = "", width = "", height = "", bordercolor = "", bgcolor = "", className = ""; + var id = "", summary = "", style = "", dir = "", lang = "", background = "", bgcolor = "", bordercolor = ""; + var inst = tinyMCE.selectedInstance; + var formObj = document.forms[0]; + var elm = tinyMCE.getParentElement(inst.getFocusElement(), "table"); + + tinyMCE.tableElm = elm; + action = tinyMCE.getWindowArg('action'); + if (action == null) + action = tinyMCE.tableElm ? "update" : "insert"; + + if (tinyMCE.tableElm && action != "insert") { + var rowsAr = tinyMCE.tableElm.rows; + var cols = 0; + for (var i=0; i cols) + cols = rowsAr[i].cells.length; + + cols = cols; + rows = rowsAr.length; + + st = tinyMCE.parseStyle(tinyMCE.getAttrib(tinyMCE.tableElm, "style")); + border = trimSize(getStyle(elm, 'border', 'borderWidth')); + cellpadding = tinyMCE.getAttrib(tinyMCE.tableElm, 'cellpadding', ""); + cellspacing = tinyMCE.getAttrib(tinyMCE.tableElm, 'cellspacing', ""); + width = trimSize(getStyle(elm, 'width', 'width')); + height = trimSize(getStyle(elm, 'height', 'height')); + bordercolor = convertRGBToHex(getStyle(elm, 'bordercolor', 'borderLeftColor')); + bgcolor = convertRGBToHex(getStyle(elm, 'bgcolor', 'backgroundColor')); + align = tinyMCE.getAttrib(tinyMCE.tableElm, 'align', align); + className = tinyMCE.getVisualAidClass(tinyMCE.getAttrib(tinyMCE.tableElm, 'class'), false); + id = tinyMCE.getAttrib(tinyMCE.tableElm, 'id'); + summary = tinyMCE.getAttrib(tinyMCE.tableElm, 'summary'); + style = tinyMCE.serializeStyle(st); + dir = tinyMCE.getAttrib(tinyMCE.tableElm, 'dir'); + lang = tinyMCE.getAttrib(tinyMCE.tableElm, 'lang'); + background = getStyle(elm, 'background', 'backgroundImage').replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1"); + formObj.caption.checked = tinyMCE.tableElm.getElementsByTagName('caption').length > 0; + + orgTableWidth = width; + orgTableHeight = height; + + action = "update"; + } + + addClassesToList('class', "table_styles"); + + // Update form + selectByValue(formObj, 'align', align); + selectByValue(formObj, 'class', className); + formObj.cols.value = cols; + formObj.rows.value = rows; + formObj.border.value = border; + formObj.cellpadding.value = cellpadding; + formObj.cellspacing.value = cellspacing; + formObj.width.value = width; + formObj.height.value = height; + formObj.bordercolor.value = bordercolor; + formObj.bgcolor.value = bgcolor; + formObj.id.value = id; + formObj.summary.value = summary; + formObj.style.value = style; + formObj.dir.value = dir; + formObj.lang.value = lang; + formObj.backgroundimage.value = background; + formObj.insert.value = tinyMCE.getLang('lang_' + action, 'Insert', true); + + updateColor('bordercolor_pick', 'bordercolor'); + updateColor('bgcolor_pick', 'bgcolor'); + + // Resize some elements + if (isVisible('backgroundimagebrowser')) + document.getElementById('backgroundimage').style.width = '180px'; + + // Disable some fields in update mode + if (action == "update") { + formObj.cols.disabled = true; + formObj.rows.disabled = true; + } +} + +function changedSize() { + var formObj = document.forms[0]; + var st = tinyMCE.parseStyle(formObj.style.value); + + var width = formObj.width.value; + if (width != "") + st['width'] = tinyMCE.getParam("inline_styles") ? getCSSSize(width) : ""; + else + st['width'] = ""; + + var height = formObj.height.value; + if (height != "") + st['height'] = getCSSSize(height); + else + st['height'] = ""; + + formObj.style.value = tinyMCE.serializeStyle(st); +} + +function changedBackgroundImage() { + var formObj = document.forms[0]; + var st = tinyMCE.parseStyle(formObj.style.value); + + st['background-image'] = "url('" + formObj.backgroundimage.value + "')"; + + formObj.style.value = tinyMCE.serializeStyle(st); +} + +function changedBorder() { + var formObj = document.forms[0]; + var st = tinyMCE.parseStyle(formObj.style.value); + + // Update border width if the element has a color + if (formObj.border.value != "" && formObj.bordercolor.value != "") + st['border-width'] = formObj.border.value + "px"; + + formObj.style.value = tinyMCE.serializeStyle(st); +} + +function changedColor() { + var formObj = document.forms[0]; + var st = tinyMCE.parseStyle(formObj.style.value); + + st['background-color'] = formObj.bgcolor.value; + + if (formObj.bordercolor.value != "") { + st['border-color'] = formObj.bordercolor.value; + + // Add border-width if it's missing + if (!st['border-width']) + st['border-width'] = formObj.border.value == "" ? "1px" : formObj.border.value + "px"; + } + + formObj.style.value = tinyMCE.serializeStyle(st); +} + +function changedStyle() { + var formObj = document.forms[0]; + var st = tinyMCE.parseStyle(formObj.style.value); + + if (st['background-image']) + formObj.backgroundimage.value = st['background-image'].replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1"); + else + formObj.backgroundimage.value = ''; + + if (st['width']) + formObj.width.value = trimSize(st['width']); + + if (st['height']) + formObj.height.value = trimSize(st['height']); + + if (st['background-color']) { + formObj.bgcolor.value = st['background-color']; + updateColor('bgcolor_pick','bgcolor'); + } + + if (st['border-color']) { + formObj.bordercolor.value = st['border-color']; + updateColor('bordercolor_pick','bordercolor'); + } +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/langs/en.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/table/langs/en.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,79 @@ +// UK lang variables + +tinyMCE.addToLang('table',{ +general_tab : 'General', +advanced_tab : 'Advanced', +general_props : 'General properties', +advanced_props : 'Advanced properties', +desc : 'Inserts a new table', +row_before_desc : 'Insert row before', +row_after_desc : 'Insert row after', +delete_row_desc : 'Delete row', +col_before_desc : 'Insert column before', +col_after_desc : 'Insert column after', +delete_col_desc : 'Remove column', +rowtype : 'Row in table part', +title : 'Insert/Modify table', +width : 'Width', +height : 'Height', +cols : 'Columns', +rows : 'Rows', +cellspacing : 'Cellspacing', +cellpadding : 'Cellpadding', +border : 'Border', +align : 'Alignment', +align_default : 'Default', +align_left : 'Left', +align_right : 'Right', +align_middle : 'Center', +row_title : 'Table row properties', +cell_title : 'Table cell properties', +cell_type : 'Cell type', +row_desc : 'Table row properties', +cell_desc : 'Table cell properties', +valign : 'Vertical alignment', +align_top : 'Top', +align_bottom : 'Bottom', +props_desc : 'Table properties', +bordercolor : 'Border color', +bgcolor : 'Background color', +merge_cells_title : 'Merge table cells', +split_cells_desc : 'Split table cells', +merge_cells_desc : 'Merge table cells', +cut_row_desc : 'Cut table row', +copy_row_desc : 'Copy table row', +paste_row_before_desc : 'Paste table row before', +paste_row_after_desc : 'Paste table row after', +id : 'Id', +style: 'Style', +langdir : 'Language direction', +langcode : 'Language code', +mime : 'Target MIME type', +ltr : 'Left to right', +rtl : 'Right to left', +bgimage : 'Background image', +summary : 'Summary', +td : "Data", +th : "Header", +cell_cell : 'Update current cell', +cell_row : 'Update all cells in row', +cell_all : 'Update all cells in table', +row_row : 'Update current row', +row_odd : 'Update odd rows in table', +row_even : 'Update even rows in table', +row_all : 'Update all rows in table', +thead : 'Table Head', +tbody : 'Table Body', +tfoot : 'Table Foot', +del : 'Delete table', +scope : 'Scope', +row : 'Row', +col : 'Col', +rowgroup : 'Row Group', +colgroup : 'Col Group', +col_limit : 'You\'ve exceeded the maximum number of columns of {$cols}.', +row_limit : 'You\'ve exceeded the maximum number of rows of {$rows}.', +cell_limit : 'You\'ve exceeded the maximum number of cells of {$cells}.', +missing_scope: 'Are you sure you want to continue without specifying a scope for this table header cell. Without it, it may be difficult for some users with disabilities to understand the content or data displayed of the table.', +caption : 'Table caption' +}); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/merge_cells.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/table/merge_cells.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,38 @@ + + + {$lang_table_merge_cells_title} + + + + + + + +
+
+ {$lang_table_merge_cells_title} + + + + + + + + + +
{$lang_table_cols}: +
{$lang_table_rows}:
+
+ +
+
+ +
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/table/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +Check the TinyMCE documentation for details on this plugin. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/row.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/table/row.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,159 @@ + + + {$lang_table_row_title} + + + + + + + + +
+ + +
+
+
+ {$lang_table_general_props} + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ +
+
+
+ +
+
+ {$lang_table_advanced_props} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + + + +
 
+
+ + + + + +
 
+
+
+
+
+ +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/table/table.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/table/table.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,160 @@ + + + {$lang_table_title} + + + + + + + + + +
+ + +
+
+
+ {$lang_table_general_props} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ {$lang_table_advanced_props} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + + + +
 
+
+ + + + + +
 
+
+ + + + + +
 
+
+
+
+
+ +
+
+ +
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/template/blank.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/template/blank.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,23 @@ + + + blank_page + + + + + + + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/template/css/template.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/template/css/template.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,23 @@ +#frmbody { + padding: 10px; + background-color: #FFF; + border: 1px solid #CCC; +} + +.frmRow { + margin-bottom: 10px; +} + +#templatesrc { + border: none; + width: 320px; + height: 240px; +} + +.title { + padding-bottom: 5px; +} + +.mceActionPanel { + padding-top: 5px; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/template/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/template/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('template');var TinyMCE_TemplatePlugin={getInfo:function(){return{longname:'Template plugin',author:'Moxiecode Systems AB',authorurl:'http://www.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/template',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},initInstance:function(inst){var cdate,mdate,content,x=0,key,value,rvals,ds=inst.getData('template');cdate=tinyMCE.getParam("template_cdate_classes",'').split(/\s+/);mdate=tinyMCE.getParam("template_mdate_classes",'').split(/\s+/);content=tinyMCE.getParam("template_selected_content_classes",'').split(/\s+/);for(x=0;x0?nodeArray[0]:null;nodeArray=[];if(ds.currentAction=="insert"){if(telm){tinyMCE.execCommand('mceBeginUndoLevel');ds.currentAction="insert-new";TinyMCE_TemplatePlugin._insertTemplate(editor_id,telm,value.title,value.tsrc,true);ds.currentAction=="insert";tinyMCE.execCommand('mceEndUndoLevel');tinyMCE.execInstanceCommand(editor_id,'mceCleanup',false)}else tinyMCE.execCommand('mceInsertContent',false,this._replaceValues(value.body))}else{nodeArray=TinyMCE_TemplatePlugin._collectTemplateElements(ds.currentTmplNode);current=[];newTmpl=[];tinyMCE.getNodeTree(telm,newTmpl);for(x=0;x','gi'),'');content=content.replace(new RegExp('
(\s| | )?(|\s)?
','gi'),'');content=content.replace(new RegExp('','gi'),'');break;case"insert_to_editor":content=content.replace(new RegExp('','gi'),'
');content=content.replace(new RegExp('','gi'),'
');break;case"get_from_editor_dom":nodes=tinyMCE.selectNodes(content,function(n){return tinyMCE.hasCSSClass(n,TinyMCE_TemplatePlugin.TMPL_ELEMENT)});TinyMCE_TemplatePlugin._applyFunctions(nodes,type);break;case"insert_to_editor_dom":nodes=tinyMCE.selectNodes(content,function(n){return tinyMCE.hasCSSClass(n,TinyMCE_TemplatePlugin.TMPL_ELEMENT)});TinyMCE_TemplatePlugin._applyFunctions(nodes,type);break}return content},_convertToNode:function(html){var elm=document.createElement('div');elm.innerHTML=html;return elm},_prepareTemplateContent:function(elms){var x,n,nodes=[];if(!elms)return{};if(!elms.length)elms=[elms];for(x=0;x';html+=elm.innerHTML;html+='';tinyMCE.execInstanceCommand(editor_id,'mceInsertContent',false,html)},functions:{blank:function(elm,editor_event){},cdate:function(elm,editor_event){var d,dsrc;if(editor_event!=TinyMCE_TemplatePlugin.TMPL_TEMPLATE_EVENT)return;d=new Date();dsrc=elm.innerHTML.match(new RegExp("","gi"));if(dsrc)d=new Date(RegExp.$1);elm.innerHTML=TinyMCE_TemplatePlugin._getDateTime(d,tinyMCE.getParam("template_cdate_format",tinyMCE.getLang("lang_template_def_date_format")));elm.innerHTML+=""},mdate:function(elm,editor_event){var d=new Date();elm.innerHTML=TinyMCE_TemplatePlugin._getDateTime(d,tinyMCE.getParam("template_mdate_format",tinyMCE.getLang("lang_template_def_date_format")))},selectedContent:function(elm,editor_event){var ds=tinyMCE.selectedInstance.getData('template');if(editor_event!=TinyMCE_TemplatePlugin.TMPL_TEMPLATE_EVENT)return;if(ds.currentAction=="insert-new"&&!tinyMCE.hasCSSClass(elm,TinyMCE_TemplatePlugin.TMPL_SEL_HTML_DONE)){elm.innerHTML=tinyMCE.selectedInstance.selection.getSelectedHTML();tinyMCE.addCSSClass(elm,TinyMCE_TemplatePlugin.TMPL_SEL_HTML_DONE)}},generateReplacer:function(s){return function(elm,editor_event){elm.innerHTML=""+s}}},_getDateTime:function(d,fmt){if(!fmt)return"";function addZeros(value,len){var i;value=""+value;if(value.length 0 ? nodeArray[0] : null; + nodeArray = []; + + if (ds.currentAction == "insert") { + //insert new template after applying all the template content functions + + // Is it a template or snippet + if (telm) { + tinyMCE.execCommand('mceBeginUndoLevel'); + ds.currentAction = "insert-new"; + TinyMCE_TemplatePlugin._insertTemplate(editor_id, telm, value.title, value.tsrc, true); + ds.currentAction == "insert"; + tinyMCE.execCommand('mceEndUndoLevel'); + tinyMCE.execInstanceCommand(editor_id, 'mceCleanup', false); + } else + tinyMCE.execCommand('mceInsertContent', false, this._replaceValues(value.body)); + } else { + // First collect the selected template in the editor + nodeArray = TinyMCE_TemplatePlugin._collectTemplateElements(ds.currentTmplNode); + current = []; + newTmpl = []; + tinyMCE.getNodeTree(telm, newTmpl); + + for (x=0; x', 'gi'), + '' + ); + + // delete any empty template wrappers + content = content.replace( + new RegExp('
(\s| | )?(|\s)?
', 'gi'), + '' + ); + + // replace the closing wrapper tag + content = content.replace( + new RegExp('', 'gi'), + '' + ); + + break; + + case "insert_to_editor": + // replace HTML comment with DIV wrapper + content = content.replace( + new RegExp('', 'gi'), + '
' + ); + + content = content.replace( + new RegExp('', 'gi'), + '
' + ); + + break; + + case "get_from_editor_dom": + // apply template content replacement functions + nodes = tinyMCE.selectNodes(content, function(n) { + return tinyMCE.hasCSSClass(n, TinyMCE_TemplatePlugin.TMPL_ELEMENT); + } + ); + + TinyMCE_TemplatePlugin._applyFunctions(nodes, type); + + break; + + case "insert_to_editor_dom": + // apply template content replacement functions + nodes = tinyMCE.selectNodes(content, function(n) { + return tinyMCE.hasCSSClass(n, TinyMCE_TemplatePlugin.TMPL_ELEMENT); + } + ); + + TinyMCE_TemplatePlugin._applyFunctions(nodes, type); + + break; + } + + return content; + }, + + // Private plugin internal methods + + /** + * Creates a HTML DIV element and sets the innerHTML to equal the temlate innerHTML so that the template can be manipulated as DOM nodes. + * + * @param {string} Template innerHTML + * @return a HTML Element + * @type HTMLElement + */ + _convertToNode : function(html) { + var elm = document.createElement('div'); + + elm.innerHTML = html; + + return elm; + }, + + /** + * pass an array of template html elements and they will have the template class name added and any template functions applied + * + * @param {array} template HTML elements + * @return array of template HTML elements + * @type array + */ + _prepareTemplateContent : function(elms) { + var x, n, nodes = []; + + if (!elms) + return {}; + + if (!elms.length) + elms = [elms]; + + for (x = 0; x'; + html += elm.innerHTML; + html += ''; + + tinyMCE.execInstanceCommand(editor_id, 'mceInsertContent', false, html); + }, + + /** + * template functions - functions for modifying template content + */ + functions : { + blank : function(elm, editor_event) {}, + + cdate : function(elm, editor_event) { + var d, dsrc; + + if (editor_event != TinyMCE_TemplatePlugin.TMPL_TEMPLATE_EVENT) + return; + + d = new Date(); + // find out if the creation date was previously stored + dsrc = elm.innerHTML.match(new RegExp("", "gi")); + + if (dsrc) + d = new Date(RegExp.$1); + + elm.innerHTML = TinyMCE_TemplatePlugin._getDateTime(d, tinyMCE.getParam("template_cdate_format", tinyMCE.getLang("lang_template_def_date_format"))); + //now we have to store the date value in a format easily read again, in case a future template change changes the date format... + elm.innerHTML += ""; + }, + + mdate : function(elm, editor_event) { + var d = new Date(); + elm.innerHTML = TinyMCE_TemplatePlugin._getDateTime(d, tinyMCE.getParam("template_mdate_format", tinyMCE.getLang("lang_template_def_date_format"))); + }, + + /** + * This will insert the currently selected editor content into the template element. + * It only does this if the template inserted is a new one and if the element does not have the special class. + * The special class name prevents this from happening more than once. + */ + selectedContent : function(elm, editor_event) { + var ds = tinyMCE.selectedInstance.getData('template'); + + if (editor_event != TinyMCE_TemplatePlugin.TMPL_TEMPLATE_EVENT) + return; + + if (ds.currentAction == "insert-new" && !tinyMCE.hasCSSClass(elm, TinyMCE_TemplatePlugin.TMPL_SEL_HTML_DONE)) { + elm.innerHTML = tinyMCE.selectedInstance.selection.getSelectedHTML(); + tinyMCE.addCSSClass(elm, TinyMCE_TemplatePlugin.TMPL_SEL_HTML_DONE); + } + }, + + /** + * When the plugin is initialised this generates the functions that insert configured strings into template elements. + */ + generateReplacer : function(s) { + return function(elm, editor_event) {elm.innerHTML = "" + s;}; + } + }, + + /** + * formats a date according to the format string - straight from the 'insert date/time' plugin + * + * @param {Date} date object + * @param {string} format string + * @return formatted date + * @type string + */ + _getDateTime : function(d,fmt) { + if (!fmt) + return ""; + + function addZeros(value, len) { + var i; + + value = "" + value; + + if (value.length < len) { + for (i=0; i<(len-value.length); i++) + value = "0" + value; + } + + return value; + } + + fmt = fmt.replace("%D", "%m/%d/%y"); + fmt = fmt.replace("%r", "%I:%M:%S %p"); + fmt = fmt.replace("%Y", "" + d.getFullYear()); + fmt = fmt.replace("%y", "" + d.getYear()); + fmt = fmt.replace("%m", addZeros(d.getMonth()+1, 2)); + fmt = fmt.replace("%d", addZeros(d.getDate(), 2)); + fmt = fmt.replace("%H", "" + addZeros(d.getHours(), 2)); + fmt = fmt.replace("%M", "" + addZeros(d.getMinutes(), 2)); + fmt = fmt.replace("%S", "" + addZeros(d.getSeconds(), 2)); + fmt = fmt.replace("%I", "" + ((d.getHours() + 11) % 12 + 1)); + fmt = fmt.replace("%p", "" + (d.getHours() < 12 ? "AM" : "PM")); + fmt = fmt.replace("%B", "" + tinyMCE.getLang("lang_template_months_long")[d.getMonth()]); + fmt = fmt.replace("%b", "" + tinyMCE.getLang("lang_template_months_short")[d.getMonth()]); + fmt = fmt.replace("%A", "" + tinyMCE.getLang("lang_template_day_long")[d.getDay()]); + fmt = fmt.replace("%a", "" + tinyMCE.getLang("lang_template_day_short")[d.getDay()]); + fmt = fmt.replace("%%", "%"); + + return fmt; + }, + + TMPL_ELEMENT : 'mceTmplElm', + TMPL : 'mceTmpl', + TMPL_BEGINS : 'mceTmplBegins', + TMPL_SEL_HTML_DONE : 'mceSelHTMLDone', + TMPL_ENDS : 'mceTmplEnds', + TMPL_DATE_SRC_ATTR : 'mcetmpldtesrc', + TMPL_TEMPLATE_EVENT : 'prepare_template' +}; + +tinyMCE.addPlugin("template", TinyMCE_TemplatePlugin); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/template/images/template.gif Binary file includes/clientside/tinymce/plugins/template/images/template.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/template/jscripts/template.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/template/jscripts/template.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,143 @@ +// Import external list url javascript +var url = tinyMCE.getParam("template_external_list_url"); +if (url != null) { + // Fix relative + if (url.charAt(0) != '/' && url.indexOf('://') == -1) + url = tinyMCE.documentBasePath + "/" + url; + + document.write(''); +} + +var TPU = { //Template Popup Utils + currentTemplateHTML : null, + templates : [], + inst : tinyMCE.getInstanceById(tinyMCE.getWindowArg('editor_id')), + plugin : tinyMCE.getWindowArg('pluginObj'), + data : tinyMCE.selectedInstance.getData('template'), + + init : function() { + document.forms[0].insert.value = tinyMCE.getLang('lang_' + this.data.currentAction, 'Insert', true); + TPU.loadTemplatePaths(); + + if (this.data.currentAction == "update") + document.getElementById('warning').innerHTML = tinyMCE.getLang('lang_template_warning'); + + this.resizeInputs(); + }, + + loadTemplatePaths : function() { + var tsrc, sel, x, u; + + tsrc = tinyMCE.getParam("template_templates", false); + sel = document.getElementById('tpath'); + + // Setup external template list + if (!tsrc && typeof(tinyMCETemplateList) != 'undefined') { + for (x=0, tsrc = []; x' + + 'blank_page' + + '' + + '' + + '' + + this.currentTemplateHTML + + '' + + ''; + } + + // Write HTML to preview iframe + d.body.innerHTML = this.currentTemplateHTML; + + // Display description + for (x = 0; x < TPU.templates.length; x++) { + if (TPU.templates[x].src == o.value) { + document.getElementById('tmpldesc').innerHTML = TPU.templates[x].description; + break; + } + } + }, + + insertTemplate : function() { + var sel, opt; + + sel = document.getElementById('tpath'); + opt = sel.options[sel.selectedIndex]; + + // Is it a template or snippet + if (TPU.currentTemplateHTML.indexOf('mceTmpl')) + tinyMCEPopup.execCommand('mceTemplate', false, {title : opt.text, tsrc : opt.value, body : TPU.currentTemplateHTML}); + else + tinyMCEPopup.execCommand('mceInsertContent', false, TPU.currentTemplateHTML); + + tinyMCEPopup.close(); + }, + + getFileContents : function(u) { + var x, d, t = 'text/plain'; + + function g(s) { + x = 0; + + try { + x = new ActiveXObject(s); + } catch (s) { + } + + return x; + }; + + x = window.ActiveXObject ? g('Msxml2.XMLHTTP') || g('Microsoft.XMLHTTP') : new XMLHttpRequest(); + + // Synchronous AJAX load file + x.overrideMimeType && x.overrideMimeType(t); + x.open("GET", u, false); + x.send(null); + + return x.responseText; + }, + + resizeInputs : function() { + var wHeight, wWidth, elm; + + if (!self.innerWidth) { + wHeight = document.body.clientHeight - 160; + wWidth = document.body.clientWidth - 40; + } else { + wHeight = self.innerHeight - 160; + wWidth = self.innerWidth - 40; + } + + elm = document.getElementById('templatesrc'); + + if (elm) { + elm.style.height = Math.abs(wHeight) + 'px'; + elm.style.width = Math.abs(wWidth - 5) + 'px'; + } + } +}; diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/template/langs/en.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/template/langs/en.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,16 @@ +// UK lang variables + +tinyMCE.addToLang('template',{ +title : 'Templates', +label : 'Template', +desc_label : 'Description', +desc : 'Insert predefined template content', +select : 'Select a template', +preview : 'Preview', +warning : 'Warning: Updating a template with a different one may cause data loss.', +def_date_format : '%Y-%m-%d %H:%M:%S', +months_long : new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"), +months_short : new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"), +day_long : new Array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"), +day_short : new Array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun") +}); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/template/template.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/template/template.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,37 @@ + + + {$lang_template_title} + + + + + + +
+
+
{$lang_template_desc}
+
+ +
+
+
+
+ {$lang_template_preview} + +
+
+ +
+
+ +
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/visualchars/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/visualchars/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('visualchars');var TinyMCE_VisualCharsPlugin={getInfo:function(){return{longname:'Visual characters',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/visualchars',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},initInstance:function(inst){inst.visualChars={state:false}},getControlHTML:function(cn){switch(cn){case"visualchars":return tinyMCE.getButtonHTML(cn,'lang_visualchars_desc','{$pluginurl}/images/visualchars.gif','mceVisualChars',false)}return""},execCommand:function(editor_id,element,command,user_interface,value){var inst=tinyMCE.getInstanceById(editor_id);switch(command){case"mceVisualChars":this._toggleVisualChars(editor_id,inst);return true}return false},cleanup:function(type,content,inst){if(type=="insert_to_editor_dom"||type=="get_from_editor_dom"){inst.visualChars.state=true;this._toggleVisualChars(inst.editorId,inst)}return content},_toggleVisualChars:function(editor_id,inst){var nl,i,h,d=inst.getDoc(),b=inst.getBody(),nv,s=inst.selection,bo;inst.visualChars.state=!inst.visualChars.state;bo=s.getBookmark(true);tinyMCE.switchClass(editor_id+'_visualchars',inst.visualChars.state?'mceButtonSelected':'mceButtonNormal');if(inst.visualChars.state){nl=tinyMCE.selectNodes(b,function(n){return n.nodeType==3&&n.nodeValue&&n.nodeValue.indexOf('\u00a0')!=-1});for(i=0;i$1');nv=nv.replace(/\u00a0/g,'\u00b7');tinyMCE.setOuterHTML(nl[i],nv,d)}}else{nl=tinyMCE.selectNodes(b,function(n){return n.nodeType==1&&n.nodeName=='SPAN'&&n.className=='mceItemHiddenVisualChar'});for(i=0;i$1'); + nv = nv.replace(/\u00a0/g, '\u00b7'); + tinyMCE.setOuterHTML(nl[i], nv, d); + } + } else { + nl = tinyMCE.selectNodes(b, function(n) {return n.nodeType == 1 && n.nodeName == 'SPAN' && n.className == 'mceItemHiddenVisualChar';}); + + for (i=0; i + + {$lang_xhtmlxtras_title_abbr_element} + + + + + + + + + + +
+ + +
+
+
+ {$lang_xhtmlxtras_fieldset_attrib_tab} + + + + + + + + + + + + + + + + + + + + + + + + + +
:
:
: + +
:
: + +
: + +
+
+
+
+
+ {$lang_xhtmlxtras_fieldset_events_tab} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:
:
:
:
:
:
:
:
:
:
:
:
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/acronym.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/acronym.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,148 @@ + + + {$lang_xhtmlxtras_title_acronym_element} + + + + + + + + + + +
+ + +
+
+
+ {$lang_xhtmlxtras_fieldset_attrib_tab} + + + + + + + + + + + + + + + + + + + + + + + + + +
:
:
: + +
:
: + +
: + +
+
+
+
+
+ {$lang_xhtmlxtras_fieldset_events_tab} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:
:
:
:
:
:
:
:
:
:
:
:
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/attributes.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/attributes.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,153 @@ + + + {$lang_xhtmlxtras_attribs_title} + + + + + + + + + +
+ + +
+
+
+ {$lang_xhtmlxtras_attribute_attrib_tab} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:
:
+ +
:
: + +
: + +
+
+
+
+
+ {$lang_xhtmlxtras_attribute_events_tab} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:
:
:
:
:
:
:
:
:
:
:
:
+
+
+
+
+
+ +
+
+ +
+
+ +
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/cite.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/cite.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,148 @@ + + + {$lang_xhtmlxtras_title_cite_element} + + + + + + + + + + +
+ + +
+
+
+ {$lang_xhtmlxtras_fieldset_attrib_tab} + + + + + + + + + + + + + + + + + + + + + + + + + +
:
:
: + +
:
: + +
: + +
+
+
+
+
+ {$lang_xhtmlxtras_fieldset_events_tab} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:
:
:
:
:
:
:
:
:
:
:
:
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/css/attributes.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/css/attributes.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,11 @@ +.panel_wrapper div.current { + height: 290px; +} + +#id, #style, #title, #dir, #hreflang, #lang, #classlist, #tabindex, #accesskey { + width: 200px; +} + +#events_panel input { + width: 200px; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/css/popup.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/css/popup.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,46 @@ +a.mceButtonNormal img, a.mceButtonSelected img {border: 1px solid #F0F0EE !important;} +a.mceButtonNormal img:hover, a.mceButtonSelected img:hover {border: 1px solid #0A246A !important; cursor: default; background-color: #B6BDD2;} + +img { + border: 0; +} + +input.field, select.field { + width: 200px; +} + +input.picker { + width: 179px; + margin-left: 5px; +} + +input.disabled { + border-color: #F2F2F2; +} + +img.picker { + vertical-align: text-bottom; + cursor: pointer; +} + +h1 { + padding: 0 0 5px 0; +} + +#remove { + font-weight: bold; + width: 90px; + height: 21px; + border: 0px; + background-image: url('../images/remove_button_bg.gif'); + cursor: pointer; + margin-left: 3px; +} + +.panel_wrapper div.current { + height: 160px; +} + +#xhtmlxtrasdel .panel_wrapper div.current, #xhtmlxtrasins .panel_wrapper div.current { + height: 220px; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/css/xhtmlxtras.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/css/xhtmlxtras.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,24 @@ +ins { + border-bottom: 1px solid green; + text-decoration: none; + color: green; +} + +del { + color: Red; + text-decoration: line-through; +} + +cite { + border-bottom: 1px dashed blue; +} + +acronym { + border-bottom: 1px dotted #CCC; + cursor: help; +} + +abbr, html\:abbr { + border-bottom: 1px dashed #CCC; + cursor: help; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/del.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/del.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,169 @@ + + + {$lang_xhtmlxtras_title_del_element} + + + + + + + + + + +
+ + +
+
+
+ {$lang_xhtmlxtras_fieldset_general_tab} + + + + + + + + + +
: + + + + + +
{$lang_xhtmlxtras_insert_date}
+
:
+
+
+ {$lang_xhtmlxtras_fieldset_attrib_tab} + + + + + + + + + + + + + + + + + + + + + + + + + +
:
:
: + +
:
: + +
: + +
+
+
+
+
+ {$lang_xhtmlxtras_fieldset_events_tab} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:
:
:
:
:
:
:
:
:
:
:
:
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+ + + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/editor_plugin.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/editor_plugin.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importPluginLanguagePack('xhtmlxtras');var TinyMCE_XHTMLXtrasPlugin={getInfo:function(){return{longname:'XHTML Xtras Plugin',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/xhtmlxtras',version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion}},initInstance:function(inst){tinyMCE.importCSS(inst.getDoc(),tinyMCE.baseURL+"/plugins/xhtmlxtras/css/xhtmlxtras.css")},getControlHTML:function(cn){switch(cn){case"cite":return tinyMCE.getButtonHTML(cn,'lang_xhtmlxtras_cite_desc','{$pluginurl}/images/cite.gif','mceCite',true);case"acronym":return tinyMCE.getButtonHTML(cn,'lang_xhtmlxtras_acronym_desc','{$pluginurl}/images/acronym.gif','mceAcronym',true);case"abbr":return tinyMCE.getButtonHTML(cn,'lang_xhtmlxtras_abbr_desc','{$pluginurl}/images/abbr.gif','mceAbbr',true);case"del":return tinyMCE.getButtonHTML(cn,'lang_xhtmlxtras_del_desc','{$pluginurl}/images/del.gif','mceDel',true);case"ins":return tinyMCE.getButtonHTML(cn,'lang_xhtmlxtras_ins_desc','{$pluginurl}/images/ins.gif','mceIns',true);case"attribs":return tinyMCE.getButtonHTML(cn,'lang_xhtmlxtras_attribs_desc','{$pluginurl}/images/attribs.gif','mceAttributes',true)}return""},execCommand:function(editor_id,element,command,user_interface,value){var template,inst,elm;switch(command){case"mceCite":if(!this._anySel(editor_id))return true;template=new Array();template['file']='../../plugins/xhtmlxtras/cite.htm';template['width']=350;template['height']=250;tinyMCE.openWindow(template,{editor_id:editor_id});return true;case"mceAcronym":if(!this._anySel(editor_id))return true;template=new Array();template['file']='../../plugins/xhtmlxtras/acronym.htm';template['width']=350;template['height']=250;tinyMCE.openWindow(template,{editor_id:editor_id});return true;case"mceAbbr":if(!this._anySel(editor_id))return true;template=new Array();template['file']='../../plugins/xhtmlxtras/abbr.htm';template['width']=350;template['height']=250;tinyMCE.openWindow(template,{editor_id:editor_id});return true;case"mceIns":if(!this._anySel(editor_id))return true;template=new Array();template['file']='../../plugins/xhtmlxtras/ins.htm';template['width']=350;template['height']=310;tinyMCE.openWindow(template,{editor_id:editor_id});return true;case"mceDel":if(!this._anySel(editor_id))return true;template=new Array();template['file']='../../plugins/xhtmlxtras/del.htm';template['width']=350;template['height']=310;tinyMCE.openWindow(template,{editor_id:editor_id});return true;case"mceAttributes":inst=tinyMCE.getInstanceById(editor_id);elm=inst.getFocusElement();if(elm&&elm.nodeName!=='BODY'&&elm.className.indexOf('mceItem')==-1){tinyMCE.openWindow({file:'../../plugins/xhtmlxtras/attributes.htm',width:380,height:370},{editor_id:editor_id})}return true}return false},cleanup:function(type,content,inst){if(type=='insert_to_editor'&&tinyMCE.isIE&&!tinyMCE.isOpera){content=content.replace(/]+)>/gi,'');content=content.replace(/<\/abbr>/gi,'')}return content},handleNodeChange:function(editor_id,node,undo_index,undo_levels,visual_aid,any_selection){var elm=tinyMCE.getParentElement(node);if(node==null)return;tinyMCE.switchClass(editor_id+'_attribs','mceButtonDisabled');if(!any_selection){tinyMCE.switchClass(editor_id+'_cite','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_acronym','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_abbr','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_del','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_ins','mceButtonDisabled')}else{tinyMCE.switchClass(editor_id+'_cite','mceButtonNormal');tinyMCE.switchClass(editor_id+'_acronym','mceButtonNormal');tinyMCE.switchClass(editor_id+'_abbr','mceButtonNormal');tinyMCE.switchClass(editor_id+'_del','mceButtonNormal');tinyMCE.switchClass(editor_id+'_ins','mceButtonNormal')}if(elm&&elm.nodeName!='BODY'&&elm.className.indexOf('mceItem')==-1)tinyMCE.switchClass(editor_id+'_attribs','mceButtonNormal');switch(node.nodeName){case"CITE":tinyMCE.switchClass(editor_id+'_cite','mceButtonSelected');return true;case"ACRONYM":tinyMCE.switchClass(editor_id+'_acronym','mceButtonSelected');return true;case"abbr":case"HTML:ABBR":case"ABBR":tinyMCE.switchClass(editor_id+'_abbr','mceButtonSelected');return true;case"DEL":tinyMCE.switchClass(editor_id+'_del','mceButtonSelected');return true;case"INS":tinyMCE.switchClass(editor_id+'_ins','mceButtonSelected');return true}return true},_anySel:function(editor_id){var inst=tinyMCE.getInstanceById(editor_id),t=inst.selection.getSelectedText(),pe;pe=tinyMCE.getParentElement(inst.getFocusElement(),'CITE,ACRONYM,ABBR,HTML:ABBR,DEL,INS');return pe||inst.getFocusElement().nodeName=="IMG"||(t&&t.length>0)}};tinyMCE.addPlugin("xhtmlxtras",TinyMCE_XHTMLXtrasPlugin); \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/editor_plugin_src.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/editor_plugin_src.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,199 @@ + /** + * $Id: editor_plugin_src.js 42 2006-08-08 14:32:24Z spocke $ + * + * @author Moxiecode - based on work by Andrew Tetlaw + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +/* Import plugin specific language pack */ +tinyMCE.importPluginLanguagePack('xhtmlxtras'); + +var TinyMCE_XHTMLXtrasPlugin = { + getInfo : function() { + return { + longname : 'XHTML Xtras Plugin', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/xhtmlxtras', + version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion + }; + }, + + initInstance : function(inst) { + tinyMCE.importCSS(inst.getDoc(), tinyMCE.baseURL + "/plugins/xhtmlxtras/css/xhtmlxtras.css"); + }, + + getControlHTML : function(cn) { + switch (cn) { + case "cite": + return tinyMCE.getButtonHTML(cn, 'lang_xhtmlxtras_cite_desc', '{$pluginurl}/images/cite.gif', 'mceCite', true); + + case "acronym": + return tinyMCE.getButtonHTML(cn, 'lang_xhtmlxtras_acronym_desc', '{$pluginurl}/images/acronym.gif', 'mceAcronym', true); + + case "abbr": + return tinyMCE.getButtonHTML(cn, 'lang_xhtmlxtras_abbr_desc', '{$pluginurl}/images/abbr.gif', 'mceAbbr', true); + + case "del": + return tinyMCE.getButtonHTML(cn, 'lang_xhtmlxtras_del_desc', '{$pluginurl}/images/del.gif', 'mceDel', true); + + case "ins": + return tinyMCE.getButtonHTML(cn, 'lang_xhtmlxtras_ins_desc', '{$pluginurl}/images/ins.gif', 'mceIns', true); + + case "attribs": + return tinyMCE.getButtonHTML(cn, 'lang_xhtmlxtras_attribs_desc', '{$pluginurl}/images/attribs.gif', 'mceAttributes', true); + } + + return ""; + }, + + execCommand : function(editor_id, element, command, user_interface, value) { + var template, inst, elm; + + switch (command) { + case "mceCite": + if (!this._anySel(editor_id)) + return true; + + template = new Array(); + template['file'] = '../../plugins/xhtmlxtras/cite.htm'; + template['width'] = 350; + template['height'] = 250; + tinyMCE.openWindow(template, {editor_id : editor_id}); + return true; + + case "mceAcronym": + if (!this._anySel(editor_id)) + return true; + + template = new Array(); + template['file'] = '../../plugins/xhtmlxtras/acronym.htm'; + template['width'] = 350; + template['height'] = 250; + tinyMCE.openWindow(template, {editor_id : editor_id}); + return true; + + case "mceAbbr": + if (!this._anySel(editor_id)) + return true; + + template = new Array(); + template['file'] = '../../plugins/xhtmlxtras/abbr.htm'; + template['width'] = 350; + template['height'] = 250; + tinyMCE.openWindow(template, {editor_id : editor_id}); + return true; + + case "mceIns": + if (!this._anySel(editor_id)) + return true; + + template = new Array(); + template['file'] = '../../plugins/xhtmlxtras/ins.htm'; + template['width'] = 350; + template['height'] = 310; + tinyMCE.openWindow(template, {editor_id : editor_id}); + return true; + + case "mceDel": + if (!this._anySel(editor_id)) + return true; + + template = new Array(); + template['file'] = '../../plugins/xhtmlxtras/del.htm'; + template['width'] = 350; + template['height'] = 310; + tinyMCE.openWindow(template, {editor_id : editor_id}); + return true; + + case "mceAttributes": + inst = tinyMCE.getInstanceById(editor_id); + elm = inst.getFocusElement(); + + if (elm && elm.nodeName !== 'BODY' && elm.className.indexOf('mceItem') == -1) { + tinyMCE.openWindow({ + file : '../../plugins/xhtmlxtras/attributes.htm', + width : 380, + height : 370 + }, {editor_id : editor_id}); + } + + return true; + } + + return false; + }, + + cleanup : function(type, content, inst) { + if (type == 'insert_to_editor' && tinyMCE.isIE && !tinyMCE.isOpera) { + content = content.replace(/]+)>/gi, ''); + content = content.replace(/<\/abbr>/gi, ''); + } + + return content; + }, + + handleNodeChange : function(editor_id, node, undo_index,undo_levels, visual_aid, any_selection) { + var elm = tinyMCE.getParentElement(node); + + if (node == null) + return; + + tinyMCE.switchClass(editor_id + '_attribs', 'mceButtonDisabled'); + + if (!any_selection) { + // Disable the buttons + tinyMCE.switchClass(editor_id + '_cite', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_acronym', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_abbr', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_del', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_ins', 'mceButtonDisabled'); + } else { + // A selection means the buttons should be active. + tinyMCE.switchClass(editor_id + '_cite', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_acronym', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_abbr', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_del', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_ins', 'mceButtonNormal'); + } + + if (elm && elm.nodeName != 'BODY' && elm.className.indexOf('mceItem') == -1) + tinyMCE.switchClass(editor_id + '_attribs', 'mceButtonNormal'); + + switch (node.nodeName) { + case "CITE": + tinyMCE.switchClass(editor_id + '_cite', 'mceButtonSelected'); + return true; + + case "ACRONYM": + tinyMCE.switchClass(editor_id + '_acronym', 'mceButtonSelected'); + return true; + + case "abbr": // IE + case "HTML:ABBR": // FF + case "ABBR": + tinyMCE.switchClass(editor_id + '_abbr', 'mceButtonSelected'); + return true; + + case "DEL": + tinyMCE.switchClass(editor_id + '_del', 'mceButtonSelected'); + return true; + + case "INS": + tinyMCE.switchClass(editor_id + '_ins', 'mceButtonSelected'); + return true; + } + + return true; + }, + + _anySel : function(editor_id) { + var inst = tinyMCE.getInstanceById(editor_id), t = inst.selection.getSelectedText(), pe; + + pe = tinyMCE.getParentElement(inst.getFocusElement(), 'CITE,ACRONYM,ABBR,HTML:ABBR,DEL,INS'); + + return pe || inst.getFocusElement().nodeName == "IMG" || (t && t.length > 0); + } +}; + +tinyMCE.addPlugin("xhtmlxtras", TinyMCE_XHTMLXtrasPlugin); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/images/abbr.gif Binary file includes/clientside/tinymce/plugins/xhtmlxtras/images/abbr.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/images/acronym.gif Binary file includes/clientside/tinymce/plugins/xhtmlxtras/images/acronym.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/images/attribs.gif Binary file includes/clientside/tinymce/plugins/xhtmlxtras/images/attribs.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/images/cite.gif Binary file includes/clientside/tinymce/plugins/xhtmlxtras/images/cite.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/images/date_time.gif Binary file includes/clientside/tinymce/plugins/xhtmlxtras/images/date_time.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/images/del.gif Binary file includes/clientside/tinymce/plugins/xhtmlxtras/images/del.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/images/ins.gif Binary file includes/clientside/tinymce/plugins/xhtmlxtras/images/ins.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/images/remove_button_bg.gif Binary file includes/clientside/tinymce/plugins/xhtmlxtras/images/remove_button_bg.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/ins.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/ins.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,169 @@ + + + {$lang_xhtmlxtras_title_ins_element} + + + + + + + + + + +
+ + +
+
+
+ {$lang_xhtmlxtras_fieldset_general_tab} + + + + + + + + + +
: + + + + + +
{$lang_xhtmlxtras_insert_date}
+
:
+
+
+ {$lang_xhtmlxtras_fieldset_attrib_tab} + + + + + + + + + + + + + + + + + + + + + + + + + +
:
:
: + +
:
: + +
: + +
+
+
+
+
+ {$lang_xhtmlxtras_fieldset_events_tab} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
:
:
:
:
:
:
:
:
:
:
:
:
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+ + + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/jscripts/abbr.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/jscripts/abbr.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,29 @@ + /** + * $Id: editor_plugin_src.js 42 2006-08-08 14:32:24Z spocke $ + * + * @author Moxiecode - based on work by Andrew Tetlaw + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +function preinit() { + // Initialize + tinyMCE.setWindowArg('mce_windowresize', false); +} + +function init() { + tinyMCEPopup.resizeToInnerSize(); + SXE.initElementDialog('abbr'); + if (SXE.currentAction == "update") { + SXE.showRemoveButton(); + } +} + +function insertAbbr() { + SXE.insertElement(tinyMCE.isIE && !tinyMCE.isOpera ? 'html:ABBR' : 'abbr'); + tinyMCEPopup.close(); +} + +function removeAbbr() { + SXE.removeElement('abbr'); + tinyMCEPopup.close(); +} \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/jscripts/acronym.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/jscripts/acronym.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,29 @@ + /** + * $Id: editor_plugin_src.js 42 2006-08-08 14:32:24Z spocke $ + * + * @author Moxiecode - based on work by Andrew Tetlaw + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +function preinit() { + // Initialize + tinyMCE.setWindowArg('mce_windowresize', false); +} + +function init() { + tinyMCEPopup.resizeToInnerSize(); + SXE.initElementDialog('acronym'); + if (SXE.currentAction == "update") { + SXE.showRemoveButton(); + } +} + +function insertAcronym() { + SXE.insertElement('acronym'); + tinyMCEPopup.close(); +} + +function removeAcronym() { + SXE.removeElement('acronym'); + tinyMCEPopup.close(); +} \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/jscripts/attributes.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/jscripts/attributes.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,130 @@ + /** + * $Id: editor_plugin_src.js 42 2006-08-08 14:32:24Z spocke $ + * + * @author Moxiecode - based on work by Andrew Tetlaw + * @copyright Copyright © 2004-2006, Moxiecode Systems AB, All rights reserved. + */ + +function preinit() { + // Initialize + tinyMCE.setWindowArg('mce_windowresize', false); +} + +function init() { + tinyMCEPopup.resizeToInnerSize(); + var inst = tinyMCE.getInstanceById(tinyMCE.getWindowArg('editor_id')); + var elm = inst.getFocusElement(); + + var f = document.forms[0]; + + var onclick = tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onclick')); + + setFormValue('title', tinyMCE.getAttrib(elm, 'title')); + setFormValue('id', tinyMCE.getAttrib(elm, 'id')); + setFormValue('style', tinyMCE.serializeStyle(tinyMCE.parseStyle(tinyMCE.getAttrib(elm, "style")))); + setFormValue('dir', tinyMCE.getAttrib(elm, 'dir')); + setFormValue('lang', tinyMCE.getAttrib(elm, 'lang')); + setFormValue('tabindex', tinyMCE.getAttrib(elm, 'tabindex', typeof(elm.tabindex) != "undefined" ? elm.tabindex : "")); + setFormValue('accesskey', tinyMCE.getAttrib(elm, 'accesskey', typeof(elm.accesskey) != "undefined" ? elm.accesskey : "")); + setFormValue('onfocus', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onfocus'))); + setFormValue('onblur', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onblur'))); + setFormValue('onclick', onclick); + setFormValue('ondblclick', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'ondblclick'))); + setFormValue('onmousedown', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onmousedown'))); + setFormValue('onmouseup', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onmouseup'))); + setFormValue('onmouseover', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onmouseover'))); + setFormValue('onmousemove', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onmousemove'))); + setFormValue('onmouseout', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onmouseout'))); + setFormValue('onkeypress', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onkeypress'))); + setFormValue('onkeydown', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onkeydown'))); + setFormValue('onkeyup', tinyMCE.cleanupEventStr(tinyMCE.getAttrib(elm, 'onkeyup'))); + + className = tinyMCE.getVisualAidClass(tinyMCE.getAttrib(elm, 'class'), false); + + addClassesToList('classlist', 'advlink_styles'); + selectByValue(f, 'classlist', className, true); + + TinyMCE_EditableSelects.init(); +} + +function setFormValue(name, value) { + if(value && document.forms[0].elements[name]){ + document.forms[0].elements[name].value = value; + } +} + +function insertAction() { + var inst = tinyMCE.getInstanceById(tinyMCE.getWindowArg('editor_id')); + var elm = inst.getFocusElement(); + + tinyMCEPopup.execCommand("mceBeginUndoLevel"); + tinyMCEPopup.restoreSelection(); + + setAllAttribs(elm); + + tinyMCE.handleVisualAid(inst.getBody(), true, inst.visualAid, inst); + tinyMCE._setEventsEnabled(inst.getBody(), false); + tinyMCEPopup.execCommand("mceEndUndoLevel"); + tinyMCEPopup.close(); +} + +function setAttrib(elm, attrib, value) { + var formObj = document.forms[0]; + var valueElm = formObj.elements[attrib.toLowerCase()]; + + if (typeof(value) == "undefined" || value == null) { + value = ""; + + if (valueElm) + value = valueElm.value; + } + + if (value != "") { + tinyMCE.setAttrib(elm, attrib.toLowerCase(), value); + + if (attrib == "style") + attrib = "style.cssText"; + + if (attrib.substring(0, 2) == 'on') + value = 'return true;' + value; + + if (attrib == "class") + attrib = "className"; + + eval('elm.' + attrib + "=value;"); + } else + elm.removeAttribute(attrib); +} + +function setAllAttribs(elm) { + var f = document.forms[0]; + + setAttrib(elm, 'title'); + setAttrib(elm, 'id'); + setAttrib(elm, 'style'); + setAttrib(elm, 'class', getSelectValue(f, 'classlist')); + setAttrib(elm, 'dir'); + setAttrib(elm, 'lang'); + setAttrib(elm, 'tabindex'); + setAttrib(elm, 'accesskey'); + setAttrib(elm, 'onfocus'); + setAttrib(elm, 'onblur'); + setAttrib(elm, 'onclick'); + setAttrib(elm, 'ondblclick'); + setAttrib(elm, 'onmousedown'); + setAttrib(elm, 'onmouseup'); + setAttrib(elm, 'onmouseover'); + setAttrib(elm, 'onmousemove'); + setAttrib(elm, 'onmouseout'); + setAttrib(elm, 'onkeypress'); + setAttrib(elm, 'onkeydown'); + setAttrib(elm, 'onkeyup'); + + // Refresh in old MSIE + if (tinyMCE.isMSIE5) + elm.outerHTML = elm.outerHTML; +} + +function insertAttribute() { + tinyMCEPopup.close(); +} \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/jscripts/cite.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/jscripts/cite.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,29 @@ + /** + * $Id: editor_plugin_src.js 42 2006-08-08 14:32:24Z spocke $ + * + * @author Moxiecode - based on work by Andrew Tetlaw + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +function preinit() { + // Initialize + tinyMCE.setWindowArg('mce_windowresize', false); +} + +function init() { + tinyMCEPopup.resizeToInnerSize(); + SXE.initElementDialog('cite'); + if (SXE.currentAction == "update") { + SXE.showRemoveButton(); + } +} + +function insertCite() { + SXE.insertElement('cite'); + tinyMCEPopup.close(); +} + +function removeCite() { + SXE.removeElement('cite'); + tinyMCEPopup.close(); +} \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/jscripts/del.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/jscripts/del.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,54 @@ + /** + * $Id: editor_plugin_src.js 42 2006-08-08 14:32:24Z spocke $ + * + * @author Moxiecode - based on work by Andrew Tetlaw + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +function preinit() { + // Initialize + tinyMCE.setWindowArg('mce_windowresize', false); +} + +function init() { + tinyMCEPopup.resizeToInnerSize(); + SXE.initElementDialog('del'); + if (SXE.currentAction == "update") { + setFormValue('datetime', tinyMCE.getAttrib(SXE.updateElement, 'datetime')); + setFormValue('cite', tinyMCE.getAttrib(SXE.updateElement, 'cite')); + SXE.showRemoveButton(); + } +} + +function setElementAttribs(elm) { + setAllCommonAttribs(elm); + setAttrib(elm, 'datetime'); + setAttrib(elm, 'cite'); +} + +function insertDel() { + var elm = tinyMCE.getParentElement(SXE.focusElement, 'del'); + + tinyMCEPopup.execCommand('mceBeginUndoLevel'); + if (elm == null) { + var s = SXE.inst.selection.getSelectedHTML(); + if(s.length > 0) { + tinyMCEPopup.execCommand('mceInsertContent', false, '' + s + ''); + var elementArray = tinyMCE.getElementsByAttributeValue(SXE.inst.getBody(), 'del', 'id', '#sxe_temp_del#'); + for (var i=0; i 0) { + tagName = element_name; + + if (tinyMCE.isIE && !tinyMCE.isOpera && element_name.indexOf('html:') == 0) + element_name = element_name.substring(5).toLowerCase(); + + h = '<' + tagName + ' id="#sxe_temp_' + element_name + '#">' + s + ''; + + tinyMCEPopup.execCommand('mceInsertContent', false, h); + + var elementArray = tinyMCE.getElementsByAttributeValue(SXE.inst.getBody(), element_name, 'id', '#sxe_temp_' + element_name + '#'); + for (var i=0; i -1) ? true : false; +} + +SXE.removeClass = function(elm,cl) { + if(elm.className == null || elm.className == "" || !SXE.containsClass(elm,cl)) { + return true; + } + var classNames = elm.className.split(" "); + var newClassNames = ""; + for (var x = 0, cnl = classNames.length; x < cnl; x++) { + if (classNames[x] != cl) { + newClassNames += (classNames[x] + " "); + } + } + elm.className = newClassNames.substring(0,newClassNames.length-1); //removes extra space at the end +} + +SXE.addClass = function(elm,cl) { + if(!SXE.containsClass(elm,cl)) elm.className ? elm.className += " " + cl : elm.className = cl; + return true; +} \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/xhtmlxtras/jscripts/ins.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/xhtmlxtras/jscripts/ins.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,53 @@ + /** + * $Id: editor_plugin_src.js 42 2006-08-08 14:32:24Z spocke $ + * + * @author Moxiecode - based on work by Andrew Tetlaw + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +function preinit() { + // Initialize + tinyMCE.setWindowArg('mce_windowresize', false); +} + +function init() { + tinyMCEPopup.resizeToInnerSize(); + SXE.initElementDialog('ins'); + if (SXE.currentAction == "update") { + setFormValue('datetime', tinyMCE.getAttrib(SXE.updateElement, 'datetime')); + setFormValue('cite', tinyMCE.getAttrib(SXE.updateElement, 'cite')); + SXE.showRemoveButton(); + } +} + +function setElementAttribs(elm) { + setAllCommonAttribs(elm); + setAttrib(elm, 'datetime'); + setAttrib(elm, 'cite'); +} + +function insertIns() { + var elm = tinyMCE.getParentElement(SXE.focusElement, 'ins'); + tinyMCEPopup.execCommand('mceBeginUndoLevel'); + if (elm == null) { + var s = SXE.inst.selection.getSelectedHTML(); + if(s.length > 0) { + tinyMCEPopup.execCommand('mceInsertContent', false, '' + s + ''); + var elementArray = tinyMCE.getElementsByAttributeValue(SXE.inst.getBody(), 'ins', 'id', '#sxe_temp_ins#'); + for (var i=0; i'+''+''+''+''+''}return""},execCommand:function(editor_id,element,command,user_interface,value){switch(command){case"mceZoom":tinyMCE.getInstanceById(editor_id).contentDocument.body.style.zoom=value;tinyMCE.getInstanceById(editor_id).contentDocument.body.style.mozZoom=value;return true}return false}};tinyMCE.addPlugin("zoom",TinyMCE_ZoomPlugin); \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/zoom/editor_plugin_src.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/zoom/editor_plugin_src.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,56 @@ +/** + * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $ + * + * @author Moxiecode + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +var TinyMCE_ZoomPlugin = { + getInfo : function() { + return { + longname : 'Zoom', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/zoom', + version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion + }; + }, + + /** + * Returns the HTML contents of the zoom control. + */ + getControlHTML : function(control_name) { + if (!tinyMCE.isMSIE || tinyMCE.isMSIE5_0 || tinyMCE.isOpera) + return ""; + + switch (control_name) { + case "zoom": + return ''; + } + + return ""; + }, + + /** + * Executes the mceZoom command. + */ + execCommand : function(editor_id, element, command, user_interface, value) { + // Handle commands + switch (command) { + case "mceZoom": + tinyMCE.getInstanceById(editor_id).contentDocument.body.style.zoom = value; + tinyMCE.getInstanceById(editor_id).contentDocument.body.style.mozZoom = value; + return true; + } + + // Pass to next handler in chain + return false; + } +}; + +tinyMCE.addPlugin("zoom", TinyMCE_ZoomPlugin); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/plugins/zoom/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/plugins/zoom/readme.txt Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +Check the TinyMCE documentation for details on this plugin. diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/switcher.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/switcher.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,51 @@ +function readCookie(name) {var nameEQ = name + "=";var ca = document.cookie.split(';');for(var i=0;i < ca.length;i++){var c = ca[i];while (c.charAt(0)==' ') c = c.substring(1,c.length);if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);}return null;} +function createCookie(name,value,days){if (days){var date = new Date();date.setTime(date.getTime()+(days*24*60*60*1000));var expires = "; expires="+date.toGMTString();}else var expires = "";document.cookie = name+"="+value+expires+"; path=/";} +function eraseCookie(name) {createCookie(name,"",-1);} + +function initSwitcher() +{ + if(readCookie('tmce_demo_mode') == 'tinymce') + { + switchToMCE(); + } +} + +function switchToMCE() +{ + elem = document.getElementById('tMceEditor'); + tinyMCE.addMCEControl(elem, 'content', document); + createCookie('tmce_demo_mode', 'tinymce', 365); +} + +function switchToText() +{ + elem = document.getElementById('tMceEditor'); + tinyMCE.removeMCEControl('content'); + createCookie('tmce_demo_mode', 'text', 365); +} + +function switchEditor() +{ + if(readCookie('tmce_demo_mode') == 'tinymce') + { + switchToText(); + } + else + { + switchToMCE(); + } +} + +window.onload = initSwitcher; + +tinyMCE.init({ + mode : "exact", + elements : '', + theme_advanced_resize_horizontal : false, + theme_advanced_resizing : true, + theme_advanced_toolbar_location : "top", + theme_advanced_toolbar_align : "left", + theme_advanced_buttons1_add : "fontselect,fontsizeselect", + theme_advanced_statusbar_location : 'bottom' + }); + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/about.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/about.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,52 @@ + + + {$lang_about_title} + + + + + + + + +
+
+

{$lang_about_title}

+

Version: {$tinymce_version} ({$tinymce_releasedate})

+

TinyMCE is a platform independent web based Javascript HTML WYSIWYG editor control released as Open Source under LGPL + by Moxiecode Systems AB. It has the ability to convert HTML TEXTAREA fields or other HTML elements to editor instances.

+

Copyright © 2003-2007, Moxiecode Systems AB, All rights reserved.

+

For more information about this software visit the TinyMCE website.

+ +
+
+ +
+
+

{$lang_loaded_plugins}

+ +
+
+ +

 

+
+
+ +
+
+
+
+ +
+
+ +
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/anchor.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/anchor.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,33 @@ + + + {$lang_insert_anchor_title} + + + + + +
+ + + + + + + + + +
{$lang_insert_anchor_title}
{$lang_insert_anchor_name}:
+ +
+
+ +
+ +
+ +
+
+ +
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/charmap.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/charmap.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,53 @@ + + + {$lang_theme_charmap_title} + + + + + + + + + + + + + + + + + +
{$lang_theme_charmap_title}
+ + + + + + + + + +
 
 
+
+ + + + + + + + + + + + + + + + +
HTML-Code
 
 
NUM-Code
 
+
+ + + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/color_picker.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/color_picker.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,74 @@ + + + {$lang_theme_colorpicker_title} + + + + + + + + + +
+
+
+ {$lang_color_picker} +
+ + +
+ +
+ +
+
+
+
+ +
+
+ {$lang_web_colors} +
+ +
+ +
+
+
+ +
+
+ {$lang_named_colors} +
+ +
+ +
+ +
+ {$lang_color_name} +
+
+
+
+ +
+
+ +
+ +
+ +
+ +
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/css/colorpicker.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/css/colorpicker.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,53 @@ +/* Colorpicker dialog specific CSS */ + +#preview { + float: right; + width: 50px; + height: 14px; + line-height: 1px; + border: 1px solid black; + margin-left: 5px; +} + +#colorpicker { + float: left; + cursor: crosshair; +} + +#light { + border: 1px solid gray; + margin-left: 5px; + float: left; + width: 15px; + cursor: crosshair; +} + +#light div { + overflow: hidden; +} + +#previewblock { + float: right; + padding-left: 10px; + height: 20px; +} + +.panel_wrapper div.current { + height: 175px; +} + +#namedcolors { + width: 150px; +} + +#namedcolors a { + display: block; + float: left; + width: 10px; height: 10px; + margin: 1px 1px 0 0; + overflow: hidden; +} + +#colornamecontainer { + margin-top: 5px; +} \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/css/editor_content.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/css/editor_content.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,58 @@ +/* This file contains the CSS data for the editable area(iframe) of TinyMCE */ +/* You can extend this CSS by adding your own CSS file with the the content_css option */ + +body, td, pre { + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 10px; +} + +body { + background-color: #FFFFFF; +} + +.mceVisualAid { + border: 1px dashed #BBBBBB !important; +} + +div.mceVisualAid { + background-image:url('../images/spacer.gif'); + visibility: visible !important; +} + +.mceItemAnchor { + width: 12px; + line-height: 6px; + overflow: hidden; + padding-left: 12px; + background-image: url('../images/anchor_symbol.gif'); + background-position: bottom; + background-repeat: no-repeat; +} + +/* Important is needed in Gecko browsers inorder to style links */ +/* +a { + color: green !important; +} +*/ + +/* Style selection range colors in Gecko browsers */ +/* +::-moz-selection { + background-color: red; + color: green; +} +*/ + +/* MSIE specific */ + +* html body { + scrollbar-3dlight-color: #F0F0EE; + scrollbar-arrow-color: #676662; + scrollbar-base-color: #F0F0EE; + scrollbar-darkshadow-color: #DDDDDD; + scrollbar-face-color: #E0E0DD; + scrollbar-highlight-color: #F0F0EE; + scrollbar-shadow-color: #F0F0EE; + scrollbar-track-color: #F5F5F5; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/css/editor_popup.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/css/editor_popup.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,331 @@ +/* This file contains the CSS data for all popups in TinyMCE */ + +body { + background-color: #F0F0EE; + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 11px; + scrollbar-3dlight-color: #F0F0EE; + scrollbar-arrow-color: #676662; + scrollbar-base-color: #F0F0EE; + scrollbar-darkshadow-color: #DDDDDD; + scrollbar-face-color: #E0E0DD; + scrollbar-highlight-color: #F0F0EE; + scrollbar-shadow-color: #F0F0EE; + scrollbar-track-color: #F5F5F5; + margin: 8px; +} + +td { + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 11px; +} + +input { + background: #FFFFFF; + border: 1px solid #cccccc; +} + +td, input, select, textarea { + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 10px; +} + +input, select, textarea { + border: 1px solid #808080; +} + +.input_noborder { + border: 0; +} + +#insert, .updateButton { + font-weight: bold; + width: 90px; + height: 21px; + border: 0; + background-image: url('../images/insert_button_bg.gif'); + cursor: pointer; +} + +#cancel { + font-weight: bold; + width: 90px; + height: 21px; + border: 0; + background-image: url('../images/cancel_button_bg.gif'); + cursor: pointer; +} + +/* Mozilla only style */ +html>body #insert, html>body #cancel { + padding-bottom: 2px; +} + +.title { + font-size: 12px; + font-weight: bold; + color: #2B6FB6; +} + +table.charmap { + border-style: solid; + border-width: 1px; + border-color: #AAAAAA; +} + +td.charmap, td.charmapOver { + color: #000000; + border-color: #AAAAAA; + border-style: solid; + border-width: 1px; + text-align: center; + font-size: 12px; +} + +td.charmapOver { + background-color: #CCCCCC; + cursor: default; +} + +a.charmap { + color: #000000; + text-decoration: none +} + +.wordWrapCode { + vertical-align: middle; + border: 1px none #000000; + background-color: transparent; +} + +input.radio { + border: 1px none #000000; + background-color: transparent; + vertical-align: middle; +} + +input.checkbox { + border: 1px none #000000; + background-color: transparent; + vertical-align: middle; +} + +.mceButtonNormal, .mceButtonOver, .mceButtonDown, .mceSeparator, .mceButtonDisabled, .mceButtonSelected { + margin-left: 1px; +} + +.mceButtonNormal { + border-top: 1px solid; + border-left: 1px solid; + border-bottom: 1px solid; + border-right: 1px solid; + border-color: #F0F0EE; + cursor: default; +} + +.mceButtonOver { + border: 1px solid #0A246A; + cursor: default; + background-color: #B6BDD2; +} + +.mceButtonDown { + cursor: default; + border: 1px solid #0A246A; + background-color: #8592B5; +} + +.mceButtonDisabled { + filter:progid:DXImageTransform.Microsoft.Alpha(opacity=30); + -moz-opacity:0.3; + opacity: 0.3; + border-top: 1px solid; + border-left: 1px solid; + border-bottom: 1px solid; + border-right: 1px solid; + border-color: #F0F0EE; + cursor: default; +} + +.mceActionPanel { + margin-top: 5px; +} + +/* Tabs classes */ + +.tabs { + float: left; + width: 100%; + line-height: normal; + background-image: url("../images/xp/tabs_bg.gif"); +} + +.tabs ul { + margin: 0; + padding: 0 0 0; + list-style: none; +} + +.tabs li { + float: left; + background: url("../images/xp/tab_bg.gif") no-repeat left top; + margin: 0; + margin-left: 0; + margin-right: 2px; + padding: 0 0 0 10px; + line-height: 18px; +} + +.tabs li.current { + background: url("../images/xp/tab_sel_bg.gif") no-repeat left top; + margin-right: 2px; +} + +.tabs span { + float: left; + display: block; + background: url("../images/xp/tab_end.gif") no-repeat right top; + padding: 0px 10px 0 0; +} + +.tabs .current span { + background: url("../images/xp/tab_sel_end.gif") no-repeat right top; +} + +.tabs a { + text-decoration: none; + font-family: Verdana, Arial; + font-size: 10px; +} + +.tabs a:link, .tabs a:visited, .tabs a:hover { + color: black; +} + +.tabs a:hover { +} + +.tabs .current { +} + +.tabs .current a, .tabs .current a:link, .tabs .current a:visited { +} + +.panel_wrapper div.panel { + display: none; +} + +.panel_wrapper div.current { + display: block; + width: 100%; + height: 300px; + overflow: visible; /* Should be auto but that breaks Safari */ +} + +.panel_wrapper { + border: 1px solid #919B9C; + border-top: 0px; + padding: 10px; + padding-top: 5px; + clear: both; + background-color: white; +} + +fieldset { + border: 1px solid #919B9C; + font-family: Verdana, Arial; + font-size: 10px; + padding: 0; + margin: 0; + padding: 4px; +} + +legend { + color: #2B6FB6; + font-weight: bold; +} + +.properties { + width: 100%; +} + +.properties .column1 { +} + +.properties .column2 { + text-align: left; +} + +a:link, a:visited { + color: black; +} + +a:hover { + color: #2B6FB6; +} + +#plugintable thead { + font-weight: bold; + background-color: #DDDDDD; +} + +#plugintable, #about #plugintable td { + border: 1px solid #919B9C; +} + +#plugintable { + width: 99%; + margin-top: 10px; +} + +#pluginscontainer { + height: 290px; + overflow: auto; +} + +/* MSIE Specific styles */ + +* html .panel_wrapper { + width: 100%; +} + +.column { + float: left; +} + +h1, h2, h3, h4 { + color: #2B6FB6; + margin: 0; + padding: 0; + padding-top: 5px; +} + +h3 { + font-size: 14px; +} + +#link .panel_wrapper, #link div.current { + height: 125px; +} + +#image .panel_wrapper, #image div.current { + height: 190px; +} + +label.msg { display: none; } +label.invalid { color: #EE0000; display: inline; } +input.invalid { border: 1px solid #EE0000; } + +/* Disables the advanced tab in the table plugin. */ +/* +#table #advanced_tab { + display: none; +} +*/ + +/* Disables the border input field and label in the table plugin. */ +/* +#table #border, #table #borderlabel { + display: none; +} +*/ diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/css/editor_ui.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/css/editor_ui.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,97 @@ +/* This file contains the CSS data for the editor UI of TinyMCE instances */ + +.mceToolbarTop a, .mceToolbarTop a:visited, .mceToolbarTop a:hover, .mceToolbarBottom a, .mceToolbarBottom a:visited, .mceToolbarBottom a:hover {border: 0; margin: 0; padding: 0; background: transparent;} +.mceSeparatorLine {border: 0; padding: 0; margin-left: 4px; margin-right: 2px;} +.mceSelectList {font-family: 'MS Sans Serif', sans-serif, Verdana, Arial; font-size: 7pt !important; font-weight: normal; margin-top: 3px; padding: 0; display: inline; vertical-align: top; background-color: #F0F0EE;} +.mceLabel, .mceLabelDisabled {font-family: 'MS Sans Serif', sans-serif, Verdana, Arial; font-size: 9pt;} +.mceLabel {color: #000000;} +.mceLabelDisabled {cursor: text; color: #999999;} +.mceEditor {background: #F0F0EE; border: 1px solid #cccccc; padding: 0; margin: 0;} +.mceEditorArea { font-family: 'MS Sans Serif', sans-serif, Verdana, Arial; background: #FFFFFF; padding: 0; margin: 0; } +.mceToolbarTop, .mceToolbarBottom {background: #F0F0EE; line-height: 1px; font-size: 1px;} +.mceToolbarTop {border-bottom: 1px solid #cccccc; padding-bottom: 1px;} +.mceToolbarBottom {border-top: 1px solid #cccccc;} +.mceToolbarContainer {display: block; position: relative; left: 0; top: 0; width: 100%;} +.mceStatusbarTop, .mceStatusbarBottom, .mceStatusbar {height: 20px;} +.mceStatusbarTop .mceStatusbarPathText, .mceStatusbarBottom .mceStatusbarPathText, .mceStatusbar .mceStatusbarPathText {font-family: 'MS Sans Serif', sans-serif, Verdana, Arial; font-size: 9pt; padding: 2px; line-height: 16px; overflow: visible;} +.mceStatusbarTop {border-bottom: 1px solid #cccccc;} +.mceStatusbarBottom {border-top: 1px solid #cccccc;} +.mceStatusbar {border-bottom: 1px solid #cccccc;} +.mcePathItem, .mcePathItem:link, .mcePathItem:visited, .mcePathItem:hover {text-decoration: none; font-family: 'MS Sans Serif', sans-serif, Verdana, Arial; font-size: 9pt; color: #000000;} +.mcePathItem:hover {text-decoration: underline;} +.mceStatusbarPathText {float: left;} +.mceStatusbarResize {float: right; background-image: url('../images/statusbar_resize.gif'); background-repeat: no-repeat; width: 11px; height: 20px; cursor: se-resize;} +.mceResizeBox {width: 10px; height: 10px; display: none; border: 1px dotted gray; margin: 0; padding: 0;} +.mceEditorIframe {border: 0;} + +/* Button CSS rules */ + +a.mceButtonDisabled img, a.mceButtonNormal img, a.mceButtonSelected img {width: 20px; height: 20px; cursor: default; margin-top: 1px; margin-left: 1px;} +a.mceButtonDisabled img {border: 0 !important;} +a.mceButtonNormal img, a.mceButtonSelected img {border: 1px solid #F0F0EE !important;} +a.mceButtonSelected img {border: 1px solid #6779AA !important; background-color: #D4D5D8;} +a.mceButtonNormal img:hover, a.mceButtonSelected img:hover {border: 1px solid #0A246A !important; cursor: default; background-color: #B6BDD2;} +a.mceButtonDisabled img {-moz-opacity:0.3; opacity: 0.3; border: 1px solid #F0F0EE !important; cursor: default;} +a.mceTiledButton img {background-image: url('../images/buttons.gif'); background-repeat: no-repeat;} + +/* Menu button CSS rules */ + +span.mceMenuButton img, span.mceMenuButtonSelected img {border: 1px solid #F0F0EE; margin-left: 1px;} +span.mceMenuButtonSelected img {border: 1px solid #6779AA; background-color: #B6BDD2;} +span.mceMenuButtonSelected img.mceMenuButton {border: 1px solid #F0F0EE; background-color: transparent;} +span.mceMenuButton img.mceMenuButton, span.mceMenuButtonSelected img.mceMenuButton {border-left: 0; margin-left: 0;} +span.mceMenuButton:hover img, span.mceMenuButtonSelected:hover img {border: 1px solid #0A246A; background-color: #B6BDD2;} +span.mceMenuButton:hover img.mceMenuButton, span.mceMenuButtonSelected:hover img.mceMenuButton {border-left: 0;} +span.mceMenuButtonFocus img {border: 1px solid gray; border-right: 0; margin-left: 1px; background-color: #F5F4F2;} +span.mceMenuButtonFocus img.mceMenuButton {border: 1px solid gray; border-left: 1px solid #F5F4F2; margin-left: 0;} +span.mceMenuHover img {border: 1px solid #0A246A; background-color: #B6BDD2;} +span.mceMenuButtonSelected.mceMenuHover img.mceMenuButton {border: 1px solid #0A246A; background-color: #B6BDD2; border-left: 0;} + +/* Menu */ + +.mceMenu {position: absolute; left: 0; top: 0; display: none; z-index: 1000; background-color: white; border: 1px solid gray; font-weight: normal;} +.mceMenu a, .mceMenuTitle, .mceMenuDisabled {display: block; width: 100%; text-decoration: none; background-color: white; font-family: Tahoma, Verdana, Arial, Helvetica; font-size: 11px; line-height: 20px; color: black;} +.mceMenu a:hover {background-color: #B6BDD2; color: black; text-decoration: none !important;} +.mceMenu span {padding-left: 10px; padding-right: 10px; display: block; line-height: 20px;} +.mceMenuSeparator {border-bottom: 1px solid gray; background-color: gray; height: 1px;} +.mceMenuTitle span {padding-left: 5px;} +.mceMenuTitle {background-color: #DDDDDD; font-weight: bold;} +.mceMenuDisabled {color: gray;} +span.mceMenuSelectedItem {background-image: url('../images/menu_check.gif'); background-repeat: no-repeat; background-position: 5px 8px; padding-left: 20px;} +span.mceMenuCheckItem {padding-left: 20px;} +span.mceMenuLine {display: block; position: absolute; left: 0; top: -1px; background-color: #F5F4F2; width: 30px; height: 1px; overflow: hidden; padding-left: 0; padding-right: 0;} +.mceColors table, .mceColors td {margin: 0; padding: 2px;} +a.mceMoreColors {width: auto; padding: 0; margin: 0 3px 3px 3px; text-align: center; border: 1px solid white; text-decoration: none !important;} +.mceColorPreview {position: absolute; overflow:hidden; left: 0; top: 0; margin-left: 3px; margin-top: 15px; width: 16px; height: 4px; background-color: red;} +a.mceMoreColors:hover {border: 1px solid #0A246A;} +.mceColors td a {width: 9px; height: 9px; overflow: hidden; border: 1px solid #808080;} + +/* MSIE 6 specific rules */ + +* html a.mceButtonNormal img, * html a.mceButtonSelected img, * html a.mceButtonDisabled img {border: 0 !important; margin-top: 2px; margin-bottom: 1px;} +* html a.mceButtonDisabled img {filter:progid:DXImageTransform.Microsoft.Alpha(opacity=30); border: 0 !important;} +* html a.mceButtonDisabled {border: 1px solid #F0F0EE !important;} +* html a.mceButtonNormal, * html a.mceButtonSelected {border: 1px solid #F0F0EE !important; cursor: default;} +* html a.mceButtonSelected {border: 1px solid #6779AA !important; background-color: #D4D5D8;} +* html a.mceButtonNormal:hover, * html a.mceButtonSelected:hover {border: 1px solid #0A246A !important; background-color: #B6BDD2; cursor: default;} +* html .mceSelectList {margin-top: 2px;} +* html span.mceMenuButton, * html span.mceMenuButtonFocus {position: relative; left: 0; top: 0;} +* html span.mceMenuButton img, * html span.mceMenuButtonSelected img, * html span.mceMenuButtonFocus img {position: relative; top: 1px;} +* html a.mceMoreColors {width: auto;} +* html .mceColors td a {width: 10px; height: 10px;} +* html .mceColorPreview {margin-left: 2px; margin-top: 14px;} + +/* MSIE 7 specific rules */ + +*:first-child+html a.mceButtonNormal img, *:first-child+html a.mceButtonSelected img, *:first-child+html a.mceButtonDisabled img {border: 0 !important; margin-top: 2px; margin-bottom: 1px;} +*:first-child+html a.mceButtonDisabled img {filter:progid:DXImageTransform.Microsoft.Alpha(opacity=30); border: 0 !important;} +*:first-child+html a.mceButtonDisabled {border: 1px solid #F0F0EE !important;} +*:first-child+html a.mceButtonNormal, *:first-child+html a.mceButtonSelected {border: 1px solid #F0F0EE !important; cursor: default;} +*:first-child+html a.mceButtonSelected {border: 1px solid #6779AA !important; background-color: #D4D5D8;} +*:first-child+html a.mceButtonNormal:hover, *:first-child+html a.mceButtonSelected:hover {border: 1px solid #0A246A !important; background-color: #B6BDD2; cursor: default;} +*:first-child+html .mceSelectList {margin-top: 2px;} +*:first-child+html span.mceMenuButton, *:first-child+html span.mceMenuButtonFocus {position: relative; left: 0; top: 0;} +*:first-child+html span.mceMenuButton img, *:first-child+html span.mceMenuButtonSelected img, *:first-child+html span.mceMenuButtonFocus img {position: relative; top: 1px;} +*:first-child+html a.mceMoreColors {width: 137px;} +*:first-child+html .mceColors td a {width: 10px; height: 10px;} +*:first-child+html .mceColorPreview {margin: 0; padding-left: 4px; margin-top: 14px; width: 14px;} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/docs/en/about.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/docs/en/about.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,31 @@ + + +About TinyMCE + + + + + + + + + + + +
+
+TinyMCE is a small WYSIWYG editor control for web browsers such as MSIE or Mozilla +that enables you to edit HTML contents in a more user friendly way. It has common +features that are found in most word processors and should not be difficult to +use.
+
+
+ + + + + + +
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/docs/en/common_buttons.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/docs/en/common_buttons.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,162 @@ + + +Common buttons + + + + + + + + + + + +
+
+Below is a short description about each button. +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bold text iconBold text style (Ctrl+B).
Italic text iconItalic text style (Ctrl+I).
Underline text icon.Underline text style (Ctrl+U).
Strikethrough text icon.Strikethrough text style.
Align left icon.Align left.
Align center icon.Align center.
Align right icon.Align right.
Align full icon.Align full.
Unordered list/bullet list icon.Unordered list/bullet list.
Ordered list/numbered list icon.Ordered list/numbered list
Outdent/decrease indentation icon.Outdent/decrease indentation.
Indent/increase indentation icon.Indent/increase indentation.
Undo the last operation.Undo the last operation (Ctrl+Z).
Redo the last operation icon.Redo the last operation (Ctrl+Y).
Insert a new link icon.Insert a new link, read more about this function in the Insert + link section.
Unlinks the current selection icon.Unlinks the current selection/removes all selected links.
Insert a new anchor icon.Insert a new anchor, read more about this function in the Insert anchor section.
Insert a new image icon.Insert a new image, read more about this function in the Insert + image section.
Cleanup code icon.Cleanup code/Removes unwanted formating. This function is useful when + you copy contents from for example a office product.
Show help icon.Shows this help window.
Source code editor icon.Opens HTML source code editor.
Insert table icon.Inserts a new table at the current location.
Adds a row above icon.Adds a row above the current one.
Adds a row under icon.Adds a row under the current one.
Remove row icon.Removes the row.
Add column before icon.Adds a column before the current one.
Add column after icon.Adds a column after the current one.
Remove column icon.Removes the current column.
Insert horizontal ruler icon.Inserts a new horizontal ruler
Remove formatting icon.Removes formatting from the selection.
Subscript icon.Makes the selection to be subscript.
Superscript icon.Makes the selection to be superscripted.
+
+
+ + + + + + + + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/docs/en/create_accessible_content.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/docs/en/create_accessible_content.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,45 @@ + + +Insert table button + + + + + + + + + + + +
+

TinyMCE can create HTML content that will be accessible to all users, including those with disabilities using assistive technologies, as well as those using text-based browsers, or those browsing the Web with images turned off.

+ +

Things you can do to make your content accessible:

+
    +
  1. Include an Image Description: Blind users, or others who are unable to view images, will rely on the Image Description (or Alt text) to take the place of the image. If an image contains no meaning, such as a decoration or a spacer image, leave the Image Description empty. TinyMCE will then insert an empty Alt text attribute that will force assistive technologies to ignore the image.

  2. + +
  3. Add Scope to data table header cells: In the table cell editor dialog window, choose a Scope when creating Header cells so the column or row label in that cell becomes explicitely associated with its data cells. Table cell headers will then be announced with each data cell, making it easier for blind users using a screen reader to understand what the content of each cell represents.

  4. + +
  5. Structure content with properly nested headings: In the format selection menu choose Heading 1 to Heading 6 to represent headings in your content, rather than using other font formating options. Blind users using a screen reader can then extract the headings from the page to generate a summary of the content it contains, and use those headings to navigate quickly to subsections within the page.

  6. + +
  7. Include alternate content: Create an alternate page for non-HTML content such as Flash, Java applets, or embedded movies. This might be a static image, with a description of the image, and a description of the content that would have appeared in its place. An alternate HTML page could also be created, and a link to it included next to the non-HTML object. This will ensure that the content will be accessible to users of assistive technologies that can not view or play the content, and ensure the content will be available to those who do not have the appropriate plugin or helper application installed.

  8. + +
  9. Check accessbility: When the AChecker plugin is installed with TinyMCE, click on the Check Accessibility button to generate a report of potential accessibility problems.

  10. + +
+ +

See the AChecker Web Site for further details about creating content that will be accessible to all users.
+

+ +
+ + + + + + + +
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/docs/en/images/insert_anchor_window.gif Binary file includes/clientside/tinymce/themes/advanced/docs/en/images/insert_anchor_window.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/docs/en/images/insert_image_window.gif Binary file includes/clientside/tinymce/themes/advanced/docs/en/images/insert_image_window.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/docs/en/images/insert_link_window.gif Binary file includes/clientside/tinymce/themes/advanced/docs/en/images/insert_link_window.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/docs/en/images/insert_table_window.gif Binary file includes/clientside/tinymce/themes/advanced/docs/en/images/insert_table_window.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/docs/en/index.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/docs/en/index.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,27 @@ + + +Help Index + + + + + + + + + +
+
+Click the links below to go to the different help sections. + +
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/docs/en/insert_anchor_button.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/docs/en/insert_anchor_button.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,32 @@ + + +Insert anchor button + + + + + + + + + + + +
+
+This button opens a new window with the insert/edit anchor function.
+
+Anchor dialog/window
+
+There are one field in this window, this is where you enter the name of you anchor point. Remember the anchor name needs to be unique.
+
+
+ + + + + + +
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/docs/en/insert_image_button.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/docs/en/insert_image_button.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,65 @@ + + +Insert image button + + + + + + + + + + + +
+
+The insert image button opens the window shown below.
+
+Insert image dialog/window
+
+You simply enter a URL to the image you want to link to and enter a image description, +this is then displayed as an alternative text descripton of the image on the page.
+
+Field descriptions:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Image URL URL/path to the image.
Image description Alternative description of image contents.
DimentionsImage width/height.
AlignmentImage alignment, useful when wrapping text around images.
BorderBorder thickness.
VSpaceVertical space, useful when wrapping text around images.
HSpaceHorizontal space, useful when wrapping text around images.
+
+
+ + + + + + +
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/docs/en/insert_link_button.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/docs/en/insert_link_button.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,33 @@ + + +Insert link button + + + + + + + + + + + +
+
+This button opens a new window with the insert/edit link function.
+
+Insert link dialog/window
+
+There are two fields in this window the first one "Link URL" is the +URL of the link. The target enables you to select how the link is to be opened.
+
+
+ + + + + + +
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/docs/en/insert_table_button.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/docs/en/insert_table_button.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,71 @@ + + +Insert table button + + + + + + + + + + + +
+
+The insert table button opens the window shown below. This action enables you to create tables.
+
+Image of table window
+
+Field descriptions:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ColumnsNumber of columns in the table.
RowsNumber of rows in the new table.
CellpaddingCellpadding of the table .
CellspacingCellspacing of the table .
AlignmentTable alignment .
BorderBorder thinkness of table.
WidthWidth in pixels of table .
HeightHeight in pixels of table.
ClassStyle or CSS class of table.
+
+
+
+ + + + + + +
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/docs/en/style.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/docs/en/style.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,28 @@ +body { background-color: #FFFFFF; } +body, td, .content { font-family: Verdana, Arial, helvetica, sans-serif; font-size: 12px; } +.title { font-family: Verdana, Arial, helvetica, sans-serif; font-size: 16px; font-weight: bold; } +.subtitle { font-size: 12px; font-weight: bold; } + +.toc_ul, .toc_li { margin-left: 8px; line-height: 16px; } +.step_ol, .step_li { margin-left: 11px; line-height: 16px; } +img { border: #000000 solid 1px; } + +a:visited { color: #666666; text-decoration: underline; } +a:active { color: #666666; text-decoration: underline; } +a:hover { color: #666666; text-decoration: underline; } +a { color: #666666; text-decoration: underline; } + +.pageheader { border: #E0E0E0 solid 1px; } +.pagefooter { border: #E0E0E0 solid 1px; } +.sample { background-color: #FFFFFF; border: #000000 solid 1px; } +.samplecontent { font-size: 10px; } + +.code { background-color: #FFFFFF; border: #000000 solid 1px; } +.codecontent { font-size: 10px; } +.codecontent a:visited { color: #666666; text-decoration: none; font-weight: bold } +.codecontent a:active { color: #666666; text-decoration: none; font-weight: bold } +.codecontent a:hover { color: #666666; text-decoration: none; font-weight: bold } +.codecontent a { color: #666666; text-decoration: none; font-weight: bold } + +hr { height: 1px; } + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/editor_template.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/editor_template.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +tinyMCE.importThemeLanguagePack('advanced');var TinyMCE_AdvancedTheme={_defColors:"000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF",_autoImportCSSClasses:true,_resizer:{},_buttons:[['bold','{$lang_bold_img}','lang_bold_desc','Bold'],['italic','{$lang_italic_img}','lang_italic_desc','Italic'],['underline','{$lang_underline_img}','lang_underline_desc','Underline'],['strikethrough','strikethrough.gif','lang_striketrough_desc','Strikethrough'],['justifyleft','justifyleft.gif','lang_justifyleft_desc','JustifyLeft'],['justifycenter','justifycenter.gif','lang_justifycenter_desc','JustifyCenter'],['justifyright','justifyright.gif','lang_justifyright_desc','JustifyRight'],['justifyfull','justifyfull.gif','lang_justifyfull_desc','JustifyFull'],['bullist','bullist.gif','lang_bullist_desc','InsertUnorderedList'],['numlist','numlist.gif','lang_numlist_desc','InsertOrderedList'],['outdent','outdent.gif','lang_outdent_desc','Outdent'],['indent','indent.gif','lang_indent_desc','Indent'],['cut','cut.gif','lang_cut_desc','Cut'],['copy','copy.gif','lang_copy_desc','Copy'],['paste','paste.gif','lang_paste_desc','Paste'],['undo','undo.gif','lang_undo_desc','Undo'],['redo','redo.gif','lang_redo_desc','Redo'],['link','link.gif','lang_link_desc','mceLink',true],['unlink','unlink.gif','lang_unlink_desc','unlink'],['image','image.gif','lang_image_desc','mceImage',true],['cleanup','cleanup.gif','lang_cleanup_desc','mceCleanup'],['help','help.gif','lang_help_desc','mceHelp'],['code','code.gif','lang_theme_code_desc','mceCodeEditor'],['hr','hr.gif','lang_theme_hr_desc','inserthorizontalrule'],['removeformat','removeformat.gif','lang_theme_removeformat_desc','removeformat'],['sub','sub.gif','lang_theme_sub_desc','subscript'],['sup','sup.gif','lang_theme_sup_desc','superscript'],['forecolor','forecolor.gif','lang_theme_forecolor_desc','forecolor',true],['forecolorpicker','forecolor.gif','lang_theme_forecolor_desc','forecolorpicker',true],['backcolor','backcolor.gif','lang_theme_backcolor_desc','HiliteColor',true],['backcolorpicker','backcolor.gif','lang_theme_backcolor_desc','backcolorpicker',true],['charmap','charmap.gif','lang_theme_charmap_desc','mceCharMap'],['visualaid','visualaid.gif','lang_theme_visualaid_desc','mceToggleVisualAid'],['anchor','anchor.gif','lang_theme_anchor_desc','mceInsertAnchor'],['newdocument','newdocument.gif','lang_newdocument_desc','mceNewDocument']],_buttonMap:'anchor,backcolor,bold,bullist,charmap,cleanup,code,copy,cut,forecolor,help,hr,image,indent,italic,justifycenter,justifyfull,justifyleft,justifyright,link,newdocument,numlist,outdent,paste,redo,removeformat,strikethrough,sub,sup,underline,undo,unlink,visualaid,advhr,ltr,rtl,emotions,flash,fullpage,fullscreen,iespell,insertdate,inserttime,pastetext,pasteword,selectall,preview,print,save,replace,search,table,cell_props,delete_col,delete_row,col_after,col_before,row_after,row_before,merge_cells,row_props,split_cells,delete_table',getControlHTML:function(button_name){var i,x,but;for(i=0;i4?but[4]:false),(but.length>5?but[5]:null));if(but[0]==button_name)return tinyMCE.getButtonHTML(but[0],but[2],'{$themeurl}/images/'+but[1],but[3],(but.length>4?but[4]:false),(but.length>5?but[5]:null))}switch(button_name){case"formatselect":var html='';return html;case"styleselect":return'';case"fontselect":var fontHTML='';return fontHTML;case"fontsizeselect":return'';case"|":case"separator":return'';case"spacer":return'';case"rowseparator":return'
'}return""},execCommand:function(editor_id,element,command,user_interface,value){switch(command){case'mceHelp':tinyMCE.openWindow({file:'about.htm',width:480,height:380},{tinymce_version:tinyMCE.majorVersion+"."+tinyMCE.minorVersion,tinymce_releasedate:tinyMCE.releaseDate,inline:"yes"});return true;case"mceLink":var inst=tinyMCE.getInstanceById(editor_id);var doc=inst.getDoc();var selectedText="";if(tinyMCE.isMSIE){var rng=doc.selection.createRange();selectedText=rng.text}else selectedText=inst.getSel().toString();if(!tinyMCE.linkElement){if((tinyMCE.selectedElement.nodeName.toLowerCase()!="img")&&(selectedText.length<=0))return true}var href="",target="",title="",onclick="",action="insert",style_class="";if(tinyMCE.selectedElement.nodeName.toLowerCase()=="a")tinyMCE.linkElement=tinyMCE.selectedElement;if(tinyMCE.linkElement!=null&&tinyMCE.getAttrib(tinyMCE.linkElement,'href')=="")tinyMCE.linkElement=null;if(tinyMCE.linkElement){href=tinyMCE.getAttrib(tinyMCE.linkElement,'href');target=tinyMCE.getAttrib(tinyMCE.linkElement,'target');title=tinyMCE.getAttrib(tinyMCE.linkElement,'title');onclick=tinyMCE.getAttrib(tinyMCE.linkElement,'onclick');style_class=tinyMCE.getAttrib(tinyMCE.linkElement,'class');if(onclick=="")onclick=tinyMCE.getAttrib(tinyMCE.linkElement,'onclick');onclick=tinyMCE.cleanupEventStr(onclick);href=eval(tinyMCE.settings['urlconverter_callback']+"(href, tinyMCE.linkElement, true);");mceRealHref=tinyMCE.getAttrib(tinyMCE.linkElement,'mce_href');if(mceRealHref!=""){href=mceRealHref;if(tinyMCE.getParam('convert_urls'))href=eval(tinyMCE.settings['urlconverter_callback']+"(href, tinyMCE.linkElement, true);")}action="update"}var template=new Array();template['file']='link.htm';template['width']=310;template['height']=200;template['width']+=tinyMCE.getLang('lang_insert_link_delta_width',0);template['height']+=tinyMCE.getLang('lang_insert_link_delta_height',0);if(inst.settings['insertlink_callback']){var returnVal=eval(inst.settings['insertlink_callback']+"(href, target, title, onclick, action, style_class);");if(returnVal&&returnVal['href'])TinyMCE_AdvancedTheme._insertLink(returnVal['href'],returnVal['target'],returnVal['title'],returnVal['onclick'],returnVal['style_class'])}else{tinyMCE.openWindow(template,{href:href,target:target,title:title,onclick:onclick,action:action,className:style_class,inline:"yes"})}return true;case"mceImage":var src="",alt="",border="",hspace="",vspace="",width="",height="",align="";var title="",onmouseover="",onmouseout="",action="insert";var img=tinyMCE.imgElement;var inst=tinyMCE.getInstanceById(editor_id);if(tinyMCE.selectedElement!=null&&tinyMCE.selectedElement.nodeName.toLowerCase()=="img"){img=tinyMCE.selectedElement;tinyMCE.imgElement=img}if(img){if(tinyMCE.getAttrib(img,'name').indexOf('mce_')==0)return true;src=tinyMCE.getAttrib(img,'src');alt=tinyMCE.getAttrib(img,'alt');if(alt=="")alt=tinyMCE.getAttrib(img,'title');if(tinyMCE.isGecko){var w=img.style.width;if(w!=null&&w!="")img.setAttribute("width",w);var h=img.style.height;if(h!=null&&h!="")img.setAttribute("height",h)}border=tinyMCE.getAttrib(img,'border');hspace=tinyMCE.getAttrib(img,'hspace');vspace=tinyMCE.getAttrib(img,'vspace');width=tinyMCE.getAttrib(img,'width');height=tinyMCE.getAttrib(img,'height');align=tinyMCE.getAttrib(img,'align');onmouseover=tinyMCE.getAttrib(img,'onmouseover');onmouseout=tinyMCE.getAttrib(img,'onmouseout');title=tinyMCE.getAttrib(img,'title');if(tinyMCE.isMSIE){width=img.attributes['width'].specified?width:"";height=img.attributes['height'].specified?height:""}src=eval(tinyMCE.settings['urlconverter_callback']+"(src, img, true);");mceRealSrc=tinyMCE.getAttrib(img,'mce_src');if(mceRealSrc!=""){src=mceRealSrc;if(tinyMCE.getParam('convert_urls'))src=eval(tinyMCE.settings['urlconverter_callback']+"(src, img, true);")}action="update"}var template=new Array();template['file']='image.htm?src={$src}';template['width']=355;template['height']=265+(tinyMCE.isMSIE?25:0);template['width']+=tinyMCE.getLang('lang_insert_image_delta_width',0);template['height']+=tinyMCE.getLang('lang_insert_image_delta_height',0);if(inst.settings['insertimage_callback']){var returnVal=eval(inst.settings['insertimage_callback']+"(src, alt, border, hspace, vspace, width, height, align, title, onmouseover, onmouseout, action);");if(returnVal&&returnVal['src'])TinyMCE_AdvancedTheme._insertImage(returnVal['src'],returnVal['alt'],returnVal['border'],returnVal['hspace'],returnVal['vspace'],returnVal['width'],returnVal['height'],returnVal['align'],returnVal['title'],returnVal['onmouseover'],returnVal['onmouseout'])}else tinyMCE.openWindow(template,{src:src,alt:alt,border:border,hspace:hspace,vspace:vspace,width:width,height:height,align:align,title:title,onmouseover:onmouseover,onmouseout:onmouseout,action:action,inline:"yes"});return true;case"forecolor":var fcp=new TinyMCE_Layer(editor_id+'_fcPreview',false),p,img,elm;TinyMCE_AdvancedTheme._hideMenus(editor_id);if(!fcp.exists()){fcp.create('div','mceColorPreview',document.getElementById(editor_id+'_toolbar'));elm=fcp.getElement();elm._editor_id=editor_id;elm._command="forecolor";elm._switchId=editor_id+"_forecolor";tinyMCE.addEvent(elm,'click',TinyMCE_AdvancedTheme._handleMenuEvent);tinyMCE.addEvent(elm,'mouseover',TinyMCE_AdvancedTheme._handleMenuEvent);tinyMCE.addEvent(elm,'mouseout',TinyMCE_AdvancedTheme._handleMenuEvent)}img=tinyMCE.selectNodes(document.getElementById(editor_id+"_forecolor"),function(n){return n.nodeName=="IMG"})[0];p=tinyMCE.getAbsPosition(img,document.getElementById(editor_id+'_toolbar'));fcp.moveTo(p.absLeft,p.absTop);fcp.getElement().style.backgroundColor=value!=null?value:tinyMCE.getInstanceById(editor_id).foreColor;fcp.show();return false;case"forecolorpicker":this._pickColor(editor_id,'forecolor');return true;case"forecolorMenu":TinyMCE_AdvancedTheme._hideMenus(editor_id);var ml=new TinyMCE_Layer(editor_id+'_fcMenu');if(!ml.exists())ml.create('div','mceMenu',document.body,TinyMCE_AdvancedTheme._getColorHTML(editor_id,'theme_advanced_text_colors','forecolor'));tinyMCE.switchClass(editor_id+'_forecolor','mceMenuButtonFocus');ml.moveRelativeTo(document.getElementById(editor_id+"_forecolor"),'bl');ml.moveBy(tinyMCE.isMSIE&&!tinyMCE.isOpera?-1:1,-1);if(tinyMCE.isOpera)ml.moveBy(0,-2);ml.show();return true;case"HiliteColor":var bcp=new TinyMCE_Layer(editor_id+'_bcPreview',false),p,img;TinyMCE_AdvancedTheme._hideMenus(editor_id);if(!bcp.exists()){bcp.create('div','mceColorPreview',document.getElementById(editor_id+'_toolbar'));elm=bcp.getElement();elm._editor_id=editor_id;elm._command="HiliteColor";elm._switchId=editor_id+"_backcolor";tinyMCE.addEvent(elm,'click',TinyMCE_AdvancedTheme._handleMenuEvent);tinyMCE.addEvent(elm,'mouseover',TinyMCE_AdvancedTheme._handleMenuEvent);tinyMCE.addEvent(elm,'mouseout',TinyMCE_AdvancedTheme._handleMenuEvent)}img=tinyMCE.selectNodes(document.getElementById(editor_id+"_backcolor"),function(n){return n.nodeName=="IMG"})[0];p=tinyMCE.getAbsPosition(img,document.getElementById(editor_id+'_toolbar'));bcp.moveTo(p.absLeft,p.absTop);bcp.getElement().style.backgroundColor=value!=null?value:tinyMCE.getInstanceById(editor_id).backColor;bcp.show();return false;case"HiliteColorMenu":TinyMCE_AdvancedTheme._hideMenus(editor_id);var ml=new TinyMCE_Layer(editor_id+'_bcMenu');if(!ml.exists())ml.create('div','mceMenu',document.body,TinyMCE_AdvancedTheme._getColorHTML(editor_id,'theme_advanced_background_colors','HiliteColor'));tinyMCE.switchClass(editor_id+'_backcolor','mceMenuButtonFocus');ml.moveRelativeTo(document.getElementById(editor_id+"_backcolor"),'bl');ml.moveBy(tinyMCE.isMSIE&&!tinyMCE.isOpera?-1:1,-1);if(tinyMCE.isOpera)ml.moveBy(0,-2);ml.show();return true;case"backcolorpicker":this._pickColor(editor_id,'HiliteColor');return true;case"mceColorPicker":if(user_interface){var template=[];if(!value['callback']&&!value['color'])value['color']=value['document'].getElementById(value['element_id']).value;template['file']='color_picker.htm';template['width']=380;template['height']=250;template['close_previous']="no";template['width']+=tinyMCE.getLang('lang_theme_advanced_colorpicker_delta_width',0);template['height']+=tinyMCE.getLang('lang_theme_advanced_colorpicker_delta_height',0);if(typeof(value['store_selection'])=="undefined")value['store_selection']=true;tinyMCE.lastColorPickerValue=value;tinyMCE.openWindow(template,{editor_id:editor_id,mce_store_selection:value['store_selection'],inline:"yes",command:"mceColorPicker",input_color:value['color']})}else{var savedVal=tinyMCE.lastColorPickerValue,elm;if(savedVal['callback']){savedVal['callback'](value);return true}elm=savedVal['document'].getElementById(savedVal['element_id']);elm.value=value;if(elm.onchange!=null&&elm.onchange!='')eval('elm.onchange();')}return true;case"mceCodeEditor":var template=new Array();template['file']='source_editor.htm';template['width']=parseInt(tinyMCE.getParam("theme_advanced_source_editor_width",720));template['height']=parseInt(tinyMCE.getParam("theme_advanced_source_editor_height",580));tinyMCE.openWindow(template,{editor_id:editor_id,resizable:"yes",scrollbars:"no",inline:"yes"});return true;case"mceCharMap":var template=new Array();template['file']='charmap.htm';template['width']=550+(tinyMCE.isOpera?40:0);template['height']=250;template['width']+=tinyMCE.getLang('lang_theme_advanced_charmap_delta_width',0);template['height']+=tinyMCE.getLang('lang_theme_advanced_charmap_delta_height',0);tinyMCE.openWindow(template,{editor_id:editor_id,inline:"yes"});return true;case"mceInsertAnchor":var template=new Array();template['file']='anchor.htm';template['width']=320;template['height']=90+(tinyMCE.isNS7?30:0);template['width']+=tinyMCE.getLang('lang_theme_advanced_anchor_delta_width',0);template['height']+=tinyMCE.getLang('lang_theme_advanced_anchor_delta_height',0);tinyMCE.openWindow(template,{editor_id:editor_id,inline:"yes"});return true;case"mceNewDocument":if(confirm(tinyMCE.getLang('lang_newdocument')))tinyMCE.execInstanceCommand(editor_id,'mceSetContent',false,' ');return true}return false},getEditorTemplate:function(settings,editorId){function removeFromArray(in_array,remove_array){var outArray=new Array(),skip;for(var i=0;i 

';var layoutManager=tinyMCE.getParam("theme_advanced_layout_manager","SimpleLayout");var styleSelectHTML='';if(settings['theme_advanced_styles']){var stylesAr=settings['theme_advanced_styles'].split(';');for(var i=0;i'+key+''}TinyMCE_AdvancedTheme._autoImportCSSClasses=false}switch(layoutManager){case"SimpleLayout":var toolbarHTML="";var toolbarLocation=tinyMCE.getParam("theme_advanced_toolbar_location","bottom");var toolbarAlign=tinyMCE.getParam("theme_advanced_toolbar_align","center");var pathLocation=tinyMCE.getParam("theme_advanced_path_location","none");var statusbarLocation=tinyMCE.getParam("theme_advanced_statusbar_location",pathLocation);var defVals={theme_advanced_buttons1:"bold,italic,underline,strikethrough,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,styleselect,formatselect",theme_advanced_buttons2:"bullist,numlist,separator,outdent,indent,separator,undo,redo,separator,link,unlink,anchor,image,cleanup,help,code",theme_advanced_buttons3:"hr,removeformat,visualaid,separator,sub,sup,separator,charmap"};toolbarHTML+='0){toolbarHTML+="
";deltaHeight-=23}}toolbarHTML+='
';template['html']='';if(toolbarLocation=="top")template['html']+='';if(statusbarLocation=="top"){template['html']+='';deltaHeight-=23}template['html']+='';if(toolbarLocation=="bottom")template['html']+='';if(toolbarLocation=="external"){var bod=document.body;var elm=document.createElement("div");toolbarHTML=tinyMCE.replaceVar(toolbarHTML,'style_select_options',styleSelectHTML);toolbarHTML=tinyMCE.applyTemplate(toolbarHTML,{editor_id:editorId});elm.className="mceToolbarExternal";elm.id=editorId+"_toolbar";elm.innerHTML='
'+toolbarHTML+'
'+statusbarHTML+'
'+toolbarHTML+'
'+toolbarHTML+'
';bod.appendChild(elm);deltaHeight=0;tinyMCE.getInstanceById(editorId).toolbarElement=elm;}else{tinyMCE.getInstanceById(editorId).toolbarElement=null}if(statusbarLocation=="bottom"){template['html']+=''+statusbarHTML+'';deltaHeight-=23}template['html']+='';break;case"RowLayout":template['html']='';var containers=tinyMCE.getParam("theme_advanced_containers","",true,",");var defaultContainerCSS=tinyMCE.getParam("theme_advanced_containers_default_class","container");var defaultContainerAlign=tinyMCE.getParam("theme_advanced_containers_default_align","center");for(var i=0;i';else if(containers[i]=="mceElementpath"||containers[i]=="mceStatusbar"){var pathClass="mceStatusbar";if(i==containers.length-1){pathClass="mceStatusbarBottom"}else if(i==0){pathClass="mceStatusbar"}else{deltaHeight-=2}template['html']+='';deltaHeight-=22}else{var curContainer=tinyMCE.getParam("theme_advanced_container_"+containers[i],"",true,',');var curContainerHTML="";var curAlign=tinyMCE.getParam("theme_advanced_container_"+containers[i]+"_align",defaultContainerAlign);var curCSS=tinyMCE.getParam("theme_advanced_container_"+containers[i]+"_class",defaultContainerCSS);curContainer=removeFromArray(curContainer,tinyMCE.getParam("theme_advanced_disable","",true,','));for(var j=0;j0){curContainerHTML+="
";deltaHeight-=23}template['html']+='
'}}template['html']+='
'+statusbarHTML+'
'+curContainerHTML+'
';break;case"CustomLayout":var customLayout=tinyMCE.getParam("theme_advanced_custom_layout","");if(customLayout!=""&&eval("typeof("+customLayout+")")!="undefined"){template=eval(customLayout+"(template);")}break}if(resizing)template['html']+='';template['html']=tinyMCE.replaceVar(template['html'],'style_select_options',styleSelectHTML);if(!template['delta_width'])template['delta_width']=0;if(!template['delta_height'])template['delta_height']=deltaHeight;return template},initInstance:function(inst){if(tinyMCE.getParam("theme_advanced_resizing",false)){if(tinyMCE.getParam("theme_advanced_resizing_use_cookie",true)){var w=TinyMCE_AdvancedTheme._getCookie("TinyMCE_"+inst.editorId+"_width");var h=TinyMCE_AdvancedTheme._getCookie("TinyMCE_"+inst.editorId+"_height");TinyMCE_AdvancedTheme._resizeTo(inst,w,h,tinyMCE.getParam("theme_advanced_resize_horizontal",true))}}inst.addShortcut('ctrl','k','lang_link_desc','mceLink')},removeInstance:function(inst){new TinyMCE_Layer(inst.editorId+'_fcMenu').remove();new TinyMCE_Layer(inst.editorId+'_bcMenu').remove()},hideInstance:function(inst){TinyMCE_AdvancedTheme._hideMenus(inst.editorId)},_handleMenuEvent:function(e){var te=tinyMCE.isMSIE?window.event.srcElement:e.target;tinyMCE._menuButtonEvent(e.type=="mouseover"?"over":"out",document.getElementById(te._switchId));if(e.type=="click")tinyMCE.execInstanceCommand(te._editor_id,te._command)},_hideMenus:function(id){var fcml=new TinyMCE_Layer(id+'_fcMenu'),bcml=new TinyMCE_Layer(id+'_bcMenu');if(fcml.exists()&&fcml.isVisible()){tinyMCE.switchClass(id+'_forecolor','mceMenuButton');fcml.hide()}if(bcml.exists()&&bcml.isVisible()){tinyMCE.switchClass(id+'_backcolor','mceMenuButton');bcml.hide()}},handleNodeChange:function(editor_id,node,undo_index,undo_levels,visual_aid,any_selection,setup_content){var alignNode,breakOut,classNode;function selectByValue(select_elm,value,first_index){first_index=typeof(first_index)=="undefined"?false:true;if(select_elm){for(var i=0;i=0;i--){var nodeName=path[i].nodeName.toLowerCase();var nodeData="";if(nodeName.indexOf("html:")==0)nodeName=nodeName.substring(5);if(nodeName=="b"){nodeName="strong"}if(nodeName=="i"){nodeName="em"}if(nodeName=="span"){var cn=tinyMCE.getAttrib(path[i],"class");if(cn!=""&&cn.indexOf('mceItem')==-1)nodeData+="class: "+cn+" ";var st=tinyMCE.getAttrib(path[i],"style");if(st!=""){st=tinyMCE.serializeStyle(tinyMCE.parseStyle(st));nodeData+="style: "+tinyMCE.xmlEncode(st)+" "}}if(nodeName=="font"){if(tinyMCE.getParam("convert_fonts_to_spans"))nodeName="span";var face=tinyMCE.getAttrib(path[i],"face");if(face!="")nodeData+="font: "+tinyMCE.xmlEncode(face)+" ";var size=tinyMCE.getAttrib(path[i],"size");if(size!="")nodeData+="size: "+tinyMCE.xmlEncode(size)+" ";var color=tinyMCE.getAttrib(path[i],"color");if(color!="")nodeData+="color: "+tinyMCE.xmlEncode(color)+" "}if(tinyMCE.getAttrib(path[i],'id')!=""){nodeData+="id: "+path[i].getAttribute('id')+" "}var className=tinyMCE.getVisualAidClass(tinyMCE.getAttrib(path[i],"class"),false);if(className!=""&&className.indexOf('mceItem')==-1)nodeData+="class: "+className+" ";if(tinyMCE.getAttrib(path[i],'src')!=""){var src=tinyMCE.getAttrib(path[i],"mce_src");if(src=="")src=tinyMCE.getAttrib(path[i],"src");nodeData+="src: "+tinyMCE.xmlEncode(src)+" "}if(path[i].nodeName=='A'&&tinyMCE.getAttrib(path[i],'href')!=""){var href=tinyMCE.getAttrib(path[i],"mce_href");if(href=="")href=tinyMCE.getAttrib(path[i],"href");nodeData+="href: "+tinyMCE.xmlEncode(href)+" "}className=tinyMCE.getAttrib(path[i],"class");if((nodeName=="img"||nodeName=="span")&&className.indexOf('mceItem')!=-1){nodeName=className.replace(/mceItem([a-z]+)/gi,'$1').toLowerCase();nodeData=path[i].getAttribute('title')}if(nodeName=="a"&&(anchor=tinyMCE.getAttrib(path[i],"name"))!=""){nodeName="a";nodeName+="#"+tinyMCE.xmlEncode(anchor);nodeData=""}if(tinyMCE.getAttrib(path[i],'name').indexOf("mce_")!=0){var className=tinyMCE.getVisualAidClass(tinyMCE.getAttrib(path[i],"class"),false);if(className!=""&&className.indexOf('mceItem')==-1){nodeName+="."+className}}var cmd='tinyMCE.execInstanceCommand(\''+editor_id+'\',\'mceSelectNodeDepth\',false,\''+i+'\');';html+=''+nodeName+'';if(i>0){html+=" » "}}pathElm.innerHTML=''+tinyMCE.getLang('lang_theme_path')+": "+html+' '}tinyMCE.switchClass(editor_id+'_justifyleft','mceButtonNormal');tinyMCE.switchClass(editor_id+'_justifyright','mceButtonNormal');tinyMCE.switchClass(editor_id+'_justifycenter','mceButtonNormal');tinyMCE.switchClass(editor_id+'_justifyfull','mceButtonNormal');tinyMCE.switchClass(editor_id+'_bold','mceButtonNormal');tinyMCE.switchClass(editor_id+'_italic','mceButtonNormal');tinyMCE.switchClass(editor_id+'_underline','mceButtonNormal');tinyMCE.switchClass(editor_id+'_strikethrough','mceButtonNormal');tinyMCE.switchClass(editor_id+'_bullist','mceButtonNormal');tinyMCE.switchClass(editor_id+'_numlist','mceButtonNormal');tinyMCE.switchClass(editor_id+'_sub','mceButtonNormal');tinyMCE.switchClass(editor_id+'_sup','mceButtonNormal');tinyMCE.switchClass(editor_id+'_anchor','mceButtonNormal');tinyMCE.switchClass(editor_id+'_link','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_unlink','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_outdent','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_image','mceButtonNormal');tinyMCE.switchClass(editor_id+'_hr','mceButtonNormal');if(node.nodeName=="A"&&tinyMCE.getAttrib(node,"class").indexOf('mceItemAnchor')!=-1)tinyMCE.switchClass(editor_id+'_anchor','mceButtonSelected');var anchorLink=tinyMCE.getParentElement(node,"a","href");if(anchorLink||any_selection){tinyMCE.switchClass(editor_id+'_link',anchorLink?'mceButtonSelected':'mceButtonNormal');tinyMCE.switchClass(editor_id+'_unlink',anchorLink?'mceButtonSelected':'mceButtonNormal')}tinyMCE.switchClass(editor_id+'_visualaid',visual_aid?'mceButtonSelected':'mceButtonNormal');if(undo_levels!=-1){tinyMCE.switchClass(editor_id+'_undo','mceButtonDisabled');tinyMCE.switchClass(editor_id+'_redo','mceButtonDisabled')}if(tinyMCE.getParentElement(node,"li,blockquote"))tinyMCE.switchClass(editor_id+'_outdent','mceButtonNormal');if(undo_index!=-1&&(undo_index0))tinyMCE.switchClass(editor_id+'_redo','mceButtonNormal');if(undo_index!=-1&&(undo_index>0&&undo_levels>0))tinyMCE.switchClass(editor_id+'_undo','mceButtonNormal');var selectElm=document.getElementById(editor_id+"_styleSelect");if(selectElm){TinyMCE_AdvancedTheme._setupCSSClasses(editor_id);classNode=node;breakOut=false;var index=0;do{if(classNode&&classNode.className){for(var i=0;i");else selectByValue(selectElm,"")}var selectElm=document.getElementById(editor_id+"_fontNameSelect");if(selectElm){if(!tinyMCE.isSafari&&!(tinyMCE.isMSIE&&!tinyMCE.isOpera)){var face=inst.queryCommandValue('FontName');face=face==null||face==""?"":face;selectByValue(selectElm,face,face!="")}else{var elm=tinyMCE.getParentElement(node,"font","face");if(elm){var family=tinyMCE.getAttrib(elm,"face");if(family=='')family=''+elm.style.fontFamily;if(!selectByValue(selectElm,family,family!=""))selectByValue(selectElm,"")}else selectByValue(selectElm,"")}}var selectElm=document.getElementById(editor_id+"_fontSizeSelect");if(selectElm){if(!tinyMCE.isSafari&&!tinyMCE.isOpera){var size=inst.queryCommandValue('FontSize');selectByValue(selectElm,size==null||size==""?"0":size)}else{var elm=tinyMCE.getParentElement(node,"font","size");if(elm){var size=tinyMCE.getAttrib(elm,"size");if(size==''){var sizes=new Array('','8px','10px','12px','14px','18px','24px','36px');size=''+elm.style.fontSize;for(var i=0;i0)selectElm.setAttribute('cssImported','true')}},_setCookie:function(name,value,expires,path,domain,secure){var curCookie=name+"="+escape(value)+((expires)?"; expires="+expires.toGMTString():"")+((path)?"; path="+escape(path):"")+((domain)?"; domain="+domain:"")+((secure)?"; secure":"");document.cookie=curCookie},_getCookie:function(name){var dc=document.cookie;var prefix=name+"=";var begin=dc.indexOf("; "+prefix);if(begin==-1){begin=dc.indexOf(prefix);if(begin!=0)return null}else begin+=2;var end=document.cookie.indexOf(";",begin);if(end==-1)end=dc.length;return unescape(dc.substring(begin+prefix.length,end))},_resizeTo:function(inst,w,h,set_w){var editorContainer=document.getElementById(inst.editorId+'_parent');var tableElm=editorContainer.firstChild;var iframe=inst.iframeElement;if(w==null||w=="null"){set_w=false;w=0}if(h==null||h=="null")return;w=parseInt(w);h=parseInt(h);if(tinyMCE.isGecko){w+=2;h+=2}var dx=w-tableElm.clientWidth;var dy=h-tableElm.clientHeight;w=w<1?30:w;h=h<1?30:h;if(set_w)tableElm.style.width=w+"px";tableElm.style.height=h+"px";iw=iframe.clientWidth+dx;ih=iframe.clientHeight+dy;iw=iw<1?30:iw;ih=ih<1?30:ih;if(tinyMCE.isGecko){iw-=2;ih-=2}if(set_w)iframe.style.width=iw+"px";iframe.style.height=ih+"px";if(set_w){var tableBodyElm=tableElm.firstChild;var minIframeWidth=tableBodyElm.scrollWidth;if(inst.iframeElement.clientWidth';for(i=0;i';if((i+1)%8==0)h+=''}h+='';if(tinyMCE.getParam("theme_advanced_more_colors",true))h+=''+tinyMCE.getLang('lang_more_colors')+'';return h},_pickColor:function(id,cm){var inputColor,inst=tinyMCE.selectedInstance;if(cm=='forecolor'&&inst)inputColor=inst.foreColor;if((cm=='backcolor'||cm=='HiliteColor')&&inst)inputColor=inst.backColor;tinyMCE.execCommand('mceColorPicker',true,{color:inputColor,callback:function(c){tinyMCE.execInstanceCommand(id,cm,false,c)}})},_insertImage:function(src,alt,border,hspace,vspace,width,height,align,title,onmouseover,onmouseout){tinyMCE.execCommand('mceBeginUndoLevel');if(src=="")return;if(!tinyMCE.imgElement&&tinyMCE.isSafari){var html="";html+=''+alt+'';tinyMCE.execCommand("mceInsertContent",false,html)}else{if(!tinyMCE.imgElement&&tinyMCE.selectedInstance){if(tinyMCE.isSafari)tinyMCE.execCommand("mceInsertContent",false,'');else tinyMCE.selectedInstance.contentDocument.execCommand("insertimage",false,tinyMCE.uniqueURL);tinyMCE.imgElement=tinyMCE.getElementByAttributeValue(tinyMCE.selectedInstance.contentDocument.body,"img","src",tinyMCE.uniqueURL)}}if(tinyMCE.imgElement){var needsRepaint=false;var msrc=src;src=eval(tinyMCE.settings['urlconverter_callback']+"(src, tinyMCE.imgElement);");if(tinyMCE.getParam('convert_urls'))msrc=src;if(onmouseover&&onmouseover!="")onmouseover="this.src='"+eval(tinyMCE.settings['urlconverter_callback']+"(onmouseover, tinyMCE.imgElement);")+"';";if(onmouseout&&onmouseout!="")onmouseout="this.src='"+eval(tinyMCE.settings['urlconverter_callback']+"(onmouseout, tinyMCE.imgElement);")+"';";if(typeof(title)=="undefined")title=alt;if(width!=tinyMCE.imgElement.getAttribute("width")||height!=tinyMCE.imgElement.getAttribute("height")||align!=tinyMCE.imgElement.getAttribute("align"))needsRepaint=true;tinyMCE.setAttrib(tinyMCE.imgElement,'src',src);tinyMCE.setAttrib(tinyMCE.imgElement,'mce_src',msrc);tinyMCE.setAttrib(tinyMCE.imgElement,'alt',alt);tinyMCE.setAttrib(tinyMCE.imgElement,'title',title);tinyMCE.setAttrib(tinyMCE.imgElement,'align',align);tinyMCE.setAttrib(tinyMCE.imgElement,'border',border,true);tinyMCE.setAttrib(tinyMCE.imgElement,'hspace',hspace,true);tinyMCE.setAttrib(tinyMCE.imgElement,'vspace',vspace,true);tinyMCE.setAttrib(tinyMCE.imgElement,'width',width,true);tinyMCE.setAttrib(tinyMCE.imgElement,'height',height,true);tinyMCE.setAttrib(tinyMCE.imgElement,'onmouseover',onmouseover);tinyMCE.setAttrib(tinyMCE.imgElement,'onmouseout',onmouseout);if(width&&width!="")tinyMCE.imgElement.style.pixelWidth=width;if(height&&height!="")tinyMCE.imgElement.style.pixelHeight=height;if(needsRepaint)tinyMCE.selectedInstance.repaint()}tinyMCE.execCommand('mceEndUndoLevel')},_insertLink:function(href,target,title,onclick,style_class){tinyMCE.execCommand('mceBeginUndoLevel');if(tinyMCE.selectedInstance&&tinyMCE.selectedElement&&tinyMCE.selectedElement.nodeName.toLowerCase()=="img"){var doc=tinyMCE.selectedInstance.getDoc();var linkElement=tinyMCE.getParentElement(tinyMCE.selectedElement,"a");var newLink=false;if(!linkElement){linkElement=doc.createElement("a");newLink=true}var mhref=href;var thref=eval(tinyMCE.settings['urlconverter_callback']+"(href, linkElement);");mhref=tinyMCE.getParam('convert_urls')?href:mhref;tinyMCE.setAttrib(linkElement,'href',thref);tinyMCE.setAttrib(linkElement,'mce_href',mhref);tinyMCE.setAttrib(linkElement,'target',target);tinyMCE.setAttrib(linkElement,'title',title);tinyMCE.setAttrib(linkElement,'onclick',onclick);tinyMCE.setAttrib(linkElement,'class',style_class);if(newLink){linkElement.appendChild(tinyMCE.selectedElement.cloneNode(true));tinyMCE.selectedElement.parentNode.replaceChild(linkElement,tinyMCE.selectedElement)}return}if(!tinyMCE.linkElement&&tinyMCE.selectedInstance){if(tinyMCE.isSafari){tinyMCE.execCommand("mceInsertContent",false,''+tinyMCE.selectedInstance.selection.getSelectedHTML()+'')}else tinyMCE.selectedInstance.contentDocument.execCommand("createlink",false,tinyMCE.uniqueURL);tinyMCE.linkElement=tinyMCE.getElementByAttributeValue(tinyMCE.selectedInstance.contentDocument.body,"a","href",tinyMCE.uniqueURL);var elementArray=tinyMCE.getElementsByAttributeValue(tinyMCE.selectedInstance.contentDocument.body,"a","href",tinyMCE.uniqueURL);for(var i=0;i 4 ? but[4] : false), (but.length > 5 ? but[5] : null)); + + if (but[0] == button_name) + return tinyMCE.getButtonHTML(but[0], but[2], '{$themeurl}/images/' + but[1], but[3], (but.length > 4 ? but[4] : false), (but.length > 5 ? but[5] : null)); + } + + // Custom controlls other than buttons + switch (button_name) { + case "formatselect": + var html = ''; + + return html; + + case "styleselect": + return ''; + + case "fontselect": + var fontHTML = ''; + return fontHTML; + + case "fontsizeselect": + return ''; + + case "|": + case "separator": + return ''; + + case "spacer": + return ''; + + case "rowseparator": + return '
'; + } + + return ""; + }, + + /** + * Theme specific execcommand handling. + */ + execCommand : function(editor_id, element, command, user_interface, value) { + switch (command) { + case 'mceHelp': + tinyMCE.openWindow({ + file : 'about.htm', + width : 480, + height : 380 + }, { + tinymce_version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion, + tinymce_releasedate : tinyMCE.releaseDate, + inline : "yes" + }); + return true; + + case "mceLink": + var inst = tinyMCE.getInstanceById(editor_id); + var doc = inst.getDoc(); + var selectedText = ""; + + if (tinyMCE.isMSIE) { + var rng = doc.selection.createRange(); + selectedText = rng.text; + } else + selectedText = inst.getSel().toString(); + + if (!tinyMCE.linkElement) { + if ((tinyMCE.selectedElement.nodeName.toLowerCase() != "img") && (selectedText.length <= 0)) + return true; + } + + var href = "", target = "", title = "", onclick = "", action = "insert", style_class = ""; + + if (tinyMCE.selectedElement.nodeName.toLowerCase() == "a") + tinyMCE.linkElement = tinyMCE.selectedElement; + + // Is anchor not a link + if (tinyMCE.linkElement != null && tinyMCE.getAttrib(tinyMCE.linkElement, 'href') == "") + tinyMCE.linkElement = null; + + if (tinyMCE.linkElement) { + href = tinyMCE.getAttrib(tinyMCE.linkElement, 'href'); + target = tinyMCE.getAttrib(tinyMCE.linkElement, 'target'); + title = tinyMCE.getAttrib(tinyMCE.linkElement, 'title'); + onclick = tinyMCE.getAttrib(tinyMCE.linkElement, 'onclick'); + style_class = tinyMCE.getAttrib(tinyMCE.linkElement, 'class'); + + // Try old onclick to if copy/pasted content + if (onclick == "") + onclick = tinyMCE.getAttrib(tinyMCE.linkElement, 'onclick'); + + onclick = tinyMCE.cleanupEventStr(onclick); + + href = eval(tinyMCE.settings['urlconverter_callback'] + "(href, tinyMCE.linkElement, true);"); + + // Use mce_href if defined + mceRealHref = tinyMCE.getAttrib(tinyMCE.linkElement, 'mce_href'); + if (mceRealHref != "") { + href = mceRealHref; + + if (tinyMCE.getParam('convert_urls')) + href = eval(tinyMCE.settings['urlconverter_callback'] + "(href, tinyMCE.linkElement, true);"); + } + + action = "update"; + } + + var template = new Array(); + + template['file'] = 'link.htm'; + template['width'] = 310; + template['height'] = 200; + + // Language specific width and height addons + template['width'] += tinyMCE.getLang('lang_insert_link_delta_width', 0); + template['height'] += tinyMCE.getLang('lang_insert_link_delta_height', 0); + + if (inst.settings['insertlink_callback']) { + var returnVal = eval(inst.settings['insertlink_callback'] + "(href, target, title, onclick, action, style_class);"); + if (returnVal && returnVal['href']) + TinyMCE_AdvancedTheme._insertLink(returnVal['href'], returnVal['target'], returnVal['title'], returnVal['onclick'], returnVal['style_class']); + } else { + tinyMCE.openWindow(template, {href : href, target : target, title : title, onclick : onclick, action : action, className : style_class, inline : "yes"}); + } + + return true; + + case "mceImage": + var src = "", alt = "", border = "", hspace = "", vspace = "", width = "", height = "", align = ""; + var title = "", onmouseover = "", onmouseout = "", action = "insert"; + var img = tinyMCE.imgElement; + var inst = tinyMCE.getInstanceById(editor_id); + + if (tinyMCE.selectedElement != null && tinyMCE.selectedElement.nodeName.toLowerCase() == "img") { + img = tinyMCE.selectedElement; + tinyMCE.imgElement = img; + } + + if (img) { + // Is it a internal MCE visual aid image, then skip this one. + if (tinyMCE.getAttrib(img, 'name').indexOf('mce_') == 0) + return true; + + src = tinyMCE.getAttrib(img, 'src'); + alt = tinyMCE.getAttrib(img, 'alt'); + + // Try polling out the title + if (alt == "") + alt = tinyMCE.getAttrib(img, 'title'); + + // Fix width/height attributes if the styles is specified + if (tinyMCE.isGecko) { + var w = img.style.width; + if (w != null && w != "") + img.setAttribute("width", w); + + var h = img.style.height; + if (h != null && h != "") + img.setAttribute("height", h); + } + + border = tinyMCE.getAttrib(img, 'border'); + hspace = tinyMCE.getAttrib(img, 'hspace'); + vspace = tinyMCE.getAttrib(img, 'vspace'); + width = tinyMCE.getAttrib(img, 'width'); + height = tinyMCE.getAttrib(img, 'height'); + align = tinyMCE.getAttrib(img, 'align'); + onmouseover = tinyMCE.getAttrib(img, 'onmouseover'); + onmouseout = tinyMCE.getAttrib(img, 'onmouseout'); + title = tinyMCE.getAttrib(img, 'title'); + + // Is realy specified? + if (tinyMCE.isMSIE) { + width = img.attributes['width'].specified ? width : ""; + height = img.attributes['height'].specified ? height : ""; + } + + //onmouseover = tinyMCE.getImageSrc(tinyMCE.cleanupEventStr(onmouseover)); + //onmouseout = tinyMCE.getImageSrc(tinyMCE.cleanupEventStr(onmouseout)); + + src = eval(tinyMCE.settings['urlconverter_callback'] + "(src, img, true);"); + + // Use mce_src if defined + mceRealSrc = tinyMCE.getAttrib(img, 'mce_src'); + if (mceRealSrc != "") { + src = mceRealSrc; + + if (tinyMCE.getParam('convert_urls')) + src = eval(tinyMCE.settings['urlconverter_callback'] + "(src, img, true);"); + } + + //if (onmouseover != "") + // onmouseover = eval(tinyMCE.settings['urlconverter_callback'] + "(onmouseover, img, true);"); + + //if (onmouseout != "") + // onmouseout = eval(tinyMCE.settings['urlconverter_callback'] + "(onmouseout, img, true);"); + + action = "update"; + } + + var template = new Array(); + + template['file'] = 'image.htm?src={$src}'; + template['width'] = 355; + template['height'] = 265 + (tinyMCE.isMSIE ? 25 : 0); + + // Language specific width and height addons + template['width'] += tinyMCE.getLang('lang_insert_image_delta_width', 0); + template['height'] += tinyMCE.getLang('lang_insert_image_delta_height', 0); + + if (inst.settings['insertimage_callback']) { + var returnVal = eval(inst.settings['insertimage_callback'] + "(src, alt, border, hspace, vspace, width, height, align, title, onmouseover, onmouseout, action);"); + if (returnVal && returnVal['src']) + TinyMCE_AdvancedTheme._insertImage(returnVal['src'], returnVal['alt'], returnVal['border'], returnVal['hspace'], returnVal['vspace'], returnVal['width'], returnVal['height'], returnVal['align'], returnVal['title'], returnVal['onmouseover'], returnVal['onmouseout']); + } else + tinyMCE.openWindow(template, {src : src, alt : alt, border : border, hspace : hspace, vspace : vspace, width : width, height : height, align : align, title : title, onmouseover : onmouseover, onmouseout : onmouseout, action : action, inline : "yes"}); + + return true; + + case "forecolor": + var fcp = new TinyMCE_Layer(editor_id + '_fcPreview', false), p, img, elm; + + TinyMCE_AdvancedTheme._hideMenus(editor_id); + + if (!fcp.exists()) { + fcp.create('div', 'mceColorPreview', document.getElementById(editor_id + '_toolbar')); + elm = fcp.getElement(); + elm._editor_id = editor_id; + elm._command = "forecolor"; + elm._switchId = editor_id + "_forecolor"; + tinyMCE.addEvent(elm, 'click', TinyMCE_AdvancedTheme._handleMenuEvent); + tinyMCE.addEvent(elm, 'mouseover', TinyMCE_AdvancedTheme._handleMenuEvent); + tinyMCE.addEvent(elm, 'mouseout', TinyMCE_AdvancedTheme._handleMenuEvent); + } + + img = tinyMCE.selectNodes(document.getElementById(editor_id + "_forecolor"), function(n) {return n.nodeName == "IMG";})[0]; + p = tinyMCE.getAbsPosition(img, document.getElementById(editor_id + '_toolbar')); + + fcp.moveTo(p.absLeft, p.absTop); + fcp.getElement().style.backgroundColor = value != null ? value : tinyMCE.getInstanceById(editor_id).foreColor; + fcp.show(); + + return false; + + case "forecolorpicker": + this._pickColor(editor_id, 'forecolor'); + return true; + + case "forecolorMenu": + TinyMCE_AdvancedTheme._hideMenus(editor_id); + + // Create color layer + var ml = new TinyMCE_Layer(editor_id + '_fcMenu'); + + if (!ml.exists()) + ml.create('div', 'mceMenu', document.body, TinyMCE_AdvancedTheme._getColorHTML(editor_id, 'theme_advanced_text_colors', 'forecolor')); + + tinyMCE.switchClass(editor_id + '_forecolor', 'mceMenuButtonFocus'); + ml.moveRelativeTo(document.getElementById(editor_id + "_forecolor"), 'bl'); + + ml.moveBy(tinyMCE.isMSIE && !tinyMCE.isOpera ? -1 : 1, -1); + + if (tinyMCE.isOpera) + ml.moveBy(0, -2); + + ml.show(); + return true; + + case "HiliteColor": + var bcp = new TinyMCE_Layer(editor_id + '_bcPreview', false), p, img; + + TinyMCE_AdvancedTheme._hideMenus(editor_id); + + if (!bcp.exists()) { + bcp.create('div', 'mceColorPreview', document.getElementById(editor_id + '_toolbar')); + elm = bcp.getElement(); + elm._editor_id = editor_id; + elm._command = "HiliteColor"; + elm._switchId = editor_id + "_backcolor"; + tinyMCE.addEvent(elm, 'click', TinyMCE_AdvancedTheme._handleMenuEvent); + tinyMCE.addEvent(elm, 'mouseover', TinyMCE_AdvancedTheme._handleMenuEvent); + tinyMCE.addEvent(elm, 'mouseout', TinyMCE_AdvancedTheme._handleMenuEvent); + } + + img = tinyMCE.selectNodes(document.getElementById(editor_id + "_backcolor"), function(n) {return n.nodeName == "IMG";})[0]; + p = tinyMCE.getAbsPosition(img, document.getElementById(editor_id + '_toolbar')); + + bcp.moveTo(p.absLeft, p.absTop); + bcp.getElement().style.backgroundColor = value != null ? value : tinyMCE.getInstanceById(editor_id).backColor; + bcp.show(); + + return false; + + case "HiliteColorMenu": + TinyMCE_AdvancedTheme._hideMenus(editor_id); + + // Create color layer + var ml = new TinyMCE_Layer(editor_id + '_bcMenu'); + + if (!ml.exists()) + ml.create('div', 'mceMenu', document.body, TinyMCE_AdvancedTheme._getColorHTML(editor_id, 'theme_advanced_background_colors', 'HiliteColor')); + + tinyMCE.switchClass(editor_id + '_backcolor', 'mceMenuButtonFocus'); + ml.moveRelativeTo(document.getElementById(editor_id + "_backcolor"), 'bl'); + + ml.moveBy(tinyMCE.isMSIE && !tinyMCE.isOpera ? -1 : 1, -1); + + if (tinyMCE.isOpera) + ml.moveBy(0, -2); + + ml.show(); + return true; + + case "backcolorpicker": + this._pickColor(editor_id, 'HiliteColor'); + return true; + + case "mceColorPicker": + if (user_interface) { + var template = []; + + if (!value['callback'] && !value['color']) + value['color'] = value['document'].getElementById(value['element_id']).value; + + template['file'] = 'color_picker.htm'; + template['width'] = 380; + template['height'] = 250; + template['close_previous'] = "no"; + + template['width'] += tinyMCE.getLang('lang_theme_advanced_colorpicker_delta_width', 0); + template['height'] += tinyMCE.getLang('lang_theme_advanced_colorpicker_delta_height', 0); + + if (typeof(value['store_selection']) == "undefined") + value['store_selection'] = true; + + tinyMCE.lastColorPickerValue = value; + tinyMCE.openWindow(template, {editor_id : editor_id, mce_store_selection : value['store_selection'], inline : "yes", command : "mceColorPicker", input_color : value['color']}); + } else { + var savedVal = tinyMCE.lastColorPickerValue, elm; + + if (savedVal['callback']) { + savedVal['callback'](value); + return true; + } + + elm = savedVal['document'].getElementById(savedVal['element_id']); + elm.value = value; + + if (elm.onchange != null && elm.onchange != '') + eval('elm.onchange();'); + } + return true; + + case "mceCodeEditor": + var template = new Array(); + + template['file'] = 'source_editor.htm'; + template['width'] = parseInt(tinyMCE.getParam("theme_advanced_source_editor_width", 720)); + template['height'] = parseInt(tinyMCE.getParam("theme_advanced_source_editor_height", 580)); + + tinyMCE.openWindow(template, {editor_id : editor_id, resizable : "yes", scrollbars : "no", inline : "yes"}); + return true; + + case "mceCharMap": + var template = new Array(); + + template['file'] = 'charmap.htm'; + template['width'] = 550 + (tinyMCE.isOpera ? 40 : 0); + template['height'] = 250; + + template['width'] += tinyMCE.getLang('lang_theme_advanced_charmap_delta_width', 0); + template['height'] += tinyMCE.getLang('lang_theme_advanced_charmap_delta_height', 0); + + tinyMCE.openWindow(template, {editor_id : editor_id, inline : "yes"}); + return true; + + case "mceInsertAnchor": + var template = new Array(); + + template['file'] = 'anchor.htm'; + template['width'] = 320; + template['height'] = 90 + (tinyMCE.isNS7 ? 30 : 0); + + template['width'] += tinyMCE.getLang('lang_theme_advanced_anchor_delta_width', 0); + template['height'] += tinyMCE.getLang('lang_theme_advanced_anchor_delta_height', 0); + + tinyMCE.openWindow(template, {editor_id : editor_id, inline : "yes"}); + return true; + + case "mceNewDocument": + if (confirm(tinyMCE.getLang('lang_newdocument'))) + tinyMCE.execInstanceCommand(editor_id, 'mceSetContent', false, ' '); + + return true; + } + + return false; + }, + + /** + * Editor instance template function. + */ + getEditorTemplate : function(settings, editorId) { + function removeFromArray(in_array, remove_array) { + var outArray = new Array(), skip; + + for (var i=0; i 

'; + var layoutManager = tinyMCE.getParam("theme_advanced_layout_manager", "SimpleLayout"); + + // Setup style select options -- MOVED UP FOR EXTERNAL TOOLBAR COMPATABILITY! + var styleSelectHTML = ''; + if (settings['theme_advanced_styles']) { + var stylesAr = settings['theme_advanced_styles'].split(';'); + + for (var i=0; i' + key + ''; + } + + TinyMCE_AdvancedTheme._autoImportCSSClasses = false; + } + + switch(layoutManager) { + case "SimpleLayout" : //the default TinyMCE Layout (for backwards compatibility)... + var toolbarHTML = ""; + var toolbarLocation = tinyMCE.getParam("theme_advanced_toolbar_location", "bottom"); + var toolbarAlign = tinyMCE.getParam("theme_advanced_toolbar_align", "center"); + var pathLocation = tinyMCE.getParam("theme_advanced_path_location", "none"); // Compatiblity + var statusbarLocation = tinyMCE.getParam("theme_advanced_statusbar_location", pathLocation); + var defVals = { + theme_advanced_buttons1 : "bold,italic,underline,strikethrough,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,styleselect,formatselect", + theme_advanced_buttons2 : "bullist,numlist,separator,outdent,indent,separator,undo,redo,separator,link,unlink,anchor,image,cleanup,help,code", + theme_advanced_buttons3 : "hr,removeformat,visualaid,separator,sub,sup,separator,charmap" + }; + + // Add accessibility control + toolbarHTML += ' 0) { + toolbarHTML += "
"; + deltaHeight -= 23; + } + } + + // Add accessibility control + toolbarHTML += '
'; + + // Setup template html + template['html'] = ''; + + if (toolbarLocation == "top") + template['html'] += ''; + + if (statusbarLocation == "top") { + template['html'] += ''; + deltaHeight -= 23; + } + + template['html'] += ''; + + if (toolbarLocation == "bottom") + template['html'] += ''; + + // External toolbar changes + if (toolbarLocation == "external") { + var bod = document.body; + var elm = document.createElement ("div"); + + toolbarHTML = tinyMCE.replaceVar(toolbarHTML, 'style_select_options', styleSelectHTML); + toolbarHTML = tinyMCE.applyTemplate(toolbarHTML, {editor_id : editorId}); + + elm.className = "mceToolbarExternal"; + elm.id = editorId+"_toolbar"; + elm.innerHTML = '
' + toolbarHTML + '
' + statusbarHTML + '
' + toolbarHTML + '
'+toolbarHTML+'
'; + bod.appendChild (elm); + // bod.style.marginTop = elm.offsetHeight + "px"; + + deltaHeight = 0; + tinyMCE.getInstanceById(editorId).toolbarElement = elm; + + //template['html'] = '
'+toolbarHTML+'
' + template["html"]; + } else { + tinyMCE.getInstanceById(editorId).toolbarElement = null; + } + + if (statusbarLocation == "bottom") { + template['html'] += '' + statusbarHTML + ''; + deltaHeight -= 23; + } + + template['html'] += ''; + //"SimpleLayout" + break; + + case "RowLayout" : //Container Layout - containers defined in "theme_advanced_containers" are rendered from top to bottom. + template['html'] = ''; + + var containers = tinyMCE.getParam("theme_advanced_containers", "", true, ","); + var defaultContainerCSS = tinyMCE.getParam("theme_advanced_containers_default_class", "container"); + var defaultContainerAlign = tinyMCE.getParam("theme_advanced_containers_default_align", "center"); + + //Render Containers: + for (var i = 0; i < containers.length; i++) + { + if (containers[i] == "mceEditor") //Exceptions for mceEditor and ... + template['html'] += ''; + else if (containers[i] == "mceElementpath" || containers[i] == "mceStatusbar") // ... mceElementpath: + { + var pathClass = "mceStatusbar"; + + if (i == containers.length-1) + { + pathClass = "mceStatusbarBottom"; + } + else if (i == 0) + { + pathClass = "mceStatusbar"; + } + else + { + deltaHeight-=2; + } + + template['html'] += ''; + deltaHeight -= 22; + } else { // Render normal Container + var curContainer = tinyMCE.getParam("theme_advanced_container_"+containers[i], "", true, ','); + var curContainerHTML = ""; + var curAlign = tinyMCE.getParam("theme_advanced_container_"+containers[i]+"_align", defaultContainerAlign); + var curCSS = tinyMCE.getParam("theme_advanced_container_"+containers[i]+"_class", defaultContainerCSS); + + curContainer = removeFromArray(curContainer, tinyMCE.getParam("theme_advanced_disable", "", true, ',')); + + for (var j=0; j 0) { + curContainerHTML += "
"; + deltaHeight -= 23; + } + + template['html'] += '
'; + } + } + + template['html'] += '
' + statusbarHTML + '
' + curContainerHTML + '
'; + //RowLayout + break; + + case "CustomLayout" : //User defined layout callback... + var customLayout = tinyMCE.getParam("theme_advanced_custom_layout",""); + + if (customLayout != "" && eval("typeof(" + customLayout + ")") != "undefined") { + template = eval(customLayout + "(template);"); + } + break; + } + + if (resizing) + template['html'] += ''; + + template['html'] = tinyMCE.replaceVar(template['html'], 'style_select_options', styleSelectHTML); + + // Set to default values + if (!template['delta_width']) + template['delta_width'] = 0; + + if (!template['delta_height']) + template['delta_height'] = deltaHeight; + + return template; + }, + + initInstance : function(inst) { + if (tinyMCE.getParam("theme_advanced_resizing", false)) { + if (tinyMCE.getParam("theme_advanced_resizing_use_cookie", true)) { + var w = TinyMCE_AdvancedTheme._getCookie("TinyMCE_" + inst.editorId + "_width"); + var h = TinyMCE_AdvancedTheme._getCookie("TinyMCE_" + inst.editorId + "_height"); + + TinyMCE_AdvancedTheme._resizeTo(inst, w, h, tinyMCE.getParam("theme_advanced_resize_horizontal", true)); + } + } + + inst.addShortcut('ctrl', 'k', 'lang_link_desc', 'mceLink'); + }, + + removeInstance : function(inst) { + new TinyMCE_Layer(inst.editorId + '_fcMenu').remove(); + new TinyMCE_Layer(inst.editorId + '_bcMenu').remove(); + }, + + hideInstance : function(inst) { + TinyMCE_AdvancedTheme._hideMenus(inst.editorId); + }, + + _handleMenuEvent : function(e) { + var te = tinyMCE.isMSIE ? window.event.srcElement : e.target; + tinyMCE._menuButtonEvent(e.type == "mouseover" ? "over" : "out", document.getElementById(te._switchId)); + + if (e.type == "click") + tinyMCE.execInstanceCommand(te._editor_id, te._command); + }, + + _hideMenus : function(id) { + var fcml = new TinyMCE_Layer(id + '_fcMenu'), bcml = new TinyMCE_Layer(id + '_bcMenu'); + + if (fcml.exists() && fcml.isVisible()) { + tinyMCE.switchClass(id + '_forecolor', 'mceMenuButton'); + fcml.hide(); + } + + if (bcml.exists() && bcml.isVisible()) { + tinyMCE.switchClass(id + '_backcolor', 'mceMenuButton'); + bcml.hide(); + } + }, + + /** + * Node change handler. + */ + handleNodeChange : function(editor_id, node, undo_index, undo_levels, visual_aid, any_selection, setup_content) { + var alignNode, breakOut, classNode; + + function selectByValue(select_elm, value, first_index) { + first_index = typeof(first_index) == "undefined" ? false : true; + + if (select_elm) { + for (var i=0; i=0; i--) { + var nodeName = path[i].nodeName.toLowerCase(); + var nodeData = ""; + + if (nodeName.indexOf("html:") == 0) + nodeName = nodeName.substring(5); + + if (nodeName == "b") { + nodeName = "strong"; + } + + if (nodeName == "i") { + nodeName = "em"; + } + + if (nodeName == "span") { + var cn = tinyMCE.getAttrib(path[i], "class"); + if (cn != "" && cn.indexOf('mceItem') == -1) + nodeData += "class: " + cn + " "; + + var st = tinyMCE.getAttrib(path[i], "style"); + if (st != "") { + st = tinyMCE.serializeStyle(tinyMCE.parseStyle(st)); + nodeData += "style: " + tinyMCE.xmlEncode(st) + " "; + } + } + + if (nodeName == "font") { + if (tinyMCE.getParam("convert_fonts_to_spans")) + nodeName = "span"; + + var face = tinyMCE.getAttrib(path[i], "face"); + if (face != "") + nodeData += "font: " + tinyMCE.xmlEncode(face) + " "; + + var size = tinyMCE.getAttrib(path[i], "size"); + if (size != "") + nodeData += "size: " + tinyMCE.xmlEncode(size) + " "; + + var color = tinyMCE.getAttrib(path[i], "color"); + if (color != "") + nodeData += "color: " + tinyMCE.xmlEncode(color) + " "; + } + + if (tinyMCE.getAttrib(path[i], 'id') != "") { + nodeData += "id: " + path[i].getAttribute('id') + " "; + } + + var className = tinyMCE.getVisualAidClass(tinyMCE.getAttrib(path[i], "class"), false); + if (className != "" && className.indexOf('mceItem') == -1) + nodeData += "class: " + className + " "; + + if (tinyMCE.getAttrib(path[i], 'src') != "") { + var src = tinyMCE.getAttrib(path[i], "mce_src"); + + if (src == "") + src = tinyMCE.getAttrib(path[i], "src"); + + nodeData += "src: " + tinyMCE.xmlEncode(src) + " "; + } + + if (path[i].nodeName == 'A' && tinyMCE.getAttrib(path[i], 'href') != "") { + var href = tinyMCE.getAttrib(path[i], "mce_href"); + + if (href == "") + href = tinyMCE.getAttrib(path[i], "href"); + + nodeData += "href: " + tinyMCE.xmlEncode(href) + " "; + } + + className = tinyMCE.getAttrib(path[i], "class"); + if ((nodeName == "img" || nodeName == "span") && className.indexOf('mceItem') != -1) { + nodeName = className.replace(/mceItem([a-z]+)/gi, '$1').toLowerCase(); + nodeData = path[i].getAttribute('title'); + } + + if (nodeName == "a" && (anchor = tinyMCE.getAttrib(path[i], "name")) != "") { + nodeName = "a"; + nodeName += "#" + tinyMCE.xmlEncode(anchor); + nodeData = ""; + } + + if (tinyMCE.getAttrib(path[i], 'name').indexOf("mce_") != 0) { + var className = tinyMCE.getVisualAidClass(tinyMCE.getAttrib(path[i], "class"), false); + if (className != "" && className.indexOf('mceItem') == -1) { + nodeName += "." + className; + } + } + + var cmd = 'tinyMCE.execInstanceCommand(\'' + editor_id + '\',\'mceSelectNodeDepth\',false,\'' + i + '\');'; + html += '' + nodeName + ''; + + if (i > 0) { + html += " » "; + } + } + + pathElm.innerHTML = '' + tinyMCE.getLang('lang_theme_path') + ": " + html + ' '; + } + + // Reset old states + tinyMCE.switchClass(editor_id + '_justifyleft', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_justifyright', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_justifycenter', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_justifyfull', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_bold', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_italic', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_underline', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_strikethrough', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_bullist', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_numlist', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_sub', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_sup', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_anchor', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_link', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_unlink', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_outdent', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_image', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_hr', 'mceButtonNormal'); + + if (node.nodeName == "A" && tinyMCE.getAttrib(node, "class").indexOf('mceItemAnchor') != -1) + tinyMCE.switchClass(editor_id + '_anchor', 'mceButtonSelected'); + + // Get link + var anchorLink = tinyMCE.getParentElement(node, "a", "href"); + + if (anchorLink || any_selection) { + tinyMCE.switchClass(editor_id + '_link', anchorLink ? 'mceButtonSelected' : 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_unlink', anchorLink ? 'mceButtonSelected' : 'mceButtonNormal'); + } + + // Handle visual aid + tinyMCE.switchClass(editor_id + '_visualaid', visual_aid ? 'mceButtonSelected' : 'mceButtonNormal'); + + if (undo_levels != -1) { + tinyMCE.switchClass(editor_id + '_undo', 'mceButtonDisabled'); + tinyMCE.switchClass(editor_id + '_redo', 'mceButtonDisabled'); + } + + // Within li, blockquote + if (tinyMCE.getParentElement(node, "li,blockquote")) + tinyMCE.switchClass(editor_id + '_outdent', 'mceButtonNormal'); + + // Has redo levels + if (undo_index != -1 && (undo_index < undo_levels-1 && undo_levels > 0)) + tinyMCE.switchClass(editor_id + '_redo', 'mceButtonNormal'); + + // Has undo levels + if (undo_index != -1 && (undo_index > 0 && undo_levels > 0)) + tinyMCE.switchClass(editor_id + '_undo', 'mceButtonNormal'); + + // Select class in select box + var selectElm = document.getElementById(editor_id + "_styleSelect"); + + if (selectElm) { + TinyMCE_AdvancedTheme._setupCSSClasses(editor_id); + + classNode = node; + breakOut = false; + var index = 0; + + do { + if (classNode && classNode.className) { + for (var i=0; i"); + else + selectByValue(selectElm, ""); + } + + // Select fontselect + var selectElm = document.getElementById(editor_id + "_fontNameSelect"); + if (selectElm) { + if (!tinyMCE.isSafari && !(tinyMCE.isMSIE && !tinyMCE.isOpera)) { + var face = inst.queryCommandValue('FontName'); + + face = face == null || face == "" ? "" : face; + + selectByValue(selectElm, face, face != ""); + } else { + var elm = tinyMCE.getParentElement(node, "font", "face"); + + if (elm) { + var family = tinyMCE.getAttrib(elm, "face"); + + if (family == '') + family = '' + elm.style.fontFamily; + + if (!selectByValue(selectElm, family, family != "")) + selectByValue(selectElm, ""); + } else + selectByValue(selectElm, ""); + } + } + + // Select fontsize + var selectElm = document.getElementById(editor_id + "_fontSizeSelect"); + if (selectElm) { + if (!tinyMCE.isSafari && !tinyMCE.isOpera) { + var size = inst.queryCommandValue('FontSize'); + selectByValue(selectElm, size == null || size == "" ? "0" : size); + } else { + var elm = tinyMCE.getParentElement(node, "font", "size"); + if (elm) { + var size = tinyMCE.getAttrib(elm, "size"); + + if (size == '') { + var sizes = new Array('', '8px', '10px', '12px', '14px', '18px', '24px', '36px'); + + size = '' + elm.style.fontSize; + + for (var i=0; i 0) + selectElm.setAttribute('cssImported', 'true'); + } + }, + + _setCookie : function(name, value, expires, path, domain, secure) { + var curCookie = name + "=" + escape(value) + + ((expires) ? "; expires=" + expires.toGMTString() : "") + + ((path) ? "; path=" + escape(path) : "") + + ((domain) ? "; domain=" + domain : "") + + ((secure) ? "; secure" : ""); + + document.cookie = curCookie; + }, + + _getCookie : function(name) { + var dc = document.cookie; + var prefix = name + "="; + var begin = dc.indexOf("; " + prefix); + + if (begin == -1) { + begin = dc.indexOf(prefix); + + if (begin != 0) + return null; + } else + begin += 2; + + var end = document.cookie.indexOf(";", begin); + + if (end == -1) + end = dc.length; + + return unescape(dc.substring(begin + prefix.length, end)); + }, + + _resizeTo : function(inst, w, h, set_w) { + var editorContainer = document.getElementById(inst.editorId + '_parent'); + var tableElm = editorContainer.firstChild; + var iframe = inst.iframeElement; + + if (w == null || w == "null") { + set_w = false; + w = 0; + } + + if (h == null || h == "null") + return; + + w = parseInt(w); + h = parseInt(h); + + if (tinyMCE.isGecko) { + w += 2; + h += 2; + } + + var dx = w - tableElm.clientWidth; + var dy = h - tableElm.clientHeight; + + w = w < 1 ? 30 : w; + h = h < 1 ? 30 : h; + + if (set_w) + tableElm.style.width = w + "px"; + + tableElm.style.height = h + "px"; + + iw = iframe.clientWidth + dx; + ih = iframe.clientHeight + dy; + + iw = iw < 1 ? 30 : iw; + ih = ih < 1 ? 30 : ih; + + if (tinyMCE.isGecko) { + iw -= 2; + ih -= 2; + } + + if (set_w) + iframe.style.width = iw + "px"; + + iframe.style.height = ih + "px"; + + // Is it to small, make it bigger again + if (set_w) { + var tableBodyElm = tableElm.firstChild; + var minIframeWidth = tableBodyElm.scrollWidth; + if (inst.iframeElement.clientWidth < minIframeWidth) { + dx = minIframeWidth - inst.iframeElement.clientWidth; + + inst.iframeElement.style.width = (iw + dx) + "px"; + } + } + + // Remove pesky table controls + inst.useCSS = false; + }, + + /** + * Handles resizing events. + */ + _resizeEventHandler : function(e) { + var resizer = TinyMCE_AdvancedTheme._resizer; + + // Do nothing + if (!resizer.resizing) + return; + + e = typeof(e) == "undefined" ? window.event : e; + + var dx = e.screenX - resizer.downX; + var dy = e.screenY - resizer.downY; + var resizeBox = resizer.resizeBox; + var editorId = resizer.editorId; + + switch (e.type) { + case "mousemove": + var w, h; + + w = resizer.width + dx; + h = resizer.height + dy; + + w = w < 1 ? 1 : w; + h = h < 1 ? 1 : h; + + if (resizer.horizontal) + resizeBox.style.width = w + "px"; + + resizeBox.style.height = h + "px"; + break; + + case "mouseup": + TinyMCE_AdvancedTheme._setResizing(e, editorId, false); + TinyMCE_AdvancedTheme._resizeTo(tinyMCE.getInstanceById(editorId), resizer.width + dx, resizer.height + dy, resizer.horizontal); + + // Expire in a month + if (tinyMCE.getParam("theme_advanced_resizing_use_cookie", true)) { + var expires = new Date(); + expires.setTime(expires.getTime() + 3600000 * 24 * 30); + + // Set the cookies + TinyMCE_AdvancedTheme._setCookie("TinyMCE_" + editorId + "_width", "" + (resizer.horizontal ? resizer.width + dx : ""), expires); + TinyMCE_AdvancedTheme._setCookie("TinyMCE_" + editorId + "_height", "" + (resizer.height + dy), expires); + } + break; + } + }, + + /** + * Starts/stops the editor resizing. + */ + _setResizing : function(e, editor_id, state) { + e = typeof(e) == "undefined" ? window.event : e; + + var resizer = TinyMCE_AdvancedTheme._resizer; + var editorContainer = document.getElementById(editor_id + '_parent'); + var editorArea = document.getElementById(editor_id + '_parent').firstChild; + var resizeBox = document.getElementById(editor_id + '_resize_box'); + var inst = tinyMCE.getInstanceById(editor_id); + + if (state) { + // Place box over editor area + var width = editorArea.clientWidth; + var height = editorArea.clientHeight; + + resizeBox.style.width = width + "px"; + resizeBox.style.height = height + "px"; + + resizer.iframeWidth = inst.iframeElement.clientWidth; + resizer.iframeHeight = inst.iframeElement.clientHeight; + + // Hide editor and show resize box + editorArea.style.display = "none"; + resizeBox.style.display = "block"; + + // Add event handlers, only once + if (!resizer.eventHandlers) { + if (tinyMCE.isMSIE) + tinyMCE.addEvent(document, "mousemove", TinyMCE_AdvancedTheme._resizeEventHandler); + else + tinyMCE.addEvent(window, "mousemove", TinyMCE_AdvancedTheme._resizeEventHandler); + + tinyMCE.addEvent(document, "mouseup", TinyMCE_AdvancedTheme._resizeEventHandler); + + resizer.eventHandlers = true; + } + + resizer.resizing = true; + resizer.downX = e.screenX; + resizer.downY = e.screenY; + resizer.width = parseInt(resizeBox.style.width); + resizer.height = parseInt(resizeBox.style.height); + resizer.editorId = editor_id; + resizer.resizeBox = resizeBox; + resizer.horizontal = tinyMCE.getParam("theme_advanced_resize_horizontal", true); + } else { + resizer.resizing = false; + resizeBox.style.display = "none"; + editorArea.style.display = tinyMCE.isMSIE && !tinyMCE.isOpera ? "block" : "table"; + tinyMCE.execCommand('mceResetDesignMode'); + } + }, + + _getColorHTML : function(id, n, cm) { + var i, h, cl; + + h = ''; + cl = tinyMCE.getParam(n, TinyMCE_AdvancedTheme._defColors).split(','); + + h += ''; + for (i=0; i'; + + if ((i+1) % 8 == 0) + h += ''; + } + + h += '
'; + + if (tinyMCE.getParam("theme_advanced_more_colors", true)) + h += '' + tinyMCE.getLang('lang_more_colors') + ''; + + return h; + }, + + _pickColor : function(id, cm) { + var inputColor, inst = tinyMCE.selectedInstance; + + if (cm == 'forecolor' && inst) + inputColor = inst.foreColor; + + if ((cm == 'backcolor' || cm == 'HiliteColor') && inst) + inputColor = inst.backColor; + + tinyMCE.execCommand('mceColorPicker', true, {color : inputColor, callback : function(c) { + tinyMCE.execInstanceCommand(id, cm, false, c); + }}); + }, + + _insertImage : function(src, alt, border, hspace, vspace, width, height, align, title, onmouseover, onmouseout) { + tinyMCE.execCommand('mceBeginUndoLevel'); + + if (src == "") + return; + + if (!tinyMCE.imgElement && tinyMCE.isSafari) { + var html = ""; + + html += '' + alt + ''; + + tinyMCE.execCommand("mceInsertContent", false, html); + } else { + if (!tinyMCE.imgElement && tinyMCE.selectedInstance) { + if (tinyMCE.isSafari) + tinyMCE.execCommand("mceInsertContent", false, ''); + else + tinyMCE.selectedInstance.contentDocument.execCommand("insertimage", false, tinyMCE.uniqueURL); + + tinyMCE.imgElement = tinyMCE.getElementByAttributeValue(tinyMCE.selectedInstance.contentDocument.body, "img", "src", tinyMCE.uniqueURL); + } + } + + if (tinyMCE.imgElement) { + var needsRepaint = false; + var msrc = src; + + src = eval(tinyMCE.settings['urlconverter_callback'] + "(src, tinyMCE.imgElement);"); + + if (tinyMCE.getParam('convert_urls')) + msrc = src; + + if (onmouseover && onmouseover != "") + onmouseover = "this.src='" + eval(tinyMCE.settings['urlconverter_callback'] + "(onmouseover, tinyMCE.imgElement);") + "';"; + + if (onmouseout && onmouseout != "") + onmouseout = "this.src='" + eval(tinyMCE.settings['urlconverter_callback'] + "(onmouseout, tinyMCE.imgElement);") + "';"; + + // Use alt as title if it's undefined + if (typeof(title) == "undefined") + title = alt; + + if (width != tinyMCE.imgElement.getAttribute("width") || height != tinyMCE.imgElement.getAttribute("height") || align != tinyMCE.imgElement.getAttribute("align")) + needsRepaint = true; + + tinyMCE.setAttrib(tinyMCE.imgElement, 'src', src); + tinyMCE.setAttrib(tinyMCE.imgElement, 'mce_src', msrc); + tinyMCE.setAttrib(tinyMCE.imgElement, 'alt', alt); + tinyMCE.setAttrib(tinyMCE.imgElement, 'title', title); + tinyMCE.setAttrib(tinyMCE.imgElement, 'align', align); + tinyMCE.setAttrib(tinyMCE.imgElement, 'border', border, true); + tinyMCE.setAttrib(tinyMCE.imgElement, 'hspace', hspace, true); + tinyMCE.setAttrib(tinyMCE.imgElement, 'vspace', vspace, true); + tinyMCE.setAttrib(tinyMCE.imgElement, 'width', width, true); + tinyMCE.setAttrib(tinyMCE.imgElement, 'height', height, true); + tinyMCE.setAttrib(tinyMCE.imgElement, 'onmouseover', onmouseover); + tinyMCE.setAttrib(tinyMCE.imgElement, 'onmouseout', onmouseout); + + // Fix for bug #989846 - Image resize bug + if (width && width != "") + tinyMCE.imgElement.style.pixelWidth = width; + + if (height && height != "") + tinyMCE.imgElement.style.pixelHeight = height; + + if (needsRepaint) + tinyMCE.selectedInstance.repaint(); + } + + tinyMCE.execCommand('mceEndUndoLevel'); + }, + + _insertLink : function(href, target, title, onclick, style_class) { + tinyMCE.execCommand('mceBeginUndoLevel'); + + if (tinyMCE.selectedInstance && tinyMCE.selectedElement && tinyMCE.selectedElement.nodeName.toLowerCase() == "img") { + var doc = tinyMCE.selectedInstance.getDoc(); + var linkElement = tinyMCE.getParentElement(tinyMCE.selectedElement, "a"); + var newLink = false; + + if (!linkElement) { + linkElement = doc.createElement("a"); + newLink = true; + } + + var mhref = href; + var thref = eval(tinyMCE.settings['urlconverter_callback'] + "(href, linkElement);"); + mhref = tinyMCE.getParam('convert_urls') ? href : mhref; + + tinyMCE.setAttrib(linkElement, 'href', thref); + tinyMCE.setAttrib(linkElement, 'mce_href', mhref); + tinyMCE.setAttrib(linkElement, 'target', target); + tinyMCE.setAttrib(linkElement, 'title', title); + tinyMCE.setAttrib(linkElement, 'onclick', onclick); + tinyMCE.setAttrib(linkElement, 'class', style_class); + + if (newLink) { + linkElement.appendChild(tinyMCE.selectedElement.cloneNode(true)); + tinyMCE.selectedElement.parentNode.replaceChild(linkElement, tinyMCE.selectedElement); + } + + return; + } + + if (!tinyMCE.linkElement && tinyMCE.selectedInstance) { + if (tinyMCE.isSafari) { + tinyMCE.execCommand("mceInsertContent", false, '' + tinyMCE.selectedInstance.selection.getSelectedHTML() + ''); + } else + tinyMCE.selectedInstance.contentDocument.execCommand("createlink", false, tinyMCE.uniqueURL); + + tinyMCE.linkElement = tinyMCE.getElementByAttributeValue(tinyMCE.selectedInstance.contentDocument.body, "a", "href", tinyMCE.uniqueURL); + + var elementArray = tinyMCE.getElementsByAttributeValue(tinyMCE.selectedInstance.contentDocument.body, "a", "href", tinyMCE.uniqueURL); + + for (var i=0; i + + {$lang_insert_image_title} + + + + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
 
+ x +
+
+
+ +
+
+ +
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/anchor.gif Binary file includes/clientside/tinymce/themes/advanced/images/anchor.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/anchor_symbol.gif Binary file includes/clientside/tinymce/themes/advanced/images/anchor_symbol.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/backcolor.gif Binary file includes/clientside/tinymce/themes/advanced/images/backcolor.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/bold.gif Binary file includes/clientside/tinymce/themes/advanced/images/bold.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/bold_de_se.gif Binary file includes/clientside/tinymce/themes/advanced/images/bold_de_se.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/bold_es.gif Binary file includes/clientside/tinymce/themes/advanced/images/bold_es.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/bold_fr.gif Binary file includes/clientside/tinymce/themes/advanced/images/bold_fr.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/bold_ru.gif Binary file includes/clientside/tinymce/themes/advanced/images/bold_ru.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/bold_tw.gif Binary file includes/clientside/tinymce/themes/advanced/images/bold_tw.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/browse.gif Binary file includes/clientside/tinymce/themes/advanced/images/browse.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/bullist.gif Binary file includes/clientside/tinymce/themes/advanced/images/bullist.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/button_menu.gif Binary file includes/clientside/tinymce/themes/advanced/images/button_menu.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/buttons.gif Binary file includes/clientside/tinymce/themes/advanced/images/buttons.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/cancel_button_bg.gif Binary file includes/clientside/tinymce/themes/advanced/images/cancel_button_bg.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/charmap.gif Binary file includes/clientside/tinymce/themes/advanced/images/charmap.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/cleanup.gif Binary file includes/clientside/tinymce/themes/advanced/images/cleanup.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/close.gif Binary file includes/clientside/tinymce/themes/advanced/images/close.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/code.gif Binary file includes/clientside/tinymce/themes/advanced/images/code.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/color.gif Binary file includes/clientside/tinymce/themes/advanced/images/color.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/colors.jpg Binary file includes/clientside/tinymce/themes/advanced/images/colors.jpg has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/copy.gif Binary file includes/clientside/tinymce/themes/advanced/images/copy.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/custom_1.gif Binary file includes/clientside/tinymce/themes/advanced/images/custom_1.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/cut.gif Binary file includes/clientside/tinymce/themes/advanced/images/cut.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/forecolor.gif Binary file includes/clientside/tinymce/themes/advanced/images/forecolor.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/help.gif Binary file includes/clientside/tinymce/themes/advanced/images/help.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/hr.gif Binary file includes/clientside/tinymce/themes/advanced/images/hr.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/image.gif Binary file includes/clientside/tinymce/themes/advanced/images/image.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/indent.gif Binary file includes/clientside/tinymce/themes/advanced/images/indent.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/insert_button_bg.gif Binary file includes/clientside/tinymce/themes/advanced/images/insert_button_bg.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/italic.gif Binary file includes/clientside/tinymce/themes/advanced/images/italic.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/italic_de_se.gif Binary file includes/clientside/tinymce/themes/advanced/images/italic_de_se.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/italic_es.gif Binary file includes/clientside/tinymce/themes/advanced/images/italic_es.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/italic_ru.gif Binary file includes/clientside/tinymce/themes/advanced/images/italic_ru.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/italic_tw.gif Binary file includes/clientside/tinymce/themes/advanced/images/italic_tw.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/justifycenter.gif Binary file includes/clientside/tinymce/themes/advanced/images/justifycenter.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/justifyfull.gif Binary file includes/clientside/tinymce/themes/advanced/images/justifyfull.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/justifyleft.gif Binary file includes/clientside/tinymce/themes/advanced/images/justifyleft.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/justifyright.gif Binary file includes/clientside/tinymce/themes/advanced/images/justifyright.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/link.gif Binary file includes/clientside/tinymce/themes/advanced/images/link.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/menu_check.gif Binary file includes/clientside/tinymce/themes/advanced/images/menu_check.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/newdocument.gif Binary file includes/clientside/tinymce/themes/advanced/images/newdocument.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/numlist.gif Binary file includes/clientside/tinymce/themes/advanced/images/numlist.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/opacity.png Binary file includes/clientside/tinymce/themes/advanced/images/opacity.png has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/outdent.gif Binary file includes/clientside/tinymce/themes/advanced/images/outdent.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/paste.gif Binary file includes/clientside/tinymce/themes/advanced/images/paste.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/redo.gif Binary file includes/clientside/tinymce/themes/advanced/images/redo.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/removeformat.gif Binary file includes/clientside/tinymce/themes/advanced/images/removeformat.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/separator.gif Binary file includes/clientside/tinymce/themes/advanced/images/separator.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/spacer.gif Binary file includes/clientside/tinymce/themes/advanced/images/spacer.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/statusbar_resize.gif Binary file includes/clientside/tinymce/themes/advanced/images/statusbar_resize.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/strikethrough.gif Binary file includes/clientside/tinymce/themes/advanced/images/strikethrough.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/sub.gif Binary file includes/clientside/tinymce/themes/advanced/images/sub.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/sup.gif Binary file includes/clientside/tinymce/themes/advanced/images/sup.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/underline.gif Binary file includes/clientside/tinymce/themes/advanced/images/underline.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/underline_es.gif Binary file includes/clientside/tinymce/themes/advanced/images/underline_es.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/underline_fr.gif Binary file includes/clientside/tinymce/themes/advanced/images/underline_fr.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/underline_ru.gif Binary file includes/clientside/tinymce/themes/advanced/images/underline_ru.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/underline_tw.gif Binary file includes/clientside/tinymce/themes/advanced/images/underline_tw.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/undo.gif Binary file includes/clientside/tinymce/themes/advanced/images/undo.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/unlink.gif Binary file includes/clientside/tinymce/themes/advanced/images/unlink.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/visualaid.gif Binary file includes/clientside/tinymce/themes/advanced/images/visualaid.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/xp/tab_bg.gif Binary file includes/clientside/tinymce/themes/advanced/images/xp/tab_bg.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/xp/tab_end.gif Binary file includes/clientside/tinymce/themes/advanced/images/xp/tab_end.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/xp/tab_sel_bg.gif Binary file includes/clientside/tinymce/themes/advanced/images/xp/tab_sel_bg.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/xp/tab_sel_end.gif Binary file includes/clientside/tinymce/themes/advanced/images/xp/tab_sel_end.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/images/xp/tabs_bg.gif Binary file includes/clientside/tinymce/themes/advanced/images/xp/tabs_bg.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/jscripts/about.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/jscripts/about.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,75 @@ +function init() { + var inst; + + tinyMCEPopup.resizeToInnerSize(); + inst = tinyMCE.selectedInstance; + + // Give FF some time + window.setTimeout('insertHelpIFrame();', 10); + + var tcont = document.getElementById('plugintablecontainer'); + var plugins = tinyMCE.getParam('plugins', '', true, ','); + if (plugins.length == 0) + document.getElementById('plugins_tab').style.display = 'none'; + + var html = ""; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + + for (var i=0; i' + info.longname + ''; + else + html += ''; + + if (info.authorurl != null && info.authorurl != '') + html += ''; + else + html += ''; + + html += ''; + html += ''; + } + + html += ''; + html += '
' + tinyMCE.getLang('lang_plugin') + '' + tinyMCE.getLang('lang_author') + '' + tinyMCE.getLang('lang_version') + '
' + info.longname + '' + info.author + '' + info.author + '' + info.version + '
'; + + tcont.innerHTML = html; +} + +function getPluginInfo(name) { + if (tinyMCE.plugins[name].getInfo) + return tinyMCE.plugins[name].getInfo(); + + return { + longname : name, + authorurl : '', + infourl : '', + author : '--', + version : '--' + }; +} + +function insertHelpIFrame() { + var html = ''; + + document.getElementById('iframecontainer').innerHTML = html; + + html = ''; + html += 'Got Moxie? '; + html += 'Hosted By Sourceforge '; + html += 'Also on freshmeat '; + + document.getElementById('buttoncontainer').innerHTML = html; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/jscripts/anchor.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/jscripts/anchor.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,74 @@ +var action, element; + +function init() { + tinyMCEPopup.resizeToInnerSize(); + + var inst = tinyMCE.getInstanceById(tinyMCE.getWindowArg('editor_id')); + var anchor = tinyMCE.getParentElement(inst.getFocusElement(), "a", "name"); + var img = inst.getFocusElement(); + action = 'insert'; + + if (anchor != null) { + element = anchor; + action = "update"; + } + + if (tinyMCE.getAttrib(img, "class") == "mceItemAnchor") { + element = img; + action = "update"; + } + + if (action == "update") + document.forms[0].anchorName.value = element.nodeName == "IMG" ? element.getAttribute("title") : element.getAttribute("name"); + + document.forms[0].insert.value = tinyMCE.getLang('lang_' + action, 'Insert', true); +} + +function insertAnchor() { + var inst = tinyMCE.getInstanceById(tinyMCE.getWindowArg('editor_id')); + var name = document.forms[0].anchorName.value, e; + + tinyMCEPopup.execCommand("mceBeginUndoLevel"); + + if (action == "update") { + if (element.nodeName == "IMG") + element.setAttribute("title", name); + else + element.setAttribute("name", name); + } else { + var rng = inst.getRng(); + + if (rng.collapse) + rng.collapse(false); + + name = name.replace(/&/g, '&'); + name = name.replace(/\"/g, '"'); + name = name.replace(//g, '>'); + + // Fix for bug #1447335 + if (tinyMCE.isGecko) + html = ''; + else + html = ''; + + tinyMCEPopup.execCommand("mceInsertContent", false, html); + + // Fix for bug #1447335 force cursor after the anchor element + if (tinyMCE.isGecko) { + e = inst.getDoc().getElementById('mceNewAnchor'); + + if (e) { + inst.selection.selectNode(e, true, false, false); + e.removeAttribute('id'); + } + } + + tinyMCE.handleVisualAid(inst.getBody(), true, inst.visualAid, inst); + } + + tinyMCEPopup.execCommand("mceEndUndoLevel"); + + tinyMCE.triggerNodeChange(); + tinyMCEPopup.close(); +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/jscripts/charmap.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/jscripts/charmap.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,326 @@ +function init() { + tinyMCEPopup.resizeToInnerSize(); +} + +var charmap = new Array(); + +// for mor details please see w3c.org +// now here is the complete list ;) + +charmap = [ + [' ', ' ', true, 'no-break space'], + ['&', '&', true, 'ampersand'], + ['"', '"', true, 'quotation mark'], +// finance + ['¢', '¢', true, 'cent sign'], + ['€', '€', true, 'euro sign'], + ['£', '£', true, 'pound sign'], + ['¥', '¥', true, 'yen sign'], +// signs + ['©', '©', true, 'copyright sign'], + ['®', '®', true, 'registered sign'], + ['™', '™', true, 'trade mark sign'], + ['‰', '‰', true, 'per mille sign'], + ['µ', 'µ', true, 'micro sign'], + ['·', '·', true, 'middle dot'], + ['•', '•', true, 'bullet'], + ['…', '…', true, 'three dot leader'], + ['′', '′', true, 'minutes / feet'], + ['″', '″', true, 'seconds / inches'], + ['§', '§', true, 'section sign'], + ['¶', '¶', true, 'paragraph sign'], + ['ß', 'ß', true, 'sharp s / ess-zed'], +// quotations + ['‹', '‹', true, 'single left-pointing angle quotation mark'], + ['›', '›', true, 'single right-pointing angle quotation mark'], + ['«', '«', true, 'left pointing guillemet'], + ['»', '»', true, 'right pointing guillemet'], + ['‘', '‘', true, 'left single quotation mark'], + ['’', '’', true, 'right single quotation mark'], + ['“', '“', true, 'left double quotation mark'], + ['”', '”', true, 'right double quotation mark'], + ['‚', '‚', true, 'single low-9 quotation mark'], + ['„', '„', true, 'double low-9 quotation mark'], + ['<', '<', true, 'less-than sign'], + ['>', '>', true, 'greater-than sign'], + ['≤', '≤', true, 'less-than or equal to'], + ['≥', '≥', true, 'greater-than or equal to'], + ['–', '–', true, 'en dash'], + ['—', '—', true, 'em dash'], + ['¯', '¯', true, 'macron'], + ['‾', '‾', true, 'overline'], + ['¤', '¤', true, 'currency sign'], + ['¦', '¦', true, 'broken bar'], + ['¨', '¨', true, 'diaeresis'], + ['¡', '¡', true, 'inverted exclamation mark'], + ['¿', '¿', true, 'turned question mark'], + ['ˆ', 'ˆ', true, 'circumflex accent'], + ['˜', '˜', true, 'small tilde'], + ['°', '°', true, 'degree sign'], + ['−', '−', true, 'minus sign'], + ['±', '±', true, 'plus-minus sign'], + ['÷', '÷', true, 'division sign'], + ['⁄', '⁄', true, 'fraction slash'], + ['×', '×', true, 'multiplication sign'], + ['¹', '¹', true, 'superscript one'], + ['²', '²', true, 'superscript two'], + ['³', '³', true, 'superscript three'], + ['¼', '¼', true, 'fraction one quarter'], + ['½', '½', true, 'fraction one half'], + ['¾', '¾', true, 'fraction three quarters'], +// math / logical + ['ƒ', 'ƒ', true, 'function / florin'], + ['∫', '∫', true, 'integral'], + ['∑', '∑', true, 'n-ary sumation'], + ['∞', '∞', true, 'infinity'], + ['√', '√', true, 'square root'], + ['∼', '∼', false,'similar to'], + ['≅', '≅', false,'approximately equal to'], + ['≈', '≈', true, 'almost equal to'], + ['≠', '≠', true, 'not equal to'], + ['≡', '≡', true, 'identical to'], + ['∈', '∈', false,'element of'], + ['∉', '∉', false,'not an element of'], + ['∋', '∋', false,'contains as member'], + ['∏', '∏', true, 'n-ary product'], + ['∧', '∧', false,'logical and'], + ['∨', '∨', false,'logical or'], + ['¬', '¬', true, 'not sign'], + ['∩', '∩', true, 'intersection'], + ['∪', '∪', false,'union'], + ['∂', '∂', true, 'partial differential'], + ['∀', '∀', false,'for all'], + ['∃', '∃', false,'there exists'], + ['∅', '∅', false,'diameter'], + ['∇', '∇', false,'backward difference'], + ['∗', '∗', false,'asterisk operator'], + ['∝', '∝', false,'proportional to'], + ['∠', '∠', false,'angle'], +// undefined + ['´', '´', true, 'acute accent'], + ['¸', '¸', true, 'cedilla'], + ['ª', 'ª', true, 'feminine ordinal indicator'], + ['º', 'º', true, 'masculine ordinal indicator'], + ['†', '†', true, 'dagger'], + ['‡', '‡', true, 'double dagger'], +// alphabetical special chars + ['À', 'À', true, 'A - grave'], + ['Á', 'Á', true, 'A - acute'], + ['Â', 'Â', true, 'A - circumflex'], + ['Ã', 'Ã', true, 'A - tilde'], + ['Ä', 'Ä', true, 'A - diaeresis'], + ['Å', 'Å', true, 'A - ring above'], + ['Æ', 'Æ', true, 'ligature AE'], + ['Ç', 'Ç', true, 'C - cedilla'], + ['È', 'È', true, 'E - grave'], + ['É', 'É', true, 'E - acute'], + ['Ê', 'Ê', true, 'E - circumflex'], + ['Ë', 'Ë', true, 'E - diaeresis'], + ['Ì', 'Ì', true, 'I - grave'], + ['Í', 'Í', true, 'I - acute'], + ['Î', 'Î', true, 'I - circumflex'], + ['Ï', 'Ï', true, 'I - diaeresis'], + ['Ð', 'Ð', true, 'ETH'], + ['Ñ', 'Ñ', true, 'N - tilde'], + ['Ò', 'Ò', true, 'O - grave'], + ['Ó', 'Ó', true, 'O - acute'], + ['Ô', 'Ô', true, 'O - circumflex'], + ['Õ', 'Õ', true, 'O - tilde'], + ['Ö', 'Ö', true, 'O - diaeresis'], + ['Ø', 'Ø', true, 'O - slash'], + ['Œ', 'Œ', true, 'ligature OE'], + ['Š', 'Š', true, 'S - caron'], + ['Ù', 'Ù', true, 'U - grave'], + ['Ú', 'Ú', true, 'U - acute'], + ['Û', 'Û', true, 'U - circumflex'], + ['Ü', 'Ü', true, 'U - diaeresis'], + ['Ý', 'Ý', true, 'Y - acute'], + ['Ÿ', 'Ÿ', true, 'Y - diaeresis'], + ['Þ', 'Þ', true, 'THORN'], + ['à', 'à', true, 'a - grave'], + ['á', 'á', true, 'a - acute'], + ['â', 'â', true, 'a - circumflex'], + ['ã', 'ã', true, 'a - tilde'], + ['ä', 'ä', true, 'a - diaeresis'], + ['å', 'å', true, 'a - ring above'], + ['æ', 'æ', true, 'ligature ae'], + ['ç', 'ç', true, 'c - cedilla'], + ['è', 'è', true, 'e - grave'], + ['é', 'é', true, 'e - acute'], + ['ê', 'ê', true, 'e - circumflex'], + ['ë', 'ë', true, 'e - diaeresis'], + ['ì', 'ì', true, 'i - grave'], + ['í', 'í', true, 'i - acute'], + ['î', 'î', true, 'i - circumflex'], + ['ï', 'ï', true, 'i - diaeresis'], + ['ð', 'ð', true, 'eth'], + ['ñ', 'ñ', true, 'n - tilde'], + ['ò', 'ò', true, 'o - grave'], + ['ó', 'ó', true, 'o - acute'], + ['ô', 'ô', true, 'o - circumflex'], + ['õ', 'õ', true, 'o - tilde'], + ['ö', 'ö', true, 'o - diaeresis'], + ['ø', 'ø', true, 'o slash'], + ['œ', 'œ', true, 'ligature oe'], + ['š', 'š', true, 's - caron'], + ['ù', 'ù', true, 'u - grave'], + ['ú', 'ú', true, 'u - acute'], + ['û', 'û', true, 'u - circumflex'], + ['ü', 'ü', true, 'u - diaeresis'], + ['ý', 'ý', true, 'y - acute'], + ['þ', 'þ', true, 'thorn'], + ['ÿ', 'ÿ', true, 'y - diaeresis'], + ['Α', 'Α', true, 'Alpha'], + ['Β', 'Β', true, 'Beta'], + ['Γ', 'Γ', true, 'Gamma'], + ['Δ', 'Δ', true, 'Delta'], + ['Ε', 'Ε', true, 'Epsilon'], + ['Ζ', 'Ζ', true, 'Zeta'], + ['Η', 'Η', true, 'Eta'], + ['Θ', 'Θ', true, 'Theta'], + ['Ι', 'Ι', true, 'Iota'], + ['Κ', 'Κ', true, 'Kappa'], + ['Λ', 'Λ', true, 'Lambda'], + ['Μ', 'Μ', true, 'Mu'], + ['Ν', 'Ν', true, 'Nu'], + ['Ξ', 'Ξ', true, 'Xi'], + ['Ο', 'Ο', true, 'Omicron'], + ['Π', 'Π', true, 'Pi'], + ['Ρ', 'Ρ', true, 'Rho'], + ['Σ', 'Σ', true, 'Sigma'], + ['Τ', 'Τ', true, 'Tau'], + ['Υ', 'Υ', true, 'Upsilon'], + ['Φ', 'Φ', true, 'Phi'], + ['Χ', 'Χ', true, 'Chi'], + ['Ψ', 'Ψ', true, 'Psi'], + ['Ω', 'Ω', true, 'Omega'], + ['α', 'α', true, 'alpha'], + ['β', 'β', true, 'beta'], + ['γ', 'γ', true, 'gamma'], + ['δ', 'δ', true, 'delta'], + ['ε', 'ε', true, 'epsilon'], + ['ζ', 'ζ', true, 'zeta'], + ['η', 'η', true, 'eta'], + ['θ', 'θ', true, 'theta'], + ['ι', 'ι', true, 'iota'], + ['κ', 'κ', true, 'kappa'], + ['λ', 'λ', true, 'lambda'], + ['μ', 'μ', true, 'mu'], + ['ν', 'ν', true, 'nu'], + ['ξ', 'ξ', true, 'xi'], + ['ο', 'ο', true, 'omicron'], + ['π', 'π', true, 'pi'], + ['ρ', 'ρ', true, 'rho'], + ['ς', 'ς', true, 'final sigma'], + ['σ', 'σ', true, 'sigma'], + ['τ', 'τ', true, 'tau'], + ['υ', 'υ', true, 'upsilon'], + ['φ', 'φ', true, 'phi'], + ['χ', 'χ', true, 'chi'], + ['ψ', 'ψ', true, 'psi'], + ['ω', 'ω', true, 'omega'], +// symbols + ['ℵ', 'ℵ', false,'alef symbol'], + ['ϖ', 'ϖ', false,'pi symbol'], + ['ℜ', 'ℜ', false,'real part symbol'], + ['ϑ','ϑ', false,'theta symbol'], + ['ϒ', 'ϒ', false,'upsilon - hook symbol'], + ['℘', '℘', false,'Weierstrass p'], + ['ℑ', 'ℑ', false,'imaginary part'], +// arrows + ['←', '←', true, 'leftwards arrow'], + ['↑', '↑', true, 'upwards arrow'], + ['→', '→', true, 'rightwards arrow'], + ['↓', '↓', true, 'downwards arrow'], + ['↔', '↔', true, 'left right arrow'], + ['↵', '↵', false,'carriage return'], + ['⇐', '⇐', false,'leftwards double arrow'], + ['⇑', '⇑', false,'upwards double arrow'], + ['⇒', '⇒', false,'rightwards double arrow'], + ['⇓', '⇓', false,'downwards double arrow'], + ['⇔', '⇔', false,'left right double arrow'], + ['∴', '∴', false,'therefore'], + ['⊂', '⊂', false,'subset of'], + ['⊃', '⊃', false,'superset of'], + ['⊄', '⊄', false,'not a subset of'], + ['⊆', '⊆', false,'subset of or equal to'], + ['⊇', '⊇', false,'superset of or equal to'], + ['⊕', '⊕', false,'circled plus'], + ['⊗', '⊗', false,'circled times'], + ['⊥', '⊥', false,'perpendicular'], + ['⋅', '⋅', false,'dot operator'], + ['⌈', '⌈', false,'left ceiling'], + ['⌉', '⌉', false,'right ceiling'], + ['⌊', '⌊', false,'left floor'], + ['⌋', '⌋', false,'right floor'], + ['⟨', '〈', false,'left-pointing angle bracket'], + ['⟩', '〉', false,'right-pointing angle bracket'], + ['◊', '◊', true,'lozenge'], + ['♠', '♠', false,'black spade suit'], + ['♣', '♣', true, 'black club suit'], + ['♥', '♥', true, 'black heart suit'], + ['♦', '♦', true, 'black diamond suit'], + [' ', ' ', false,'en space'], + [' ', ' ', false,'em space'], + [' ', ' ', false,'thin space'], + ['‌', '‌', false,'zero width non-joiner'], + ['‍', '‍', false,'zero width joiner'], + ['‎', '‎', false,'left-to-right mark'], + ['‏', '‏', false,'right-to-left mark'], + ['­', '­', false,'soft hyphen'] +]; + +function renderCharMapHTML() { + var charsPerRow = 20, tdWidth=20, tdHeight=20; + var html = ''; + var cols=-1; + for (var i=0; i' + + charmap[i][1] + + ''; + if ((cols+1) % charsPerRow == 0) + html += ''; + } + } + if (cols % charsPerRow > 0) { + var padd = charsPerRow - (cols % charsPerRow); + for (var i=0; i '; + } + html += '
'; + document.write(html); +} + +function insertChar(chr) { + tinyMCEPopup.execCommand('mceInsertContent', false, '&#' + chr + ';'); + + // Refocus in window + if (tinyMCEPopup.isWindow) + window.focus(); + + tinyMCEPopup.close(); +} + +function previewChar(codeA, codeB, codeN) { + var elmA = document.getElementById('codeA'); + var elmB = document.getElementById('codeB'); + var elmV = document.getElementById('codeV'); + var elmN = document.getElementById('codeN'); + + if (codeA=='#160;') { + elmV.innerHTML = '__'; + } else { + elmV.innerHTML = '&' + codeA; + } + + elmB.innerHTML = '&' + codeA; + elmA.innerHTML = '&' + codeB; + elmN.innerHTML = codeN; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/jscripts/color_picker.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/jscripts/color_picker.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,249 @@ +var detail = 50, strhex = "0123456789abcdef", i, isMouseDown = false, isMouseOver = false; + +var colors = new Array( + "#000000","#000033","#000066","#000099","#0000cc","#0000ff","#330000","#330033", + "#330066","#330099","#3300cc","#3300ff","#660000","#660033","#660066","#660099", + "#6600cc","#6600ff","#990000","#990033","#990066","#990099","#9900cc","#9900ff", + "#cc0000","#cc0033","#cc0066","#cc0099","#cc00cc","#cc00ff","#ff0000","#ff0033", + "#ff0066","#ff0099","#ff00cc","#ff00ff","#003300","#003333","#003366","#003399", + "#0033cc","#0033ff","#333300","#333333","#333366","#333399","#3333cc","#3333ff", + "#663300","#663333","#663366","#663399","#6633cc","#6633ff","#993300","#993333", + "#993366","#993399","#9933cc","#9933ff","#cc3300","#cc3333","#cc3366","#cc3399", + "#cc33cc","#cc33ff","#ff3300","#ff3333","#ff3366","#ff3399","#ff33cc","#ff33ff", + "#006600","#006633","#006666","#006699","#0066cc","#0066ff","#336600","#336633", + "#336666","#336699","#3366cc","#3366ff","#666600","#666633","#666666","#666699", + "#6666cc","#6666ff","#996600","#996633","#996666","#996699","#9966cc","#9966ff", + "#cc6600","#cc6633","#cc6666","#cc6699","#cc66cc","#cc66ff","#ff6600","#ff6633", + "#ff6666","#ff6699","#ff66cc","#ff66ff","#009900","#009933","#009966","#009999", + "#0099cc","#0099ff","#339900","#339933","#339966","#339999","#3399cc","#3399ff", + "#669900","#669933","#669966","#669999","#6699cc","#6699ff","#999900","#999933", + "#999966","#999999","#9999cc","#9999ff","#cc9900","#cc9933","#cc9966","#cc9999", + "#cc99cc","#cc99ff","#ff9900","#ff9933","#ff9966","#ff9999","#ff99cc","#ff99ff", + "#00cc00","#00cc33","#00cc66","#00cc99","#00cccc","#00ccff","#33cc00","#33cc33", + "#33cc66","#33cc99","#33cccc","#33ccff","#66cc00","#66cc33","#66cc66","#66cc99", + "#66cccc","#66ccff","#99cc00","#99cc33","#99cc66","#99cc99","#99cccc","#99ccff", + "#cccc00","#cccc33","#cccc66","#cccc99","#cccccc","#ccccff","#ffcc00","#ffcc33", + "#ffcc66","#ffcc99","#ffcccc","#ffccff","#00ff00","#00ff33","#00ff66","#00ff99", + "#00ffcc","#00ffff","#33ff00","#33ff33","#33ff66","#33ff99","#33ffcc","#33ffff", + "#66ff00","#66ff33","#66ff66","#66ff99","#66ffcc","#66ffff","#99ff00","#99ff33", + "#99ff66","#99ff99","#99ffcc","#99ffff","#ccff00","#ccff33","#ccff66","#ccff99", + "#ccffcc","#ccffff","#ffff00","#ffff33","#ffff66","#ffff99","#ffffcc","#ffffff" +); + +var named = { + '#F0F8FF':'AliceBlue','#FAEBD7':'AntiqueWhite','#00FFFF':'Aqua','#7FFFD4':'Aquamarine','#F0FFFF':'Azure','#F5F5DC':'Beige', + '#FFE4C4':'Bisque','#000000':'Black','#FFEBCD':'BlanchedAlmond','#0000FF':'Blue','#8A2BE2':'BlueViolet','#A52A2A':'Brown', + '#DEB887':'BurlyWood','#5F9EA0':'CadetBlue','#7FFF00':'Chartreuse','#D2691E':'Chocolate','#FF7F50':'Coral','#6495ED':'CornflowerBlue', + '#FFF8DC':'Cornsilk','#DC143C':'Crimson','#00FFFF':'Cyan','#00008B':'DarkBlue','#008B8B':'DarkCyan','#B8860B':'DarkGoldenRod', + '#A9A9A9':'DarkGray','#A9A9A9':'DarkGrey','#006400':'DarkGreen','#BDB76B':'DarkKhaki','#8B008B':'DarkMagenta','#556B2F':'DarkOliveGreen', + '#FF8C00':'Darkorange','#9932CC':'DarkOrchid','#8B0000':'DarkRed','#E9967A':'DarkSalmon','#8FBC8F':'DarkSeaGreen','#483D8B':'DarkSlateBlue', + '#2F4F4F':'DarkSlateGray','#2F4F4F':'DarkSlateGrey','#00CED1':'DarkTurquoise','#9400D3':'DarkViolet','#FF1493':'DeepPink','#00BFFF':'DeepSkyBlue', + '#696969':'DimGray','#696969':'DimGrey','#1E90FF':'DodgerBlue','#B22222':'FireBrick','#FFFAF0':'FloralWhite','#228B22':'ForestGreen', + '#FF00FF':'Fuchsia','#DCDCDC':'Gainsboro','#F8F8FF':'GhostWhite','#FFD700':'Gold','#DAA520':'GoldenRod','#808080':'Gray','#808080':'Grey', + '#008000':'Green','#ADFF2F':'GreenYellow','#F0FFF0':'HoneyDew','#FF69B4':'HotPink','#CD5C5C':'IndianRed','#4B0082':'Indigo','#FFFFF0':'Ivory', + '#F0E68C':'Khaki','#E6E6FA':'Lavender','#FFF0F5':'LavenderBlush','#7CFC00':'LawnGreen','#FFFACD':'LemonChiffon','#ADD8E6':'LightBlue', + '#F08080':'LightCoral','#E0FFFF':'LightCyan','#FAFAD2':'LightGoldenRodYellow','#D3D3D3':'LightGray','#D3D3D3':'LightGrey','#90EE90':'LightGreen', + '#FFB6C1':'LightPink','#FFA07A':'LightSalmon','#20B2AA':'LightSeaGreen','#87CEFA':'LightSkyBlue','#778899':'LightSlateGray','#778899':'LightSlateGrey', + '#B0C4DE':'LightSteelBlue','#FFFFE0':'LightYellow','#00FF00':'Lime','#32CD32':'LimeGreen','#FAF0E6':'Linen','#FF00FF':'Magenta','#800000':'Maroon', + '#66CDAA':'MediumAquaMarine','#0000CD':'MediumBlue','#BA55D3':'MediumOrchid','#9370D8':'MediumPurple','#3CB371':'MediumSeaGreen','#7B68EE':'MediumSlateBlue', + '#00FA9A':'MediumSpringGreen','#48D1CC':'MediumTurquoise','#C71585':'MediumVioletRed','#191970':'MidnightBlue','#F5FFFA':'MintCream','#FFE4E1':'MistyRose','#FFE4B5':'Moccasin', + '#FFDEAD':'NavajoWhite','#000080':'Navy','#FDF5E6':'OldLace','#808000':'Olive','#6B8E23':'OliveDrab','#FFA500':'Orange','#FF4500':'OrangeRed','#DA70D6':'Orchid', + '#EEE8AA':'PaleGoldenRod','#98FB98':'PaleGreen','#AFEEEE':'PaleTurquoise','#D87093':'PaleVioletRed','#FFEFD5':'PapayaWhip','#FFDAB9':'PeachPuff', + '#CD853F':'Peru','#FFC0CB':'Pink','#DDA0DD':'Plum','#B0E0E6':'PowderBlue','#800080':'Purple','#FF0000':'Red','#BC8F8F':'RosyBrown','#4169E1':'RoyalBlue', + '#8B4513':'SaddleBrown','#FA8072':'Salmon','#F4A460':'SandyBrown','#2E8B57':'SeaGreen','#FFF5EE':'SeaShell','#A0522D':'Sienna','#C0C0C0':'Silver', + '#87CEEB':'SkyBlue','#6A5ACD':'SlateBlue','#708090':'SlateGray','#708090':'SlateGrey','#FFFAFA':'Snow','#00FF7F':'SpringGreen', + '#4682B4':'SteelBlue','#D2B48C':'Tan','#008080':'Teal','#D8BFD8':'Thistle','#FF6347':'Tomato','#40E0D0':'Turquoise','#EE82EE':'Violet', + '#F5DEB3':'Wheat','#FFFFFF':'White','#F5F5F5':'WhiteSmoke','#FFFF00':'Yellow','#9ACD32':'YellowGreen' +}; + +function init() { + var inputColor = convertRGBToHex(tinyMCE.getWindowArg('input_color')); + + if (tinyMCE.isMSIE) + tinyMCEPopup.resizeToInnerSize(); + + generatePicker(); + + if (inputColor) { + changeFinalColor(inputColor); + + col = convertHexToRGB(inputColor); + + if (col) + updateLight(col.r, col.g, col.b); + } +} + +function insertAction() { + var color = document.getElementById("color").value; + + tinyMCEPopup.execCommand(tinyMCE.getWindowArg('command'), false, color); + tinyMCEPopup.close(); +} + +function showColor(color, name) { + if (name) + document.getElementById("colorname").innerHTML = name; + + document.getElementById("preview").style.backgroundColor = color; + document.getElementById("color").value = color; +} + +function convertRGBToHex(col) { + var re = new RegExp("rgb\\s*\\(\\s*([0-9]+).*,\\s*([0-9]+).*,\\s*([0-9]+).*\\)", "gi"); + + if (!col) + return col; + + var rgb = col.replace(re, "$1,$2,$3").split(','); + if (rgb.length == 3) { + r = parseInt(rgb[0]).toString(16); + g = parseInt(rgb[1]).toString(16); + b = parseInt(rgb[2]).toString(16); + + r = r.length == 1 ? '0' + r : r; + g = g.length == 1 ? '0' + g : g; + b = b.length == 1 ? '0' + b : b; + + return "#" + r + g + b; + } + + return col; +} + +function convertHexToRGB(col) { + if (col.indexOf('#') != -1) { + col = col.replace(new RegExp('[^0-9A-F]', 'gi'), ''); + + r = parseInt(col.substring(0, 2), 16); + g = parseInt(col.substring(2, 4), 16); + b = parseInt(col.substring(4, 6), 16); + + return {r : r, g : g, b : b}; + } + + return null; +} + +function generatePicker() { + var el = document.getElementById('light'), h = '', i; + + for (i = 0; i < detail; i++){ + h += '
'; + } + + el.innerHTML = h; +} + +function generateWebColors() { + var el = document.getElementById('webcolors'), h = '', i; + + if (el.className == 'generated') + return; + + h += '' + + ''; + + for (i=0; i' + + '' + + '' + colors[i] +  ''; + if ((i+1) % 18 == 0) + h += ''; + } + + h += '
'; + + el.innerHTML = h; + el.className = 'generated'; +} + +function generateNamedColors() { + var el = document.getElementById('namedcolors'), h = '', n, v, i = 0; + + if (el.className == 'generated') + return; + + for (n in named) { + v = named[n]; + h += '' + } + + el.innerHTML = h; + el.className = 'generated'; +} + +function selectColor() { + var color = document.getElementById("color").value; + + if(window.opener) + window.opener.tinyMCE.execInstanceCommand(tinyMCE.getWindowArg('editor_id'),tinyMCE.getWindowArg('command'),false,color); + + window.close(); +} + +function dechex(n) { + return strhex.charAt(Math.floor(n / 16)) + strhex.charAt(n % 16); +} + +function computeColor(e) { + var x, y, partWidth, partDetail, imHeight, r, g, b, coef, i, finalCoef, finalR, finalG, finalB; + + x = e.offsetX ? e.offsetX : (e.target ? e.clientX - e.target.x : 0); + y = e.offsetY ? e.offsetY : (e.target ? e.clientY - e.target.y : 0); + + partWidth = document.getElementById('colorpicker').width / 6; + partDetail = detail / 2; + imHeight = document.getElementById('colorpicker').height; + + r = (x >= 0)*(x < partWidth)*255 + (x >= partWidth)*(x < 2*partWidth)*(2*255 - x * 255 / partWidth) + (x >= 4*partWidth)*(x < 5*partWidth)*(-4*255 + x * 255 / partWidth) + (x >= 5*partWidth)*(x < 6*partWidth)*255; + g = (x >= 0)*(x < partWidth)*(x * 255 / partWidth) + (x >= partWidth)*(x < 3*partWidth)*255 + (x >= 3*partWidth)*(x < 4*partWidth)*(4*255 - x * 255 / partWidth); + b = (x >= 2*partWidth)*(x < 3*partWidth)*(-2*255 + x * 255 / partWidth) + (x >= 3*partWidth)*(x < 5*partWidth)*255 + (x >= 5*partWidth)*(x < 6*partWidth)*(6*255 - x * 255 / partWidth); + + coef = (imHeight - y) / imHeight; + r = 128 + (r - 128) * coef; + g = 128 + (g - 128) * coef; + b = 128 + (b - 128) * coef; + + changeFinalColor('#' + dechex(r) + dechex(g) + dechex(b)); + updateLight(r, g, b); +} + +function updateLight(r, g, b) { + var i, partDetail = detail / 2, finalCoef, finalR, finalG, finalB, color; + + for (i=0; i=0) && (i'); +} + +function insertImage() { + var src = document.forms[0].src.value; + var alt = document.forms[0].alt.value; + var border = document.forms[0].border.value; + var vspace = document.forms[0].vspace.value; + var hspace = document.forms[0].hspace.value; + var width = document.forms[0].width.value; + var height = document.forms[0].height.value; + var align = document.forms[0].align.options[document.forms[0].align.selectedIndex].value; + + tinyMCEPopup.restoreSelection(); + tinyMCE.themes['advanced']._insertImage(src, alt, border, hspace, vspace, width, height, align); + tinyMCEPopup.close(); +} + +function init() { + tinyMCEPopup.resizeToInnerSize(); + + document.getElementById('srcbrowsercontainer').innerHTML = getBrowserHTML('srcbrowser','src','image','theme_advanced_image'); + + var formObj = document.forms[0]; + + for (var i=0; i 0) { + for (var i=0; i'); +} + +function init() { + tinyMCEPopup.resizeToInnerSize(); + + document.getElementById('hrefbrowsercontainer').innerHTML = getBrowserHTML('hrefbrowser','href','file','theme_advanced_link'); + + // Handle file browser + if (isVisible('hrefbrowser')) + document.getElementById('href').style.width = '180px'; + + var formObj = document.forms[0]; + + for (var i=0; i 0) { + var formObj = document.forms[0]; + + for (var i=0; i + + {$lang_insert_link_title} + + + + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + +
 
+
+
+
+ +
+
+ +
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/advanced/source_editor.htm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/advanced/source_editor.htm Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,32 @@ + + + + {$lang_theme_code_title} + + + + + +
+
{$lang_theme_code_title}
+ +
+ +
+ +
+ + + +
+
+ +
+ +
+ +
+
+
+ + diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/css/editor_content.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/simple/css/editor_content.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,25 @@ +body, td, pre { + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 10px; +} + +body { + background-color: #FFFFFF; +} + +.mceVisualAid { + border: 1px dashed #BBBBBB; +} + +/* MSIE specific */ + +* html body { + scrollbar-3dlight-color: #F0F0EE; + scrollbar-arrow-color: #676662; + scrollbar-base-color: #F0F0EE; + scrollbar-darkshadow-color: #DDDDDD; + scrollbar-face-color: #E0E0DD; + scrollbar-highlight-color: #F0F0EE; + scrollbar-shadow-color: #F0F0EE; + scrollbar-track-color: #F5F5F5; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/css/editor_popup.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/simple/css/editor_popup.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,41 @@ +body { + background-color: #F0F0EE; + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 11px; + scrollbar-3dlight-color: #F0F0EE; + scrollbar-arrow-color: #676662; + scrollbar-base-color: #F0F0EE; + scrollbar-darkshadow-color: #DDDDDD; + scrollbar-face-color: #E0E0DD; + scrollbar-highlight-color: #F0F0EE; + scrollbar-shadow-color: #F0F0EE; + scrollbar-track-color: #F5F5F5; +} + +td { + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 11px; +} + +input { + background: #FFFFFF; + border: 1px solid #cccccc; +} + +td, input, select, textarea { + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 10px; +} + +input, select, textarea { + border: 1px solid #808080; +} + +.input_noborder { + border: 0; +} + +.title { + font-size: 12px; + font-weight: bold; +} \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/css/editor_ui.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/simple/css/editor_ui.css Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,125 @@ +.mceSeparatorLine { + border: 0; + padding: 0; + margin-left: 4px; + margin-right: 2px; +} + +.mceSelectList { + font-family: "MS Sans Serif"; + font-size: 7pt; + font-weight: normal; + margin-top: 2px; +} + +.mceLabel, .mceLabelDisabled { + font-family: "MS Sans Serif"; + font-size: 9pt; +} + +.mceLabel { + color: #000000; +} + +.mceLabelDisabled { + cursor: text; + color: #999999; +} + +.mceEditor { + background: #F0F0EE; + border: 1px solid #cccccc; +} + +.mceEditorArea { + font-family: "MS Sans Serif"; + background: #FFFFFF; +} + +.mceToolbar { + background: #F0F0EE; + border-top: 1px solid #cccccc; + line-height: 1px; + font-size: 1px; + padding-bottom: 1px; +} + +.mceEditorIframe { + border: 0; +} + +/* Button CSS rules */ + +a.mceButtonDisabled img, a.mceButtonNormal img, a.mceButtonSelected img { + width: 20px; + height: 20px; + cursor: default; + margin-top: 1px; + margin-left: 1px; +} + +a.mceButtonDisabled img { + border: 0 !important; +} + +a.mceButtonNormal img, a.mceButtonSelected img { + border: 1px solid #F0F0EE !important; +} + +a.mceButtonSelected img { + border: 1px solid #C0C0BB !important; +} + +a.mceButtonNormal img:hover, a.mceButtonSelected img:hover { + border: 1px solid #0A246A !important; + cursor: default; + background-color: #B6BDD2; +} + +a.mceButtonDisabled img { + -moz-opacity:0.3; + opacity: 0.3; + border: 1px solid #F0F0EE !important; + cursor: default; +} + +a.mceTiledButton img { + background-image: url('../images/buttons.gif'); + background-repeat: no-repeat; +} + +/* MSIE specific rules */ + +* html a.mceButtonNormal img, * html a.mceButtonSelected img, * html a.mceButtonDisabled img { + border: 0px !important; + margin-top: 2px; + margin-bottom: 1px; +} + +* html a.mceButtonDisabled img { + filter:progid:DXImageTransform.Microsoft.Alpha(opacity=30); + border: 0px !important; +} + +* html a.mceButtonDisabled { + border: 1px solid #F0F0EE !important; +} + +* html a.mceButtonNormal, * html a.mceButtonSelected { + border: 1px solid #F0F0EE; + cursor: default; +} + +* html a.mceButtonSelected { + border: 1px solid #C0C0BB; +} + +* html a.mceButtonNormal:hover, * html a.mceButtonSelected:hover { + border: 1px solid #0A246A; + cursor: default; + background-color: #B6BDD2; +} + +* html .mceSelectList { + margin-top: 2px; +} diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/editor_template.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/simple/editor_template.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +var TinyMCE_SimpleTheme={_buttonMap:'bold,bullist,cleanup,italic,numlist,redo,strikethrough,underline,undo',getEditorTemplate:function(){var html='';html+='';html+='';html+='
';html+='IFRAME';html+='
';html+=tinyMCE.getButtonHTML('bold','lang_bold_desc','{$themeurl}/images/{$lang_bold_img}','Bold');html+=tinyMCE.getButtonHTML('italic','lang_italic_desc','{$themeurl}/images/{$lang_italic_img}','Italic');html+=tinyMCE.getButtonHTML('underline','lang_underline_desc','{$themeurl}/images/{$lang_underline_img}','Underline');html+=tinyMCE.getButtonHTML('strikethrough','lang_striketrough_desc','{$themeurl}/images/strikethrough.gif','Strikethrough');html+='';html+=tinyMCE.getButtonHTML('undo','lang_undo_desc','{$themeurl}/images/undo.gif','Undo');html+=tinyMCE.getButtonHTML('redo','lang_redo_desc','{$themeurl}/images/redo.gif','Redo');html+='';html+=tinyMCE.getButtonHTML('cleanup','lang_cleanup_desc','{$themeurl}/images/cleanup.gif','mceCleanup');html+='';html+=tinyMCE.getButtonHTML('bullist','lang_bullist_desc','{$themeurl}/images/bullist.gif','InsertUnorderedList');html+=tinyMCE.getButtonHTML('numlist','lang_numlist_desc','{$themeurl}/images/numlist.gif','InsertOrderedList');html+='
';return{delta_width:0,delta_height:20,html:html}},handleNodeChange:function(editor_id,node){tinyMCE.switchClass(editor_id+'_bold','mceButtonNormal');tinyMCE.switchClass(editor_id+'_italic','mceButtonNormal');tinyMCE.switchClass(editor_id+'_underline','mceButtonNormal');tinyMCE.switchClass(editor_id+'_strikethrough','mceButtonNormal');tinyMCE.switchClass(editor_id+'_bullist','mceButtonNormal');tinyMCE.switchClass(editor_id+'_numlist','mceButtonNormal');do{switch(node.nodeName.toLowerCase()){case"b":case"strong":tinyMCE.switchClass(editor_id+'_bold','mceButtonSelected');break;case"i":case"em":tinyMCE.switchClass(editor_id+'_italic','mceButtonSelected');break;case"u":tinyMCE.switchClass(editor_id+'_underline','mceButtonSelected');break;case"strike":tinyMCE.switchClass(editor_id+'_strikethrough','mceButtonSelected');break;case"ul":tinyMCE.switchClass(editor_id+'_bullist','mceButtonSelected');break;case"ol":tinyMCE.switchClass(editor_id+'_numlist','mceButtonSelected');break}}while((node=node.parentNode)!=null)}};tinyMCE.addTheme("simple",TinyMCE_SimpleTheme);tinyMCE.addButtonMap(TinyMCE_SimpleTheme._buttonMap); \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/editor_template_src.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/themes/simple/editor_template_src.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,84 @@ +/** + * $Id: editor_template_src.js 162 2007-01-03 16:16:52Z spocke $ + * + * @author Moxiecode + * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved. + */ + +var TinyMCE_SimpleTheme = { + // List of button ids in tile map + _buttonMap : 'bold,bullist,cleanup,italic,numlist,redo,strikethrough,underline,undo', + + getEditorTemplate : function() { + var html = ''; + + html += ''; + html += ''; + html += '
'; + html += 'IFRAME'; + html += '
'; + html += tinyMCE.getButtonHTML('bold', 'lang_bold_desc', '{$themeurl}/images/{$lang_bold_img}', 'Bold'); + html += tinyMCE.getButtonHTML('italic', 'lang_italic_desc', '{$themeurl}/images/{$lang_italic_img}', 'Italic'); + html += tinyMCE.getButtonHTML('underline', 'lang_underline_desc', '{$themeurl}/images/{$lang_underline_img}', 'Underline'); + html += tinyMCE.getButtonHTML('strikethrough', 'lang_striketrough_desc', '{$themeurl}/images/strikethrough.gif', 'Strikethrough'); + html += ''; + html += tinyMCE.getButtonHTML('undo', 'lang_undo_desc', '{$themeurl}/images/undo.gif', 'Undo'); + html += tinyMCE.getButtonHTML('redo', 'lang_redo_desc', '{$themeurl}/images/redo.gif', 'Redo'); + html += ''; + html += tinyMCE.getButtonHTML('cleanup', 'lang_cleanup_desc', '{$themeurl}/images/cleanup.gif', 'mceCleanup'); + html += ''; + html += tinyMCE.getButtonHTML('bullist', 'lang_bullist_desc', '{$themeurl}/images/bullist.gif', 'InsertUnorderedList'); + html += tinyMCE.getButtonHTML('numlist', 'lang_numlist_desc', '{$themeurl}/images/numlist.gif', 'InsertOrderedList'); + html += '
'; + + return { + delta_width : 0, + delta_height : 20, + html : html + }; + }, + + handleNodeChange : function(editor_id, node) { + // Reset old states + tinyMCE.switchClass(editor_id + '_bold', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_italic', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_underline', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_strikethrough', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_bullist', 'mceButtonNormal'); + tinyMCE.switchClass(editor_id + '_numlist', 'mceButtonNormal'); + + // Handle elements + do { + switch (node.nodeName.toLowerCase()) { + case "b": + case "strong": + tinyMCE.switchClass(editor_id + '_bold', 'mceButtonSelected'); + break; + + case "i": + case "em": + tinyMCE.switchClass(editor_id + '_italic', 'mceButtonSelected'); + break; + + case "u": + tinyMCE.switchClass(editor_id + '_underline', 'mceButtonSelected'); + break; + + case "strike": + tinyMCE.switchClass(editor_id + '_strikethrough', 'mceButtonSelected'); + break; + + case "ul": + tinyMCE.switchClass(editor_id + '_bullist', 'mceButtonSelected'); + break; + + case "ol": + tinyMCE.switchClass(editor_id + '_numlist', 'mceButtonSelected'); + break; + } + } while ((node = node.parentNode) != null); + } +}; + +tinyMCE.addTheme("simple", TinyMCE_SimpleTheme); +tinyMCE.addButtonMap(TinyMCE_SimpleTheme._buttonMap); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/bold.gif Binary file includes/clientside/tinymce/themes/simple/images/bold.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/bold_de_se.gif Binary file includes/clientside/tinymce/themes/simple/images/bold_de_se.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/bold_fr.gif Binary file includes/clientside/tinymce/themes/simple/images/bold_fr.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/bold_ru.gif Binary file includes/clientside/tinymce/themes/simple/images/bold_ru.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/bold_tw.gif Binary file includes/clientside/tinymce/themes/simple/images/bold_tw.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/bullist.gif Binary file includes/clientside/tinymce/themes/simple/images/bullist.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/buttons.gif Binary file includes/clientside/tinymce/themes/simple/images/buttons.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/cleanup.gif Binary file includes/clientside/tinymce/themes/simple/images/cleanup.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/italic.gif Binary file includes/clientside/tinymce/themes/simple/images/italic.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/italic_de_se.gif Binary file includes/clientside/tinymce/themes/simple/images/italic_de_se.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/italic_ru.gif Binary file includes/clientside/tinymce/themes/simple/images/italic_ru.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/italic_tw.gif Binary file includes/clientside/tinymce/themes/simple/images/italic_tw.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/numlist.gif Binary file includes/clientside/tinymce/themes/simple/images/numlist.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/redo.gif Binary file includes/clientside/tinymce/themes/simple/images/redo.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/separator.gif Binary file includes/clientside/tinymce/themes/simple/images/separator.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/spacer.gif Binary file includes/clientside/tinymce/themes/simple/images/spacer.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/strikethrough.gif Binary file includes/clientside/tinymce/themes/simple/images/strikethrough.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/underline.gif Binary file includes/clientside/tinymce/themes/simple/images/underline.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/underline_fr.gif Binary file includes/clientside/tinymce/themes/simple/images/underline_fr.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/underline_ru.gif Binary file includes/clientside/tinymce/themes/simple/images/underline_ru.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/underline_tw.gif Binary file includes/clientside/tinymce/themes/simple/images/underline_tw.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/themes/simple/images/undo.gif Binary file includes/clientside/tinymce/themes/simple/images/undo.gif has changed diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/tiny_mce.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/tiny_mce.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1 @@ +function TinyMCE_Engine(){var ua;this.majorVersion="2";this.minorVersion="1.0";this.releaseDate="2007-02-13";this.instances=new Array();this.switchClassCache=new Array();this.windowArgs=new Array();this.loadedFiles=new Array();this.pendingFiles=new Array();this.loadingIndex=0;this.configs=new Array();this.currentConfig=0;this.eventHandlers=new Array();this.log=new Array();this.undoLevels=[];this.undoIndex=0;this.typingUndoIndex=-1;ua=navigator.userAgent;this.isMSIE=(navigator.appName=="Microsoft Internet Explorer");this.isMSIE5=this.isMSIE&&(ua.indexOf('MSIE 5')!=-1);this.isMSIE5_0=this.isMSIE&&(ua.indexOf('MSIE 5.0')!=-1);this.isMSIE7=this.isMSIE&&(ua.indexOf('MSIE 7')!=-1);this.isGecko=ua.indexOf('Gecko')!=-1;this.isSafari=ua.indexOf('Safari')!=-1;this.isOpera=ua.indexOf('Opera')!=-1;this.isMac=ua.indexOf('Mac')!=-1;this.isNS7=ua.indexOf('Netscape/7')!=-1;this.isNS71=ua.indexOf('Netscape/7.1')!=-1;this.dialogCounter=0;this.plugins=new Array();this.themes=new Array();this.menus=new Array();this.loadedPlugins=new Array();this.buttonMap=new Array();this.isLoaded=false;if(this.isOpera){this.isMSIE=true;this.isGecko=false;this.isSafari=false}this.isIE=this.isMSIE;this.isRealIE=this.isMSIE&&!this.isOpera;this.idCounter=0};TinyMCE_Engine.prototype={init:function(settings){var theme,nl,baseHREF="",i;if(this.isMSIE5_0)return;this.settings=settings;if(typeof(document.execCommand)=='undefined')return;if(!tinyMCE.baseURL){var elements=document.getElementsByTagName('script');nl=document.getElementsByTagName('base');for(i=0;i');this._def("font_size_classes",'');this._def("font_size_style_values",'xx-small,x-small,small,medium,large,x-large,xx-large',true);this._def("event_elements",'a,img',true);this._def("convert_urls",true);this._def("table_inline_editing",false);this._def("object_resizing",true);this._def("custom_shortcuts",true);this._def("convert_on_click",false);this._def("content_css",'');this._def("fix_list_elements",true);this._def("fix_table_elements",false);this._def("strict_loading_mode",document.contentType=='application/xhtml+xml');this._def("hidden_tab_class",'');this._def("display_tab_class",'');this._def("gecko_spellcheck",false);this._def("hide_selects_on_submit",true);if(this.isMSIE&&!this.isOpera)this.settings.strict_loading_mode=false;if(this.isMSIE&&this.settings['browsers'].indexOf('msie')==-1)return;if(this.isGecko&&this.settings['browsers'].indexOf('gecko')==-1)return;if(this.isSafari&&this.settings['browsers'].indexOf('safari')==-1)return;if(this.isOpera&&this.settings['browsers'].indexOf('opera')==-1)return;baseHREF=tinyMCE.settings['document_base_url'];var h=document.location.href;var p=h.indexOf('://');if(p>0&&document.location.protocol!="file:"){p=h.indexOf('/',p+3);h=h.substring(0,p);if(baseHREF.indexOf('://')==-1)baseHREF=h+baseHREF;tinyMCE.settings['document_base_url']=baseHREF;tinyMCE.settings['document_base_prefix']=h}if(baseHREF.indexOf('?')!=-1)baseHREF=baseHREF.substring(0,baseHREF.indexOf('?'));this.settings['base_href']=baseHREF.substring(0,baseHREF.lastIndexOf('/'))+"/";theme=this.settings['theme'];this.inlineStrict='A|BR|SPAN|BDO|MAP|OBJECT|IMG|TT|I|B|BIG|SMALL|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|#text|#comment';this.inlineTransitional='A|BR|SPAN|BDO|OBJECT|APPLET|IMG|MAP|IFRAME|TT|I|B|U|S|STRIKE|BIG|SMALL|FONT|BASEFONT|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|INPUT|SELECT|TEXTAREA|LABEL|BUTTON|#text|#comment';this.blockElms='H[1-6]|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|FORM|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP';this.blockRegExp=new RegExp("^("+this.blockElms+")$","i");this.posKeyCodes=new Array(13,45,36,35,33,34,37,38,39,40);this.uniqueURL='javascript:void(091039730);';this.uniqueTag='';this.callbacks=new Array('onInit','getInfo','getEditorTemplate','setupContent','onChange','onPageLoad','handleNodeChange','initInstance','execCommand','getControlHTML','handleEvent','cleanup','removeInstance');this.settings['theme_href']=tinyMCE.baseURL+"/themes/"+theme;if(!tinyMCE.isIE||tinyMCE.isOpera)this.settings['force_br_newlines']=false;if(tinyMCE.getParam("popups_css",false)){var cssPath=tinyMCE.getParam("popups_css","");if(cssPath.indexOf('://')==-1&&cssPath.charAt(0)!='/')this.settings['popups_css']=this.documentBasePath+"/"+cssPath;else this.settings['popups_css']=cssPath}else this.settings['popups_css']=tinyMCE.baseURL+"/themes/"+theme+"/css/editor_popup.css";if(tinyMCE.getParam("editor_css",false)){var cssPath=tinyMCE.getParam("editor_css","");if(cssPath.indexOf('://')==-1&&cssPath.charAt(0)!='/')this.settings['editor_css']=this.documentBasePath+"/"+cssPath;else this.settings['editor_css']=cssPath}else{if(this.settings.editor_css!='')this.settings['editor_css']=tinyMCE.baseURL+"/themes/"+theme+"/css/editor_ui.css"}if(tinyMCE.settings['debug']){var msg="Debug: \n";msg+="baseURL: "+this.baseURL+"\n";msg+="documentBasePath: "+this.documentBasePath+"\n";msg+="content_css: "+this.settings['content_css']+"\n";msg+="popups_css: "+this.settings['popups_css']+"\n";msg+="editor_css: "+this.settings['editor_css']+"\n";alert(msg)}if(this.configs.length==0){if(typeof(TinyMCECompressed)=="undefined"){tinyMCE.addEvent(window,"DOMContentLoaded",TinyMCE_Engine.prototype.onLoad);if(tinyMCE.isRealIE){if(document.body)tinyMCE.addEvent(document.body,"readystatechange",TinyMCE_Engine.prototype.onLoad);else tinyMCE.addEvent(document,"readystatechange",TinyMCE_Engine.prototype.onLoad)}tinyMCE.addEvent(window,"load",TinyMCE_Engine.prototype.onLoad);tinyMCE._addUnloadEvents()}}this.loadScript(tinyMCE.baseURL+'/themes/'+this.settings['theme']+'/editor_template'+tinyMCE.srcMode+'.js');this.loadScript(tinyMCE.baseURL+'/langs/'+this.settings['language']+'.js');this.loadCSS(this.settings['editor_css']);var p=tinyMCE.getParam('plugins','',true,',');if(p.length>0){for(var i=0;i&"\']','g');this.xmlEncodeRe=new RegExp('[<>&"]','g');},_addUnloadEvents:function(){if(tinyMCE.isIE){if(tinyMCE.settings['add_unload_trigger']){tinyMCE.addEvent(window,"unload",TinyMCE_Engine.prototype.unloadHandler);tinyMCE.addEvent(window.document,"beforeunload",TinyMCE_Engine.prototype.unloadHandler)}}else{if(tinyMCE.settings['add_unload_trigger'])tinyMCE.addEvent(window,"unload",function(){tinyMCE.triggerSave(true,true)})}},_def:function(key,def_val,t){var v=tinyMCE.getParam(key,def_val);v=t?v.replace(/\s+/g,""):v;this.settings[key]=v},hasPlugin:function(n){return typeof(this.plugins[n])!="undefined"&&this.plugins[n]!=null},addPlugin:function(n,p){var op=this.plugins[n];p.baseURL=op?op.baseURL:tinyMCE.baseURL+"/plugins/"+n;this.plugins[n]=p;this.loadNextScript()},setPluginBaseURL:function(n,u){var op=this.plugins[n];if(op)op.baseURL=u;else this.plugins[n]={baseURL:u}},loadPlugin:function(n,u){u=u.indexOf('.js')!=-1?u.substring(0,u.lastIndexOf('/')):u;u=u.charAt(u.length-1)=='/'?u.substring(0,u.length-1):u;this.plugins[n]={baseURL:u};this.loadScript(u+"/editor_plugin"+(tinyMCE.srcMode?'_src':'')+".js")},hasTheme:function(n){return typeof(this.themes[n])!="undefined"&&this.themes[n]!=null},addTheme:function(n,t){this.themes[n]=t;this.loadNextScript()},addMenu:function(n,m){this.menus[n]=m},hasMenu:function(n){return typeof(this.plugins[n])!="undefined"&&this.plugins[n]!=null},loadScript:function(url){var i;for(i=0;i');this.loadedFiles[this.loadedFiles.length]=url},loadNextScript:function(){var d=document,se;if(!tinyMCE.settings.strict_loading_mode)return;if(this.loadingIndex0){for(i=0,lflen=this.loadedFiles.length;i');this.loadedFiles[this.loadedFiles.length]=ar[x]}}}},importCSS:function(doc,css){var css_ary=css.replace(/\s+/,'').split(',');var csslen,elm,headArr,x,css_file;for(x=0,csslen=css_ary.length;x0){if(css_file.indexOf('://')==-1&&css_file.charAt(0)!='/')css_file=this.documentBasePath+"/"+css_file;if(typeof(doc.createStyleSheet)=="undefined"){elm=doc.createElement("link");elm.rel="stylesheet";elm.href=css_file;if((headArr=doc.getElementsByTagName("head"))!=null&&headArr.length>0)headArr[0].appendChild(elm)}else doc.createStyleSheet(css_file)}}},confirmAdd:function(e,settings){var elm=tinyMCE.isIE?event.srcElement:e.target;var elementId=elm.name?elm.name:elm.id;tinyMCE.settings=settings;if(tinyMCE.settings['convert_on_click']||(!elm.getAttribute('mce_noask')&&confirm(tinyMCELang['lang_edit_confirm'])))tinyMCE.addMCEControl(elm,elementId);elm.setAttribute('mce_noask','true')},updateContent:function(form_element_name){var formElement=document.getElementById(form_element_name);for(var n in tinyMCE.instances){var inst=tinyMCE.instances[n];if(!tinyMCE.isInstance(inst))continue;inst.switchSettings();if(inst.formElement==formElement){var doc=inst.getDoc();tinyMCE._setHTML(doc,inst.formElement.value);if(!tinyMCE.isIE)doc.body.innerHTML=tinyMCE._cleanupHTML(inst,doc,this.settings,doc.body,inst.visualAid)}}},addMCEControl:function(replace_element,form_element_name,target_document){var id="mce_editor_"+tinyMCE.idCounter++;var inst=new TinyMCE_Control(tinyMCE.settings);inst.editorId=id;this.instances[id]=inst;inst._onAdd(replace_element,form_element_name,target_document)},removeInstance:function(ti){var t=[],n,i;for(n in tinyMCE.instances){i=tinyMCE.instances[n];if(tinyMCE.isInstance(i)&&ti!=i)t[n]=i}tinyMCE.instances=t;n=[];t=tinyMCE.undoLevels;for(i=0;i0){tinyMCE.nextUndoRedoAction='Undo';inst=this.undoLevels[--this.undoIndex];inst.select();if(!tinyMCE.nextUndoRedoInstanceId)inst.execCommand('Undo')}}else inst.execCommand('Undo');return true;case"Redo":if(this.getParam('custom_undo_redo_global')){if(this.undoIndex<=this.undoLevels.length-1){tinyMCE.nextUndoRedoAction='Redo';inst=this.undoLevels[this.undoIndex++];inst.select();if(!tinyMCE.nextUndoRedoInstanceId)inst.execCommand('Redo')}}else inst.execCommand('Redo');return true;case'mceFocus':var inst=tinyMCE.getInstanceById(value);if(inst)inst.getWin().focus();return;case"mceAddControl":case"mceAddEditor":tinyMCE.addMCEControl(tinyMCE._getElementById(value),value);return;case"mceAddFrameControl":tinyMCE.addMCEControl(tinyMCE._getElementById(value['element'],value['document']),value['element'],value['document']);return;case"mceRemoveControl":case"mceRemoveEditor":tinyMCE.removeMCEControl(value);return;case"mceToggleEditor":var inst=tinyMCE.getInstanceById(value),pe,te;if(inst){pe=document.getElementById(inst.editorId+'_parent');te=inst.oldTargetElement;if(typeof(inst.enabled)=='undefined')inst.enabled=true;inst.enabled=!inst.enabled;if(!inst.enabled){pe.style.display='none';te.value=inst.getHTML();te.style.display=inst.oldTargetDisplay;tinyMCE.dispatchCallback(inst,'hide_instance_callback','hideInstance',inst)}else{pe.style.display='block';te.style.display='none';inst.setHTML(te.value);inst.useCSS=false;tinyMCE.dispatchCallback(inst,'show_instance_callback','showInstance',inst)}}else tinyMCE.addMCEControl(tinyMCE._getElementById(value),value);return;case"mceResetDesignMode":if(!tinyMCE.isIE){for(var n in tinyMCE.instances){if(!tinyMCE.isInstance(tinyMCE.instances[n]))continue;try{tinyMCE.instances[n].getDoc().designMode="on"}catch(e){}}}return}if(inst){inst.execCommand(command,user_interface,value)}else if(tinyMCE.settings['focus_alert'])alert(tinyMCELang['lang_focus_alert'])},_createIFrame:function(replace_element,doc,win){var iframe,id=replace_element.getAttribute("id");var aw,ah;if(typeof(doc)=="undefined")doc=document;if(typeof(win)=="undefined")win=window;iframe=doc.createElement("iframe");aw=""+tinyMCE.settings['area_width'];ah=""+tinyMCE.settings['area_height'];if(aw.indexOf('%')==-1){aw=parseInt(aw);aw=(isNaN(aw)||aw<0)?300:aw;aw=aw+"px"}if(ah.indexOf('%')==-1){ah=parseInt(ah);ah=(isNaN(ah)||ah<0)?240:ah;ah=ah+"px"}iframe.setAttribute("id",id);iframe.setAttribute("name",id);iframe.setAttribute("class","mceEditorIframe");iframe.setAttribute("border","0");iframe.setAttribute("frameBorder","0");iframe.setAttribute("marginWidth","0");iframe.setAttribute("marginHeight","0");iframe.setAttribute("leftMargin","0");iframe.setAttribute("topMargin","0");iframe.setAttribute("width",aw);iframe.setAttribute("height",ah);iframe.setAttribute("allowtransparency","true");iframe.className='mceEditorIframe';if(tinyMCE.settings["auto_resize"])iframe.setAttribute("scrolling","no");if(tinyMCE.isRealIE)iframe.setAttribute("src",this.settings['default_document']);iframe.style.width=aw;iframe.style.height=ah;if(tinyMCE.settings.strict_loading_mode)iframe.style.marginBottom='-5px';if(tinyMCE.isRealIE)replace_element.outerHTML=iframe.outerHTML;else replace_element.parentNode.replaceChild(iframe,replace_element);if(tinyMCE.isRealIE)return win.frames[id];else return iframe},setupContent:function(editor_id){var inst=tinyMCE.instances[editor_id],i;var doc=inst.getDoc();var head=doc.getElementsByTagName('head').item(0);var content=inst.startContent;if(tinyMCE.settings.strict_loading_mode){content=content.replace(/</g,'<');content=content.replace(/>/g,'>');content=content.replace(/"/g,'"');content=content.replace(/&/g,'&')}tinyMCE.selectedInstance=inst;inst.switchSettings();if(!tinyMCE.isIE&&tinyMCE.getParam("setupcontent_reload",false)&&doc.title!="blank_page"){try{doc.location.href=tinyMCE.baseURL+"/blank.htm"}catch(ex){}window.setTimeout("tinyMCE.setupContent('"+editor_id+"');",1000);return}if(!head){window.setTimeout("tinyMCE.setupContent('"+editor_id+"');",10);return}tinyMCE.importCSS(inst.getDoc(),tinyMCE.baseURL+"/themes/"+inst.settings['theme']+"/css/editor_content.css");tinyMCE.importCSS(inst.getDoc(),inst.settings['content_css']);tinyMCE.dispatchCallback(inst,'init_instance_callback','initInstance',inst);if(tinyMCE.getParam('custom_undo_redo_keyboard_shortcuts')){inst.addShortcut('ctrl','z','lang_undo_desc','Undo');inst.addShortcut('ctrl','y','lang_redo_desc','Redo')}for(i=1;i<=6;i++)inst.addShortcut('ctrl',''+i,'','FormatBlock',false,'');inst.addShortcut('ctrl','7','','FormatBlock',false,'

');inst.addShortcut('ctrl','8','','FormatBlock',false,'

');inst.addShortcut('ctrl','9','','FormatBlock',false,'
');if(tinyMCE.isGecko){inst.addShortcut('ctrl','b','lang_bold_desc','Bold');inst.addShortcut('ctrl','i','lang_italic_desc','Italic');inst.addShortcut('ctrl','u','lang_underline_desc','Underline')}if(tinyMCE.getParam("convert_fonts_to_spans"))inst.getBody().setAttribute('id','mceSpanFonts');if(tinyMCE.settings['nowrap'])doc.body.style.whiteSpace="nowrap";doc.body.dir=this.settings['directionality'];doc.editorId=editor_id;if(!tinyMCE.isIE)doc.documentElement.editorId=editor_id;inst.setBaseHREF(tinyMCE.settings['base_href']);if(tinyMCE.settings['convert_newlines_to_brs']){content=tinyMCE.regexpReplace(content,"\r\n","
","gi");content=tinyMCE.regexpReplace(content,"\r","
","gi");content=tinyMCE.regexpReplace(content,"\n","
","gi")}content=tinyMCE.storeAwayURLs(content);content=tinyMCE._customCleanup(inst,"insert_to_editor",content);if(tinyMCE.isIE){window.setInterval('try{tinyMCE.getCSSClasses(tinyMCE.instances["'+editor_id+'"].getDoc(), "'+editor_id+'");}catch(e){}',500);if(tinyMCE.settings["force_br_newlines"])doc.styleSheets[0].addRule("p","margin: 0;");var body=inst.getBody();body.editorId=editor_id}content=tinyMCE.cleanupHTMLCode(content);if(!tinyMCE.isIE){var contentElement=inst.getDoc().createElement("body");var doc=inst.getDoc();contentElement.innerHTML=content;if(tinyMCE.isGecko&&tinyMCE.settings['remove_lt_gt'])content=content.replace(new RegExp('<>','g'),"");if(tinyMCE.settings['cleanup_on_startup'])tinyMCE.setInnerHTML(inst.getBody(),tinyMCE._cleanupHTML(inst,doc,this.settings,contentElement));else tinyMCE.setInnerHTML(inst.getBody(),content);tinyMCE.convertAllRelativeURLs(inst.getBody())}else{if(tinyMCE.settings['cleanup_on_startup']){tinyMCE._setHTML(inst.getDoc(),content);eval('try {tinyMCE.setInnerHTML(inst.getBody(), tinyMCE._cleanupHTML(inst, inst.contentDocument, this.settings, inst.getBody()));} catch(e) {}')}else tinyMCE._setHTML(inst.getDoc(),content)}tinyMCE.handleVisualAid(inst.getBody(),true,tinyMCE.settings['visual'],inst);tinyMCE.dispatchCallback(inst,'setupcontent_callback','setupContent',editor_id,inst.getBody(),inst.getDoc());if(!tinyMCE.isIE)tinyMCE.addEventHandlers(inst);if(tinyMCE.isIE){tinyMCE.addEvent(inst.getBody(),"blur",TinyMCE_Engine.prototype._eventPatch);tinyMCE.addEvent(inst.getBody(),"beforedeactivate",TinyMCE_Engine.prototype._eventPatch);if(!tinyMCE.isOpera){tinyMCE.addEvent(doc.body,"mousemove",TinyMCE_Engine.prototype.onMouseMove);tinyMCE.addEvent(doc.body,"beforepaste",TinyMCE_Engine.prototype._eventPatch);tinyMCE.addEvent(doc.body,"drop",TinyMCE_Engine.prototype._eventPatch)}}inst.select();tinyMCE.selectedElement=inst.contentWindow.document.body;tinyMCE._customCleanup(inst,"insert_to_editor_dom",inst.getBody());tinyMCE._customCleanup(inst,"setup_content_dom",inst.getBody());tinyMCE._setEventsEnabled(inst.getBody(),false);tinyMCE.cleanupAnchors(inst.getDoc());if(tinyMCE.getParam("convert_fonts_to_spans"))tinyMCE.convertSpansToFonts(inst.getDoc());inst.startContent=tinyMCE.trim(inst.getBody().innerHTML);inst.undoRedo.add({content:inst.startContent});if(tinyMCE.isGecko){tinyMCE.selectNodes(inst.getBody(),function(n){if(n.nodeType==3||n.nodeType==8)n.nodeValue=n.nodeValue.replace(new RegExp('\\s(mce_src|mce_href)=\"[^\"]*\"','gi'),"");return false})}if(tinyMCE.isGecko)inst.getBody().spellcheck=tinyMCE.getParam("gecko_spellcheck");tinyMCE._removeInternal(inst.getBody());inst.select();tinyMCE.triggerNodeChange(false,true)},storeAwayURLs:function(s){if(!s.match(/(mce_src|mce_href)/gi,s)){s=s.replace(new RegExp('src\\s*=\\s*\"([^ >\"]*)\"','gi'),'src="$1" mce_src="$1"');s=s.replace(new RegExp('href\\s*=\\s*\"([^ >\"]*)\"','gi'),'href="$1" mce_href="$1"')}return s},_removeInternal:function(n){if(tinyMCE.isGecko){tinyMCE.selectNodes(n,function(n){if(n.nodeType==3||n.nodeType==8)n.nodeValue=n.nodeValue.replace(new RegExp('\\s(mce_src|mce_href)=\"[^\"]*\"','gi'),"");return false})}},removeTinyMCEFormElements:function(form_obj){var i,elementId;if(!tinyMCE.getParam('hide_selects_on_submit'))return;if(typeof(form_obj)=="undefined"||form_obj==null)return;if(form_obj.nodeName!="FORM"){if(form_obj.form)form_obj=form_obj.form;else form_obj=tinyMCE.getParentElement(form_obj,"form")}if(form_obj==null)return;for(i=0;i");rng.collapse(false);rng.select();tinyMCE.execCommand("mceAddUndoLevel");tinyMCE.triggerNodeChange(false);return false}}if(e.keyCode==8||e.keyCode==46){tinyMCE.selectedElement=e.target;tinyMCE.linkElement=tinyMCE.getParentElement(e.target,"a");tinyMCE.imgElement=tinyMCE.getParentElement(e.target,"img");tinyMCE.triggerNodeChange(false)}return false;break;case"keyup":case"keydown":tinyMCE.hideMenus();tinyMCE.hasMouseMoved=false;if(inst&&inst.handleShortcut(e))return false;if(e.target.editorId)tinyMCE.instances[e.target.editorId].select();if(tinyMCE.selectedInstance)tinyMCE.selectedInstance.switchSettings();var inst=tinyMCE.selectedInstance;if(tinyMCE.isGecko&&tinyMCE.settings['force_p_newlines']&&(e.keyCode==8||e.keyCode==46)&&!e.shiftKey){if(TinyMCE_ForceParagraphs._handleBackSpace(tinyMCE.selectedInstance,e.type)){tinyMCE.execCommand("mceAddUndoLevel");e.preventDefault();return false}}tinyMCE.selectedElement=null;tinyMCE.selectedNode=null;var elm=tinyMCE.selectedInstance.getFocusElement();tinyMCE.linkElement=tinyMCE.getParentElement(elm,"a");tinyMCE.imgElement=tinyMCE.getParentElement(elm,"img");tinyMCE.selectedElement=elm;if(tinyMCE.isGecko&&e.type=="keyup"&&e.keyCode==9)tinyMCE.handleVisualAid(tinyMCE.selectedInstance.getBody(),true,tinyMCE.settings['visual'],tinyMCE.selectedInstance);if(tinyMCE.isIE&&e.type=="keydown"&&e.keyCode==13)tinyMCE.enterKeyElement=tinyMCE.selectedInstance.getFocusElement();if(tinyMCE.isIE&&e.type=="keyup"&&e.keyCode==13){var elm=tinyMCE.enterKeyElement;if(elm){var re=new RegExp('^HR|IMG|BR$','g');var dre=new RegExp('^H[1-6]$','g');if(!elm.hasChildNodes()&&!re.test(elm.nodeName)){if(dre.test(elm.nodeName))elm.innerHTML="  ";else elm.innerHTML=" "}}}var keys=tinyMCE.posKeyCodes;var posKey=false;for(var i=0;i';h+='';h+=''}else{h+='';h+='';h+=''}return h},getMenuButtonHTML:function(id,lang,img,mcmd,cmd,ui,val){var h='',m,x;mcmd='tinyMCE.execInstanceCommand(\'{$editor_id}\',\''+mcmd+'\');';cmd='tinyMCE.execInstanceCommand(\'{$editor_id}\',\''+cmd+'\'';if(typeof(ui)!="undefined"&&ui!=null)cmd+=','+ui;if(typeof(val)!="undefined"&&val!=null)cmd+=",'"+val+"'";cmd+=');';if(tinyMCE.getParam('button_tile_map')&&(!tinyMCE.isIE||tinyMCE.isOpera)&&(m=tinyMCE.buttonMap[id])!=null&&(tinyMCE.getParam("language")=="en"||img.indexOf('$lang')==-1)){x=0-(m*20)==0?'0':0-(m*20);if(tinyMCE.isRealIE)h+='';else h+='';h+='';h+='';h+='';h+=''}else{if(tinyMCE.isRealIE)h+='';else h+='';h+='';h+='';h+='';h+=''}return h},_menuButtonEvent:function(e,o){if(o.className=='mceMenuButtonFocus')return;if(e=='over')o.className=o.className+' mceMenuHover';else o.className=o.className.replace(/\s.*$/,'')},addButtonMap:function(m){var i,a=m.replace(/\s+/,'').split(',');for(i=0;i0);if(tinyMCE.settings['custom_undo_redo']){undoIndex=inst.undoRedo.undoIndex;undoLevels=inst.undoRedo.undoLevels.length}tinyMCE.dispatchCallback(inst,'handle_node_change_callback','handleNodeChange',editorId,elm,undoIndex,undoLevels,inst.visualAid,anySelection,setup_content)}if(this.selectedInstance&&(typeof(focus)=="undefined"||focus))this.selectedInstance.contentWindow.focus()},_customCleanup:function(inst,type,content){var pl,po,i;var customCleanup=tinyMCE.settings['cleanup_callback'];if(customCleanup!=""&&eval("typeof("+customCleanup+")")!="undefined")content=eval(customCleanup+"(type, content, inst);");po=tinyMCE.themes[tinyMCE.settings['theme']];if(po&&po.cleanup)content=po.cleanup(type,content,inst);pl=inst.plugins;for(i=0;i0)className+=" ";className+=classNames[i]}return className},handleVisualAid:function(el,deep,state,inst,skip_dispatch){if(!el)return;if(!skip_dispatch)tinyMCE.dispatchCallback(inst,'handle_visual_aid_callback','handleVisualAid',el,deep,state,inst);var tableElement=null;switch(el.nodeName){case"TABLE":var oldW=el.style.width;var oldH=el.style.height;var bo=tinyMCE.getAttrib(el,"border");bo=bo==""||bo=="0"?true:false;tinyMCE.setAttrib(el,"class",tinyMCE.getVisualAidClass(tinyMCE.getAttrib(el,"class"),state&&bo));el.style.width=oldW;el.style.height=oldH;for(var y=0;y0)return inst.cssClasses;if(typeof(editor_id)=="undefined"&&typeof(doc)=="undefined"){var instance;for(var instanceName in tinyMCE.instances){instance=tinyMCE.instances[instanceName];if(!tinyMCE.isInstance(instance))continue;break}doc=instance.getDoc()}if(typeof(doc)=="undefined"){var instance=tinyMCE.getInstanceById(editor_id);doc=instance.getDoc()}if(doc){var styles=doc.styleSheets;if(styles&&styles.length>0){for(var x=0;x'+tinyMCE.replaceVar(v,"pluginurl",o.baseURL)+'';return tinyMCE.replaceVar(v,"pluginurl",o.baseURL)}}o=tinyMCE.themes[tinyMCE.settings['theme']];if(o.getControlHTML&&(v=o.getControlHTML(c))!=''){if(rtl)return''+v+'';return v}return''},evalFunc:function(f,idx,a,o){o=!o?window:o;f=typeof(f)=='function'?f:o[f];return f.apply(o,Array.prototype.slice.call(a,idx))},dispatchCallback:function(i,p,n){return this.callFunc(i,p,n,0,this.dispatchCallback.arguments)},executeCallback:function(i,p,n){return this.callFunc(i,p,n,1,this.executeCallback.arguments)},execCommandCallback:function(i,p,n){return this.callFunc(i,p,n,2,this.execCommandCallback.arguments)},callFunc:function(ins,p,n,m,a){var l,i,on,o,s,v;s=m==2;l=tinyMCE.getParam(p,'');if(l!=''&&(v=tinyMCE.evalFunc(l,3,a))==s&&m>0)return true;if(ins!=null){for(i=0,l=ins.plugins;i0)return true}}l=tinyMCE.themes;for(on in l){o=l[on];if(o[n]&&(v=tinyMCE.evalFunc(n,3,a,o))==s&&m>0)return true}return false},xmlEncode:function(s,skip_apos){return s?(''+s).replace(!skip_apos?this.xmlEncodeAposRe:this.xmlEncodeRe,function(c,b){switch(c){case'&':return'&';case'"':return'"';case'\'':return''';case'<':return'<';case'>':return'>'}return c}):s},extend:function(p,np){var o={};o.parent=p;for(n in p)o[n]=p[n];for(n in np)o[n]=np[n];return o},hideMenus:function(){var e=tinyMCE.lastSelectedMenuBtn;if(tinyMCE.lastMenu){tinyMCE.lastMenu.hide();tinyMCE.lastMenu=null}if(e){tinyMCE.switchClass(e,tinyMCE.lastMenuBtnClass);tinyMCE.lastSelectedMenuBtn=null}}};var TinyMCE=TinyMCE_Engine;var tinyMCE=new TinyMCE_Engine();var tinyMCELang={};function TinyMCE_Control(settings){var t,i,to,fu,p,x,fn,fu,pn,s=settings;this.undoRedoLevel=true;this.isTinyMCE_Control=true;this.settings=s;this.settings['theme']=tinyMCE.getParam("theme","default");this.settings['width']=tinyMCE.getParam("width",-1);this.settings['height']=tinyMCE.getParam("height",-1);this.selection=new TinyMCE_Selection(this);this.undoRedo=new TinyMCE_UndoRedo(this);this.cleanup=new TinyMCE_Cleanup();this.shortcuts=new Array();this.hasMouseMoved=false;this.foreColor=this.backColor="#999999";this.data={};this.cssClasses=[];this.cleanup.init({valid_elements:s.valid_elements,extended_valid_elements:s.extended_valid_elements,valid_child_elements:s.valid_child_elements,entities:s.entities,entity_encoding:s.entity_encoding,debug:s.cleanup_debug,indent:s.apply_source_formatting,invalid_elements:s.invalid_elements,verify_html:s.verify_html,fix_content_duplication:s.fix_content_duplication,convert_fonts_to_spans:s.convert_fonts_to_spans});t=this.settings['theme'];if(!tinyMCE.hasTheme(t)){fn=tinyMCE.callbacks;to={};for(i=0;i0){for(i=0;i1&&tinyMCE.currentConfig!=this.settings['index']){tinyMCE.settings=this.settings;tinyMCE.currentConfig=this.settings['index']}},select:function(){var oldInst=tinyMCE.selectedInstance;if(oldInst!=this){if(oldInst)oldInst.execCommand('mceEndTyping');tinyMCE.dispatchCallback(this,'select_instance_callback','selectInstance',this,oldInst);tinyMCE.selectedInstance=this}},getBody:function(){return this.contentBody?this.contentBody:this.getDoc().body},getDoc:function(){return this.contentWindow.document},getWin:function(){return this.contentWindow},getContainerWin:function(){return this.containerWindow?this.containerWindow:window},getViewPort:function(){return tinyMCE.getViewPort(this.getWin())},getParentNode:function(n,f){return tinyMCE.getParentNode(n,f,this.getBody())},getParentElement:function(n,na,f){return tinyMCE.getParentElement(n,na,f,this.getBody())},getParentBlockElement:function(n){return tinyMCE.getParentBlockElement(n,this.getBody())},resizeToContent:function(){var d=this.getDoc(),b=d.body,de=d.documentElement;this.iframeElement.style.height=(tinyMCE.isRealIE)?b.scrollHeight:de.offsetHeight+'px'},addShortcut:function(m,k,d,cmd,ui,va){var n=typeof(k)=="number",ie=tinyMCE.isIE,c,sc,i,scl=this.shortcuts;if(!tinyMCE.getParam('custom_shortcuts'))return false;m=m.toLowerCase();k=ie&&!n?k.toUpperCase():k;c=n?null:k.charCodeAt(0);d=d&&d.indexOf('lang_')==0?tinyMCE.getLang(d):d;sc={alt:m.indexOf('alt')!=-1,ctrl:m.indexOf('ctrl')!=-1,shift:m.indexOf('shift')!=-1,charCode:c,keyCode:n?k:(ie?c:null),desc:d,cmd:cmd,ui:ui,val:va};for(i=0;i0)rng.pasteHTML('
'+rng.htmlText+"
");tinyMCE.triggerNodeChange();return}}}switch(command){case"mceRepaint":this.repaint();return true;case"unlink":if(tinyMCE.isGecko&&this.getSel().isCollapsed){focusElm=tinyMCE.getParentElement(focusElm,'A');if(focusElm)this.selection.selectNode(focusElm,false)}this.getDoc().execCommand(command,user_interface,value);tinyMCE.isGecko&&this.getSel().collapseToEnd();tinyMCE.triggerNodeChange();return true;case"InsertUnorderedList":case"InsertOrderedList":this.getDoc().execCommand(command,user_interface,value);tinyMCE.triggerNodeChange();break;case"Strikethrough":this.getDoc().execCommand(command,user_interface,value);tinyMCE.triggerNodeChange();break;case"mceSelectNode":this.selection.selectNode(value);tinyMCE.triggerNodeChange();tinyMCE.selectedNode=value;break;case"FormatBlock":if(value==null||value==""){var elm=tinyMCE.getParentElement(this.getFocusElement(),"p,div,h1,h2,h3,h4,h5,h6,pre,address,blockquote,dt,dl,dd,samp");if(elm)this.execCommand("mceRemoveNode",false,elm)}else{if(!this.cleanup.isValid(value))return true;if(tinyMCE.isGecko&&new RegExp('<(div|blockquote|code|dt|dd|dl|samp)>','gi').test(value))value=value.replace(/[^a-z]/gi,'');if(tinyMCE.isIE&&new RegExp('blockquote|code|samp','gi').test(value)){var b=this.selection.getBookmark();this.getDoc().execCommand("FormatBlock",false,'

');tinyMCE.renameElement(tinyMCE.getParentBlockElement(this.getFocusElement()),value);this.selection.moveToBookmark(b)}else this.getDoc().execCommand("FormatBlock",false,value)}tinyMCE.triggerNodeChange();break;case"mceRemoveNode":if(!value)value=tinyMCE.getParentElement(this.getFocusElement());if(tinyMCE.isIE){value.outerHTML=value.innerHTML}else{var rng=value.ownerDocument.createRange();rng.setStartBefore(value);rng.setEndAfter(value);rng.deleteContents();rng.insertNode(rng.createContextualFragment(value.innerHTML))}tinyMCE.triggerNodeChange();break;case"mceSelectNodeDepth":var parentNode=this.getFocusElement();for(var i=0;parentNode;i++){if(parentNode.nodeName.toLowerCase()=="body")break;if(parentNode.nodeName.toLowerCase()=="#text"){i--;parentNode=parentNode.parentNode;continue}if(i==value){this.selection.selectNode(parentNode,false);tinyMCE.triggerNodeChange();tinyMCE.selectedNode=parentNode;return}parentNode=parentNode.parentNode}break;case"mceSetStyleInfo":case"SetStyleInfo":var rng=this.getRng();var sel=this.getSel();var scmd=value['command'];var sname=value['name'];var svalue=value['value']==null?'':value['value'];var wrapper=value['wrapper']?value['wrapper']:"span";var parentElm=null;var invalidRe=new RegExp("^BODY|HTML$","g");var invalidParentsRe=tinyMCE.settings['merge_styles_invalid_parents']!=''?new RegExp(tinyMCE.settings['merge_styles_invalid_parents'],"gi"):null;if(tinyMCE.isIE){if(rng.item)parentElm=rng.item(0);else{var pelm=rng.parentElement();var prng=doc.selection.createRange();prng.moveToElementText(pelm);if(rng.htmlText==prng.htmlText||rng.boundingWidth==0){if(invalidParentsRe==null||!invalidParentsRe.test(pelm.nodeName))parentElm=pelm}}}else{var felm=this.getFocusElement();if(sel.isCollapsed||(new RegExp('td|tr|tbody|table','gi').test(felm.nodeName)&&sel.anchorNode==felm.parentNode))parentElm=felm}if(parentElm&&!invalidRe.test(parentElm.nodeName)){if(scmd=="setstyle")tinyMCE.setStyleAttrib(parentElm,sname,svalue);if(scmd=="setattrib")tinyMCE.setAttrib(parentElm,sname,svalue);if(scmd=="removeformat"){parentElm.style.cssText='';tinyMCE.setAttrib(parentElm,'class','')}var ch=tinyMCE.getNodeTree(parentElm,new Array(),1);for(var z=0;z=0;i--){var elm=nodes[i];var isNew=tinyMCE.getAttrib(elm,"mce_new")=="true";elm.removeAttribute("mce_new");if(elm.childNodes&&elm.childNodes.length==1&&elm.childNodes[0].nodeType==1){this._mergeElements(scmd,elm,elm.childNodes[0],isNew);continue}if(elm.parentNode.childNodes.length==1&&!invalidRe.test(elm.nodeName)&&!invalidRe.test(elm.parentNode.nodeName)){if(invalidParentsRe==null||!invalidParentsRe.test(elm.parentNode.nodeName))this._mergeElements(scmd,elm.parentNode,elm,false)}}var nodes=doc.getElementsByTagName(wrapper);for(var i=nodes.length-1;i>=0;i--){var elm=nodes[i];var isEmpty=true;var tmp=doc.createElement("body");tmp.appendChild(elm.cloneNode(false));tmp.innerHTML=tmp.innerHTML.replace(new RegExp('style=""|class=""','gi'),'');if(new RegExp('','gi').test(tmp.innerHTML)){for(var x=0;x0){value=tinyMCE.replaceVar(value,"selection",selectedText);tinyMCE.execCommand('mceInsertContent',false,value)}tinyMCE.triggerNodeChange();break;case"mceSetAttribute":if(typeof(value)=='object'){var targetElms=(typeof(value['targets'])=="undefined")?"p,img,span,div,td,h1,h2,h3,h4,h5,h6,pre,address":value['targets'];var targetNode=tinyMCE.getParentElement(this.getFocusElement(),targetElms);if(targetNode){targetNode.setAttribute(value['name'],value['value']);tinyMCE.triggerNodeChange()}}break;case"mceSetCSSClass":this.execCommand("mceSetStyleInfo",false,{command:"setattrib",name:"class",value:value});break;case"mceInsertRawHTML":var key='tiny_mce_marker';this.execCommand('mceBeginUndoLevel');this.execCommand('mceInsertContent',false,key);var scrollX=this.getBody().scrollLeft+this.getDoc().documentElement.scrollLeft;var scrollY=this.getBody().scrollTop+this.getDoc().documentElement.scrollTop;var html=this.getBody().innerHTML;if((pos=html.indexOf(key))!=-1)tinyMCE.setInnerHTML(this.getBody(),html.substring(0,pos)+value+html.substring(pos+key.length));this.contentWindow.scrollTo(scrollX,scrollY);this.execCommand('mceEndUndoLevel');break;case"mceInsertContent":if(!value)value='';var insertHTMLFailed=false;if(tinyMCE.isGecko||tinyMCE.isOpera){try{if(value.indexOf('<')==-1&&!value.match(/(&| |<|>)/g)){var r=this.getRng();var n=this.getDoc().createTextNode(tinyMCE.entityDecode(value));var s=this.getSel();var r2=r.cloneRange();s.removeAllRanges();r.deleteContents();r.insertNode(n);r2.selectNode(n);r2.collapse(false);s.removeAllRanges();s.addRange(r2)}else{value=tinyMCE.fixGeckoBaseHREFBug(1,this.getDoc(),value);this.getDoc().execCommand('inserthtml',false,value);tinyMCE.fixGeckoBaseHREFBug(2,this.getDoc(),value)}}catch(ex){insertHTMLFailed=true}if(!insertHTMLFailed){tinyMCE.triggerNodeChange();return}}if(!tinyMCE.isIE){var isHTML=value.indexOf('<')!=-1;var sel=this.getSel();var rng=this.getRng();if(isHTML){if(tinyMCE.isSafari){var tmpRng=this.getDoc().createRange();tmpRng.setStart(this.getBody(),0);tmpRng.setEnd(this.getBody(),0);value=tmpRng.createContextualFragment(value)}else value=rng.createContextualFragment(value)}else{var el=document.createElement("div");el.innerHTML=value;value=el.firstChild.nodeValue;value=doc.createTextNode(value)}if(tinyMCE.isSafari&&!isHTML){this.execCommand('InsertText',false,value.nodeValue);tinyMCE.triggerNodeChange();return true}else if(tinyMCE.isSafari&&isHTML){rng.deleteContents();rng.insertNode(value);tinyMCE.triggerNodeChange();return true}rng.deleteContents();if(rng.startContainer.nodeType==3){var node=rng.startContainer.splitText(rng.startOffset);node.parentNode.insertBefore(value,node)}else rng.insertNode(value);if(!isHTML){sel.selectAllChildren(doc.body);sel.removeAllRanges();var rng=doc.createRange();rng.selectNode(value);rng.collapse(false);sel.addRange(rng)}else rng.collapse(false);tinyMCE.fixGeckoBaseHREFBug(2,this.getDoc(),value)}else{var rng=doc.selection.createRange(),tmpRng=null;var c=value.indexOf('"}if(hc){cn=n.childNodes;for(i=0,l=cn.length;i';return h},_serializeAttribute:function(n,r,an){var av='',t,os=this.settings.on_save;if(os&&(an.indexOf('mce_')==0||an.indexOf('_moz')==0))return'';if(os&&this.mceAttribs[an])av=this._getAttrib(n,this.mceAttribs[an]);if(av.length==0)av=this._getAttrib(n,an);if(av.length==0&&r.defaultAttribs&&(t=r.defaultAttribs[an])){av=t;if(av=="mce_empty")return" "+an+'=""'}if(r.forceAttribs&&(t=r.forceAttribs[an]))av=t;if(os&&av.length!=0&&/^(src|href|longdesc)$/.test(an))av=this._urlConverter(this,n,av);if(av.length!=0&&r.validAttribValues&&r.validAttribValues[an]&&!r.validAttribValues[an].test(av))return"";if(av.length!=0&&av=="{$uid}")av="uid_"+(this.idCount++);if(av.length!=0){if(an.indexOf('on')!=0)av=this.xmlEncode(av,1);return" "+an+"="+'"'+av+'"'}return""},formatHTML:function(h){var s=this.settings,p='',i=0,li=0,o='',l;h=h.replace(/]*)>(.*?)<\/pre>/gi,function(a,b,c){c=c.replace(//gi,'\n');return''+c+''});h=h.replace(/\r/g,'');h='\n'+h;h=h.replace(new RegExp('\\n\\s+','gi'),'\n');h=h.replace(this.nlBeforeRe,'\n<$1$2>');h=h.replace(this.nlAfterRe,'<$1$2>\n');h=h.replace(this.nlBeforeAfterRe,'\n<$1$2$3>\n');h+='\n';while((i=h.indexOf('\n',i+1))!=-1){if((l=h.substring(li+1,i)).length!=0){if(this.ouRe.test(l)&&p.length>=s.indent_levels)p=p.substring(s.indent_levels);o+=p+l+'\n';if(this.inRe.test(l))p+=this.inStr}li=i}return o},xmlEncode:function(s,skip_apos){var cl=this,re=!skip_apos?this.xmlEncodeAposRe:this.xmlEncodeRe;this._setupEntities();switch(this.settings.entity_encoding){case"raw":return tinyMCE.xmlEncode(s,skip_apos);case"named":return s.replace(re,function(c,b){b=cl.entities[c.charCodeAt(0)];return b?'&'+b+';':c});case"numeric":return s.replace(re,function(c,b){return b?'&#'+c.charCodeAt(0)+';':c})}return s},split:function(re,s){var c=s.split(re);var i,l,o=new Array();for(i=0,l=c.length;i':'>'+h+'';return o};TinyMCE_Engine.prototype.createTag=function(d,tn,a,h){var o=d.createElement(tn);if(a){for(n in a){if(typeof(a[n])!='function'&&a[n]!=null)tinyMCE.setAttrib(o,n,a[n])}}if(h)o.innerHTML=h;return o};TinyMCE_Engine.prototype.getElementByAttributeValue=function(n,e,a,v){return(n=this.getElementsByAttributeValue(n,e,a,v)).length==0?null:n[0]};TinyMCE_Engine.prototype.getElementsByAttributeValue=function(n,e,a,v){var i,nl=n.getElementsByTagName(e),o=new Array();for(i=0;i]*)>/gi,'');h=h.replace(/]*)>/gi,'');h=h.replace(/]*)>/gi,'');h=h.replace(/]*)>/gi,'');h=h.replace(/<\/strong>/gi,'');h=h.replace(/<\/em>/gi,'')}if(tinyMCE.isRealIE){h=h.replace(/\s\/>/g,'>');h=h.replace(/]*)>\u00A0?<\/p>/gi,' 

');h=h.replace(/]*)>\s* \s*<\/p>/gi,' 

');h=h.replace(/]*)>\s+<\/p>/gi,' 

');e.innerHTML=tinyMCE.uniqueTag+h;e.firstChild.removeNode(true);nl=e.getElementsByTagName("p");for(i=nl.length-1;i>=0;i--){n=nl[i];if(n.nodeName=='P'&&!n.hasChildNodes()&&!n.mce_keep)n.parentNode.removeChild(n)}}else{h=this.fixGeckoBaseHREFBug(1,e,h);e.innerHTML=h;this.fixGeckoBaseHREFBug(2,e,h)}};TinyMCE_Engine.prototype.getOuterHTML=function(e){if(tinyMCE.isIE)return e.outerHTML;var d=e.ownerDocument.createElement("body");d.appendChild(e.cloneNode(true));return d.innerHTML};TinyMCE_Engine.prototype.setOuterHTML=function(e,h,d){var d=typeof(d)=="undefined"?e.ownerDocument:d,i,nl,t;if(tinyMCE.isIE&&e.nodeType==1)e.outerHTML=h;else{t=d.createElement("body");t.innerHTML=h;for(i=0,nl=t.childNodes;i-1;i--){if(ar[i].specified&&ar[i].nodeValue)ne.setAttribute(ar[i].nodeName.toLowerCase(),ar[i].nodeValue)}ar=e.childNodes;for(i=0;i=strTok2.length){for(var i=0;i=strTok2.length||strTok1[i]!=strTok2[i]){breakPoint=i+1;break}}}if(strTok1.length=strTok1.length||strTok1[i]!=strTok2[i]){breakPoint=i+1;break}}}if(breakPoint==1)return targetURL.path;for(var i=0;i<(strTok1.length-(breakPoint-1));i++)outPath+="../";for(var i=breakPoint-1;i=0;i--){if(baseURLParts[i].length==0)continue;newBaseURLParts[newBaseURLParts.length]=baseURLParts[i]}baseURLParts=newBaseURLParts.reverse();var newRelURLParts=new Array();var numBack=0;for(var i=relURLParts.length-1;i>=0;i--){if(relURLParts[i].length==0||relURLParts[i]==".")continue;if(relURLParts[i]=='..'){numBack++;continue}if(numBack>0){numBack--;continue}newRelURLParts[newRelURLParts.length]=relURLParts[i]}relURLParts=newRelURLParts.reverse();var len=baseURLParts.length-numBack;var absPath=(len<=0?"":"/")+baseURLParts.slice(0,len).join('/')+"/"+relURLParts.join('/');var start="",end="";relURL.protocol=baseURL.protocol;relURL.host=baseURL.host;relURL.port=baseURL.port;if(relURL.path.charAt(relURL.path.length-1)=="/")absPath+="/";relURL.path=absPath;return this.serializeURL(relURL)};TinyMCE_Engine.prototype.convertURL=function(url,node,on_save){var prot=document.location.protocol;var host=document.location.hostname;var port=document.location.port;if(prot=="file:")return url;url=tinyMCE.regexpReplace(url,'(http|https):///','/');if(url.indexOf('mailto:')!=-1||url.indexOf('javascript:')!=-1||tinyMCE.regexpReplace(url,'[ \t\r\n\+]|%20','').charAt(0)=="#")return url;if(!tinyMCE.isIE&&!on_save&&url.indexOf("://")==-1&&url.charAt(0)!='/')return tinyMCE.settings['base_href']+url;if(on_save&&tinyMCE.getParam('relative_urls')){var curl=tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'],url);if(curl.charAt(0)=='/')curl=tinyMCE.settings['document_base_prefix']+curl;var urlParts=tinyMCE.parseURL(curl);var tmpUrlParts=tinyMCE.parseURL(tinyMCE.settings['document_base_url']);if(urlParts['host']==tmpUrlParts['host']&&(urlParts['port']==tmpUrlParts['port']))return tinyMCE.convertAbsoluteURLToRelativeURL(tinyMCE.settings['document_base_url'],curl)}if(!tinyMCE.getParam('relative_urls')){var urlParts=tinyMCE.parseURL(url);var baseUrlParts=tinyMCE.parseURL(tinyMCE.settings['base_href']);url=tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'],url);if(urlParts['anchor']&&urlParts['path']==baseUrlParts['path'])return"#"+urlParts['anchor']}if(tinyMCE.getParam('remove_script_host')){var start="",portPart="";if(port!="")portPart=":"+port;start=prot+"//"+host+portPart+"/";if(url.indexOf(start)==0)url=url.substring(start.length-1)}return url};TinyMCE_Engine.prototype.convertAllRelativeURLs=function(body){var i,elms,src,href,mhref,msrc;elms=body.getElementsByTagName("img");for(i=0;ibookmark.index){try{rng.addElement(nl[bookmark.index])}catch(ex){}}}else{try{if(bookmark.start<0)return true;rng=inst.getSel().createRange();rng.moveToElementText(inst.getBody());rng.collapse(true);rng.moveStart('character',bookmark.start);rng.moveEnd('character',bookmark.length)}catch(ex){return true}}rng.select();win.scrollTo(bookmark.scrollX,bookmark.scrollY);return true}if(tinyMCE.isGecko||tinyMCE.isOpera){if(!sel)return false;if(bookmark.rng){sel.removeAllRanges();sel.addRange(bookmark.rng)}if(bookmark.start!=-1&&bookmark.end!=-1){try{sd=this._getTextPos(b,bookmark.start,bookmark.end);rng=doc.createRange();rng.setStart(sd.startNode,sd.startOffset);rng.setEnd(sd.endNode,sd.endOffset);sel.removeAllRanges();sel.addRange(rng);win.focus()}catch(ex){}}win.scrollTo(bookmark.scrollX,bookmark.scrollY);return true}return false},_getPosText:function(r,sn,en){var w=document.createTreeWalker(r,NodeFilter.SHOW_TEXT,null,false),n,p=0,d={};while((n=w.nextNode())!=null){if(n==sn)d.start=p;if(n==en){d.end=p;return d}p+=n.nodeValue?n.nodeValue.length:0}return null},_getTextPos:function(r,sp,ep){var w=document.createTreeWalker(r,NodeFilter.SHOW_TEXT,null,false),n,p=0,d={};while((n=w.nextNode())!=null){p+=n.nodeValue?n.nodeValue.length:0;if(p>=sp&&!d.startNode){d.startNode=n;d.startOffset=sp-(p-n.nodeValue.length)}if(p>=ep){d.endNode=n;d.endOffset=ep-(p-n.nodeValue.length);return d}}return null},selectNode:function(node,collapse,select_text_node,to_start){var inst=this.instance,sel,rng,nodes;if(!node)return;if(typeof(collapse)=="undefined")collapse=true;if(typeof(select_text_node)=="undefined")select_text_node=false;if(typeof(to_start)=="undefined")to_start=true;if(inst.settings.auto_resize)inst.resizeToContent();if(tinyMCE.isRealIE){rng=inst.getDoc().body.createTextRange();try{rng.moveToElementText(node);if(collapse)rng.collapse(to_start);rng.select()}catch(e){}}else{sel=this.getSel();if(!sel)return;if(tinyMCE.isSafari){sel.setBaseAndExtent(node,0,node,node.innerText.length);if(collapse){if(to_start)sel.collapseToStart();else sel.collapseToEnd()}this.scrollToNode(node);return}rng=inst.getDoc().createRange();if(select_text_node){nodes=tinyMCE.getNodeTree(node,new Array(),3);if(nodes.length>0)rng.selectNodeContents(nodes[0]);else rng.selectNodeContents(node)}else rng.selectNode(node);if(collapse){if(!to_start&&node.nodeType==3){rng.setStart(node,node.nodeValue.length);rng.setEnd(node,node.nodeValue.length)}else rng.collapse(to_start)}sel.removeAllRanges();sel.addRange(rng)}this.scrollToNode(node);tinyMCE.selectedElement=null;if(node.nodeType==1)tinyMCE.selectedElement=node},scrollToNode:function(node){var inst=this.instance,w=inst.getWin(),vp=inst.getViewPort(),pos=tinyMCE.getAbsPosition(node),cvp,p,cwin;if(pos.absLeftvp.left+vp.width||pos.absTopvp.top+(vp.height-25))w.scrollTo(pos.absLeft,pos.absTop-vp.height+25);if(inst.settings.auto_resize){cwin=inst.getContainerWin();cvp=tinyMCE.getViewPort(cwin);p=this.getAbsPosition(node);if(p.absLeftcvp.left+cvp.width||p.absTopcvp.top+cvp.height)cwin.scrollTo(p.absLeft,p.absTop-cvp.height+25)}},getAbsPosition:function(n){var pos=tinyMCE.getAbsPosition(n),ipos=tinyMCE.getAbsPosition(this.instance.iframeElement);return{absLeft:ipos.absLeft+pos.absLeft,absTop:ipos.absTop+pos.absTop}},getSel:function(){var inst=this.instance;if(tinyMCE.isRealIE)return inst.getDoc().selection;return inst.contentWindow.getSelection()},getRng:function(){var s=this.getSel();if(s==null)return null;if(tinyMCE.isRealIE)return s.createRange();if(tinyMCE.isSafari&&!s.getRangeAt)return''+window.getSelection();if(s.rangeCount>0)return s.getRangeAt(0);return null},isCollapsed:function(){var r=this.getRng();if(r.item)return false;return r.boundingWidth==0||this.getSel().isCollapsed},collapse:function(b){var r=this.getRng(),s=this.getSel();if(r.select){r.collapse(b);r.select()}else{if(b)s.collapseToStart();else s.collapseToEnd()}},getFocusElement:function(){var inst=this.instance,doc,rng,sel,elm;if(tinyMCE.isRealIE){doc=inst.getDoc();rng=doc.selection.createRange();elm=rng.item?rng.item(0):rng.parentElement()}else{if(!tinyMCE.isSafari&&inst.isHidden())return inst.getBody();sel=this.getSel();rng=this.getRng();if(!sel||!rng)return null;elm=rng.commonAncestorContainer;if(!rng.collapsed){if(rng.startContainer==rng.endContainer){if(rng.startOffset-rng.endOffset<2){if(rng.startContainer.hasChildNodes())elm=rng.startContainer.childNodes[rng.startOffset]}}}elm=tinyMCE.getParentElement(elm);}return elm}};function TinyMCE_UndoRedo(inst){this.instance=inst;this.undoLevels=new Array();this.undoIndex=0;this.typingUndoIndex=-1;this.undoRedo=true};TinyMCE_UndoRedo.prototype={add:function(l){var b,customUndoLevels,newHTML,inst=this.instance,i,ul,ur;if(l){this.undoLevels[this.undoLevels.length]=l;return true}if(this.typingUndoIndex!=-1){this.undoIndex=this.typingUndoIndex;if(tinyMCE.typingUndoIndex!=-1)tinyMCE.undoIndex=tinyMCE.typingUndoIndex}newHTML=tinyMCE.trim(inst.getBody().innerHTML);if(this.undoLevels[this.undoIndex]&&newHTML!=this.undoLevels[this.undoIndex].content){tinyMCE.dispatchCallback(inst,'onchange_callback','onChange',inst);customUndoLevels=tinyMCE.settings['custom_undo_redo_levels'];if(customUndoLevels!=-1&&this.undoLevels.length>customUndoLevels){for(i=0;i0){this.undoIndex--;tinyMCE.setInnerHTML(inst.getBody(),this.undoLevels[this.undoIndex].content);inst.repaint();if(inst.settings.custom_undo_redo_restore_selection)inst.selection.moveToBookmark(this.undoLevels[this.undoIndex].bookmark)}},redo:function(){var inst=this.instance;tinyMCE.execCommand("mceEndTyping");if(this.undoIndex<(this.undoLevels.length-1)){this.undoIndex++;tinyMCE.setInnerHTML(inst.getBody(),this.undoLevels[this.undoIndex].content);inst.repaint();if(inst.settings.custom_undo_redo_restore_selection)inst.selection.moveToBookmark(this.undoLevels[this.undoIndex].bookmark)}tinyMCE.triggerNodeChange()}};var TinyMCE_ForceParagraphs={_insertPara:function(inst,e){var doc=inst.getDoc(),sel=inst.getSel(),body=inst.getBody(),win=inst.contentWindow,rng=sel.getRangeAt(0);var rootElm=doc.documentElement,blockName="P",startNode,endNode,startBlock,endBlock;var rngBefore,rngAfter,direct,startNode,startOffset,endNode,endOffset,b=tinyMCE.isOpera?inst.selection.getBookmark():null;var paraBefore,paraAfter,startChop,endChop,contents;function isEmpty(para){function isEmptyHTML(html){return html.replace(new RegExp('[ \t\r\n]+','g'),'').toLowerCase()==""}if(para.getElementsByTagName("img").length>0)return false;if(para.getElementsByTagName("table").length>0)return false;if(para.getElementsByTagName("hr").length>0)return false;var nodes=tinyMCE.getNodeTree(para,new Array(),3);for(var i=0;i <"+blockName+"> ";paraAfter=body.childNodes[1]}inst.selection.moveToBookmark(b);inst.selection.selectNode(paraAfter,true,true);return true}if(startChop.nodeName==blockName)rngBefore.setStart(startChop,0);else rngBefore.setStartBefore(startChop);rngBefore.setEnd(startNode,startOffset);paraBefore.appendChild(rngBefore.cloneContents());rngAfter.setEndAfter(endChop);rngAfter.setStart(endNode,endOffset);contents=rngAfter.cloneContents();if(contents.firstChild&&contents.firstChild.nodeName==blockName){paraAfter.innerHTML=contents.firstChild.innerHTML}else paraAfter.appendChild(contents);if(isEmpty(paraBefore))paraBefore.innerHTML=" ";if(isEmpty(paraAfter))paraAfter.innerHTML=" ";rng=doc.createRange();if(!startChop.previousSibling&&startChop.parentNode.nodeName.toUpperCase()==blockName){rng.setStartBefore(startChop.parentNode)}else{if(rngBefore.startContainer.nodeName.toUpperCase()==blockName&&rngBefore.startOffset==0)rng.setStartBefore(rngBefore.startContainer);else rng.setStart(rngBefore.startContainer,rngBefore.startOffset)}if(!endChop.nextSibling&&endChop.parentNode.nodeName.toUpperCase()==blockName)rng.setEndAfter(endChop.parentNode);else rng.setEnd(rngAfter.endContainer,rngAfter.endOffset);rng.deleteContents();if(tinyMCE.isOpera){rng.insertNode(paraBefore);rng.insertNode(paraAfter)}else{rng.insertNode(paraAfter);rng.insertNode(paraBefore)}paraAfter.normalize();paraBefore.normalize();inst.selection.moveToBookmark(b);inst.selection.selectNode(paraAfter,true,true);return true},_handleBackSpace:function(inst){var r=inst.getRng(),sn=r.startContainer,nv,s=false;if(sn&&sn.nextSibling&&sn.nextSibling.nodeName=="BR"&&sn.parentNode.nodeName!="BODY"){nv=sn.nodeValue;if(nv!=null&&r.startOffset==nv.length)sn.nextSibling.parentNode.removeChild(sn.nextSibling)}if(inst.settings.auto_resize)inst.resizeToContent();return s}};function TinyMCE_Layer(id,bm){this.id=id;this.blockerElement=null;this.events=false;this.element=null;this.blockMode=typeof(bm)!='undefined'?bm:true;this.doc=document};TinyMCE_Layer.prototype={moveRelativeTo:function(re,p){var rep=this.getAbsPosition(re);var w=parseInt(re.offsetWidth);var h=parseInt(re.offsetHeight);var e=this.getElement();var ew=parseInt(e.offsetWidth);var eh=parseInt(e.offsetHeight);var x,y;switch(p){case"tl":x=rep.absLeft;y=rep.absTop;break;case"tr":x=rep.absLeft+w;y=rep.absTop;break;case"bl":x=rep.absLeft;y=rep.absTop+h;break;case"br":x=rep.absLeft+w;y=rep.absTop+h;break;case"cc":x=rep.absLeft+(w/ 2) - (ew /2);y=rep.absTop+(h/ 2) - (eh /2);break}this.moveTo(x,y)},moveBy:function(x,y){var e=this.getElement();this.moveTo(parseInt(e.style.left)+x,parseInt(e.style.top)+y)},moveTo:function(x,y){var e=this.getElement();e.style.left=x+"px";e.style.top=y+"px";this.updateBlocker()},resizeBy:function(w,h){var e=this.getElement();this.resizeTo(parseInt(e.style.width)+w,parseInt(e.style.height)+h)},resizeTo:function(w,h){var e=this.getElement();if(w!=null)e.style.width=w+"px";if(h!=null)e.style.height=h+"px";this.updateBlocker()},show:function(){var el=this.getElement();if(el){el.style.display='block';this.updateBlocker()}},hide:function(){var el=this.getElement();if(el){el.style.display='none';this.updateBlocker()}},isVisible:function(){return this.getElement().style.display=='block'},getElement:function(){if(!this.element)this.element=this.doc.getElementById(this.id);return this.element},setBlockMode:function(s){this.blockMode=s},updateBlocker:function(){var e,b,x,y,w,h;b=this.getBlocker();if(b){if(this.blockMode){e=this.getElement();x=this.parseInt(e.style.left);y=this.parseInt(e.style.top);w=this.parseInt(e.offsetWidth);h=this.parseInt(e.offsetHeight);b.style.left=x+'px';b.style.top=y+'px';b.style.width=w+'px';b.style.height=h+'px';b.style.display=e.style.display}else b.style.display='none'}},getBlocker:function(){var d,b;if(!this.blockerElement&&this.blockMode){d=this.doc;b=d.getElementById(this.id+"_blocker");if(!b){b=d.createElement("iframe");b.setAttribute('id',this.id+"_blocker");b.style.cssText='display: none; position: absolute; left: 0; top: 0';b.src='javascript:false;';b.frameBorder='0';b.scrolling='no';d.body.appendChild(b)}this.blockerElement=b}return this.blockerElement},getAbsPosition:function(n){var p={absLeft:0,absTop:0};while(n){p.absLeft+=n.offsetLeft;p.absTop+=n.offsetTop;n=n.offsetParent}return p},create:function(n,c,p,h){var d=this.doc,e=d.createElement(n);e.setAttribute('id',this.id);if(c)e.className=c;if(!p)p=d.body;if(h)e.innerHTML=h;p.appendChild(e);return this.element=e},exists:function(){return this.doc.getElementById(this.id)!=null},parseInt:function(s){if(s==null||s=='')return 0;return parseInt(s)},remove:function(){var e=this.getElement(),b=this.getBlocker();if(e)e.parentNode.removeChild(e);if(b)b.parentNode.removeChild(b)}};function TinyMCE_Menu(){var id;if(typeof(tinyMCE.menuCounter)=="undefined")tinyMCE.menuCounter=0;id="mc_menu_"+tinyMCE.menuCounter++;TinyMCE_Layer.call(this,id,true);this.id=id;this.items=new Array();this.needsUpdate=true};TinyMCE_Menu.prototype=tinyMCE.extend(TinyMCE_Layer.prototype,{init:function(s){var n;this.settings={separator_class:'mceMenuSeparator',title_class:'mceMenuTitle',disabled_class:'mceMenuDisabled',menu_class:'mceMenu',drop_menu:true};for(n in s)this.settings[n]=s[n];this.create('div',this.settings.menu_class)},clear:function(){this.items=new Array()},addTitle:function(t){this.add({type:'title',text:t})},addDisabled:function(t){this.add({type:'disabled',text:t})},addSeparator:function(){this.add({type:'separator'})},addItem:function(t,js){this.add({text:t,js:js})},add:function(mi){this.items[this.items.length]=mi;this.needsUpdate=true},update:function(){var e=this.getElement(),h='',i,t,m=this.items,s=this.settings;if(this.settings.drop_menu)h+='';h+='';for(i=0;i'}h+='
';break;case'title':h+='
'+t+'';break;case'disabled':h+='
'+t+'';break;default:h+='
'+t+''}h+='
';e.innerHTML=h;this.needsUpdate=false;this.updateBlocker()},show:function(){var nl,i;if(tinyMCE.lastMenu==this)return;if(this.needsUpdate)this.update();if(tinyMCE.lastMenu&&tinyMCE.lastMenu!=this)tinyMCE.lastMenu.hide();TinyMCE_Layer.prototype.show.call(this);if(!tinyMCE.isOpera){}tinyMCE.lastMenu=this}});if(!Function.prototype.call){Function.prototype.call=function(){var a=arguments,s=a[0],i,as='',r,o;for(i=1;i1?',':'')+'a['+i+']';o=s._fu;s._fu=this;r=eval('s._fu('+as+')');s._fu=o;return r}};TinyMCE_Engine.prototype.debug=function(){var m="",a,i,l=tinyMCE.log.length;for(i=0,a=this.debug.arguments;i'); + + if (tinyMCE.getParam("popups_css_add")) { + c = tinyMCE.getParam("popups_css_add"); + + // Is relative + if (c.indexOf('://') == -1 && c.charAt(0) != '/') + c = tinyMCE.documentBasePath + "/" + c; + + document.write(''); + } + + tinyMCE.addEvent(window, "load", this.onLoad); + }, + + onLoad : function() { + var dir, i, elms, body = document.body; + + if (tinyMCE.getWindowArg('mce_replacevariables', true)) + body.innerHTML = tinyMCE.applyTemplate(body.innerHTML, tinyMCE.windowArgs); + + dir = tinyMCE.selectedInstance.settings['directionality']; + if (dir == "rtl" && document.forms && document.forms.length > 0) { + elms = document.forms[0].elements; + for (i=0; i=0; i--) { + if (wrapper.hasChildNodes()) + wrapper.insertBefore(nodes[i].cloneNode(true), wrapper.firstChild); + else + wrapper.appendChild(nodes[i].cloneNode(true)); + + nodes[i].parentNode.removeChild(nodes[i]); + } + + // Add wrapper + doc.body.appendChild(wrapper); + + // Create iframe + iframe = document.createElement("iframe"); + iframe.id = "mcWinIframe"; + iframe.src = document.location.href.toLowerCase().indexOf('https') == -1 ? "about:blank" : tinyMCE.settings['default_document']; + iframe.width = "100%"; + iframe.height = "100%"; + iframe.style.margin = '0'; + + // Add iframe + doc.body.appendChild(iframe); + + // Measure iframe + iframe = document.getElementById('mcWinIframe'); + dx = tinyMCE.getWindowArg('mce_width') - iframe.clientWidth; + dy = tinyMCE.getWindowArg('mce_height') - iframe.clientHeight; + + // Resize window + // tinyMCE.debug(tinyMCE.getWindowArg('mce_width') + "," + tinyMCE.getWindowArg('mce_height') + " - " + dx + "," + dy); + window.resizeBy(dx, dy); + + // Hide iframe and show wrapper + body.style.margin = oldMargin; + iframe.style.display = 'none'; + wrapper.style.display = 'block'; + } + }, + + resizeToContent : function() { + var isMSIE = (navigator.appName == "Microsoft Internet Explorer"); + var isOpera = (navigator.userAgent.indexOf("Opera") != -1); + + if (isOpera) + return; + + if (isMSIE) { + try { window.resizeTo(10, 10); } catch (e) {} + + var elm = document.body; + var width = elm.offsetWidth; + var height = elm.offsetHeight; + var dx = (elm.scrollWidth - width) + 4; + var dy = elm.scrollHeight - height; + + try { window.resizeBy(dx, dy); } catch (e) {} + } else { + window.scrollBy(1000, 1000); + if (window.scrollX > 0 || window.scrollY > 0) { + window.resizeBy(window.innerWidth * 2, window.innerHeight * 2); + window.sizeToContent(); + window.scrollTo(0, 0); + var x = parseInt(screen.width / 2.0) - (window.outerWidth / 2.0); + var y = parseInt(screen.height / 2.0) - (window.outerHeight / 2.0); + window.moveTo(x, y); + } + } + }, + + getWindowArg : function(name, default_value) { + return tinyMCE.getWindowArg(name, default_value); + }, + + restoreSelection : function() { + if (this.storeSelection) { + var inst = tinyMCE.selectedInstance; + + inst.getWin().focus(); + + if (inst.selectionBookmark) + inst.selection.moveToBookmark(inst.selectionBookmark); + } + }, + + execCommand : function(command, user_interface, value) { + var inst = tinyMCE.selectedInstance; + + this.restoreSelection(); + inst.execCommand(command, user_interface, value); + + // Store selection + if (this.storeSelection) + inst.selectionBookmark = inst.selection.getBookmark(true); + }, + + close : function() { + tinyMCE.closeWindow(window); + }, + + pickColor : function(e, element_id) { + tinyMCE.selectedInstance.execCommand('mceColorPicker', true, { + element_id : element_id, + document : document, + window : window, + store_selection : false + }); + }, + + openBrowser : function(element_id, type, option) { + var cb = tinyMCE.getParam(option, tinyMCE.getParam("file_browser_callback")); + var url = document.getElementById(element_id).value; + + tinyMCE.setWindowArg("window", window); + tinyMCE.setWindowArg("document", document); + + // Call to external callback + if (eval('typeof(tinyMCEPopup.windowOpener.' + cb + ')') == "undefined") + alert("Callback function: " + cb + " could not be found."); + else + eval("tinyMCEPopup.windowOpener." + cb + "(element_id, url, type, window);"); + }, + + importClass : function(c) { + window[c] = function() {}; + + for (var n in window.opener[c].prototype) + window[c].prototype[n] = window.opener[c].prototype[n]; + + window[c].constructor = window.opener[c].constructor; + } + + }; + +// Setup global instance +var tinyMCEPopup = new TinyMCE_Popup(); + +tinyMCEPopup.init(); diff -r 902822492a68 -r fe660c52c48f includes/clientside/tinymce/tiny_mce_src.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/tinymce/tiny_mce_src.js Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,7498 @@ + +/* file:jscripts/tiny_mce/classes/TinyMCE_Engine.class.js */ + +function TinyMCE_Engine() { + var ua; + + this.majorVersion = "2"; + this.minorVersion = "1.0"; + this.releaseDate = "2007-02-13"; + + this.instances = new Array(); + this.switchClassCache = new Array(); + this.windowArgs = new Array(); + this.loadedFiles = new Array(); + this.pendingFiles = new Array(); + this.loadingIndex = 0; + this.configs = new Array(); + this.currentConfig = 0; + this.eventHandlers = new Array(); + this.log = new Array(); + this.undoLevels = []; + this.undoIndex = 0; + this.typingUndoIndex = -1; + + // Browser check + ua = navigator.userAgent; + this.isMSIE = (navigator.appName == "Microsoft Internet Explorer"); + this.isMSIE5 = this.isMSIE && (ua.indexOf('MSIE 5') != -1); + this.isMSIE5_0 = this.isMSIE && (ua.indexOf('MSIE 5.0') != -1); + this.isMSIE7 = this.isMSIE && (ua.indexOf('MSIE 7') != -1); + this.isGecko = ua.indexOf('Gecko') != -1; + this.isSafari = ua.indexOf('Safari') != -1; + this.isOpera = ua.indexOf('Opera') != -1; + this.isMac = ua.indexOf('Mac') != -1; + this.isNS7 = ua.indexOf('Netscape/7') != -1; + this.isNS71 = ua.indexOf('Netscape/7.1') != -1; + this.dialogCounter = 0; + this.plugins = new Array(); + this.themes = new Array(); + this.menus = new Array(); + this.loadedPlugins = new Array(); + this.buttonMap = new Array(); + this.isLoaded = false; + + // Fake MSIE on Opera and if Opera fakes IE, Gecko or Safari cancel those + if (this.isOpera) { + this.isMSIE = true; + this.isGecko = false; + this.isSafari = false; + } + + this.isIE = this.isMSIE; + this.isRealIE = this.isMSIE && !this.isOpera; + + // TinyMCE editor id instance counter + this.idCounter = 0; +}; + +TinyMCE_Engine.prototype = { + init : function(settings) { + var theme, nl, baseHREF = "", i; + + // IE 5.0x is no longer supported since 5.5, 6.0 and 7.0 now exists. We can't support old browsers forever, sorry. + if (this.isMSIE5_0) + return; + + this.settings = settings; + + // Check if valid browser has execcommand support + if (typeof(document.execCommand) == 'undefined') + return; + + // Get script base path + if (!tinyMCE.baseURL) { + var elements = document.getElementsByTagName('script'); + + // If base element found, add that infront of baseURL + nl = document.getElementsByTagName('base'); + for (i=0; i'); + this._def("font_size_classes", ''); + this._def("font_size_style_values", 'xx-small,x-small,small,medium,large,x-large,xx-large', true); + this._def("event_elements", 'a,img', true); + this._def("convert_urls", true); + this._def("table_inline_editing", false); + this._def("object_resizing", true); + this._def("custom_shortcuts", true); + this._def("convert_on_click", false); + this._def("content_css", ''); + this._def("fix_list_elements", true); + this._def("fix_table_elements", false); + this._def("strict_loading_mode", ( ( IE ) ? true : true ) ); // document.contentType == 'application/xhtml+xml'); + this._def("hidden_tab_class", ''); + this._def("display_tab_class", ''); + this._def("gecko_spellcheck", false); + this._def("hide_selects_on_submit", true); + + // Force strict loading mode to false on non Gecko browsers + //if (this.isMSIE && !this.isOpera) + // this.settings.strict_loading_mode = false; + + // Browser check IE + if (this.isMSIE && this.settings['browsers'].indexOf('msie') == -1) + return; + + // Browser check Gecko + if (this.isGecko && this.settings['browsers'].indexOf('gecko') == -1) + return; + + // Browser check Safari + if (this.isSafari && this.settings['browsers'].indexOf('safari') == -1) + return; + + // Browser check Opera + if (this.isOpera && this.settings['browsers'].indexOf('opera') == -1) + return; + + // If not super absolute make it so + baseHREF = tinyMCE.settings['document_base_url']; + var h = document.location.href; + var p = h.indexOf('://'); + if (p > 0 && document.location.protocol != "file:") { + p = h.indexOf('/', p + 3); + h = h.substring(0, p); + + if (baseHREF.indexOf('://') == -1) + baseHREF = h + baseHREF; + + tinyMCE.settings['document_base_url'] = baseHREF; + tinyMCE.settings['document_base_prefix'] = h; + } + + // Trim away query part + if (baseHREF.indexOf('?') != -1) + baseHREF = baseHREF.substring(0, baseHREF.indexOf('?')); + + this.settings['base_href'] = baseHREF.substring(0, baseHREF.lastIndexOf('/')) + "/"; + + theme = this.settings['theme']; + this.inlineStrict = 'A|BR|SPAN|BDO|MAP|OBJECT|IMG|TT|I|B|BIG|SMALL|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|#text|#comment'; + this.inlineTransitional = 'A|BR|SPAN|BDO|OBJECT|APPLET|IMG|MAP|IFRAME|TT|I|B|U|S|STRIKE|BIG|SMALL|FONT|BASEFONT|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|INPUT|SELECT|TEXTAREA|LABEL|BUTTON|#text|#comment'; + this.blockElms = 'H[1-6]|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|FORM|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP'; + this.blockRegExp = new RegExp("^(" + this.blockElms + ")$", "i"); + this.posKeyCodes = new Array(13,45,36,35,33,34,37,38,39,40); + this.uniqueURL = 'javascript:void(091039730);'; // Make unique URL non real URL + this.uniqueTag = ''; + this.callbacks = new Array('onInit', 'getInfo', 'getEditorTemplate', 'setupContent', 'onChange', 'onPageLoad', 'handleNodeChange', 'initInstance', 'execCommand', 'getControlHTML', 'handleEvent', 'cleanup', 'removeInstance'); + + // Theme url + this.settings['theme_href'] = tinyMCE.baseURL + "/themes/" + theme; + + if (!tinyMCE.isIE || tinyMCE.isOpera) + this.settings['force_br_newlines'] = false; + + if (tinyMCE.getParam("popups_css", false)) { + var cssPath = tinyMCE.getParam("popups_css", ""); + + // Is relative + if (cssPath.indexOf('://') == -1 && cssPath.charAt(0) != '/') + this.settings['popups_css'] = this.documentBasePath + "/" + cssPath; + else + this.settings['popups_css'] = cssPath; + } else + this.settings['popups_css'] = tinyMCE.baseURL + "/themes/" + theme + "/css/editor_popup.css"; + + if (tinyMCE.getParam("editor_css", false)) { + var cssPath = tinyMCE.getParam("editor_css", ""); + + // Is relative + if (cssPath.indexOf('://') == -1 && cssPath.charAt(0) != '/') + this.settings['editor_css'] = this.documentBasePath + "/" + cssPath; + else + this.settings['editor_css'] = cssPath; + } else { + if (this.settings.editor_css != '') + this.settings['editor_css'] = tinyMCE.baseURL + "/themes/" + theme + "/css/editor_ui.css"; + } + + if (tinyMCE.settings['debug']) { + var msg = "Debug: \n"; + + msg += "baseURL: " + this.baseURL + "\n"; + msg += "documentBasePath: " + this.documentBasePath + "\n"; + msg += "content_css: " + this.settings['content_css'] + "\n"; + msg += "popups_css: " + this.settings['popups_css'] + "\n"; + msg += "editor_css: " + this.settings['editor_css'] + "\n"; + + alert(msg); + } + + // Only do this once + if (this.configs.length == 0) { + if (typeof(TinyMCECompressed) == "undefined") { + tinyMCE.addEvent(window, "DOMContentLoaded", TinyMCE_Engine.prototype.onLoad); + + if (tinyMCE.isRealIE) { + if (document.body) + tinyMCE.addEvent(document.body, "readystatechange", TinyMCE_Engine.prototype.onLoad); + else + tinyMCE.addEvent(document, "readystatechange", TinyMCE_Engine.prototype.onLoad); + } + + tinyMCE.addEvent(window, "load", TinyMCE_Engine.prototype.onLoad); + tinyMCE._addUnloadEvents(); + } + } + + this.loadScript(tinyMCE.baseURL + '/themes/' + this.settings['theme'] + '/editor_template' + tinyMCE.srcMode + '.js'); + this.loadScript(tinyMCE.baseURL + '/langs/' + this.settings['language'] + '.js'); + this.loadCSS(this.settings['editor_css']); + + // Add plugins + var p = tinyMCE.getParam('plugins', '', true, ','); + if (p.length > 0) { + for (var i=0; i&"\']', 'g'); + this.xmlEncodeRe = new RegExp('[<>&"]', 'g'); +// this.xmlEncodeEnts = {'&':'&','"':'"',"'":''','<':'<','>':'>'}; + }, + + _addUnloadEvents : function() { + if (tinyMCE.isIE) { + if (tinyMCE.settings['add_unload_trigger']) { + tinyMCE.addEvent(window, "unload", TinyMCE_Engine.prototype.unloadHandler); + tinyMCE.addEvent(window.document, "beforeunload", TinyMCE_Engine.prototype.unloadHandler); + } + } else { + if (tinyMCE.settings['add_unload_trigger']) + tinyMCE.addEvent(window, "unload", function () {tinyMCE.triggerSave(true, true);}); + } + }, + + _def : function(key, def_val, t) { + var v = tinyMCE.getParam(key, def_val); + + v = t ? v.replace(/\s+/g, "") : v; + + this.settings[key] = v; + }, + + hasPlugin : function(n) { + return typeof(this.plugins[n]) != "undefined" && this.plugins[n] != null; + }, + + addPlugin : function(n, p) { + var op = this.plugins[n]; + + // Use the previous plugin object base URL used when loading external plugins + p.baseURL = op ? op.baseURL : tinyMCE.baseURL + "/plugins/" + n; + this.plugins[n] = p; + + this.loadNextScript(); + }, + + setPluginBaseURL : function(n, u) { + var op = this.plugins[n]; + + if (op) + op.baseURL = u; + else + this.plugins[n] = {baseURL : u}; + }, + + loadPlugin : function(n, u) { + u = u.indexOf('.js') != -1 ? u.substring(0, u.lastIndexOf('/')) : u; + u = u.charAt(u.length-1) == '/' ? u.substring(0, u.length-1) : u; + this.plugins[n] = {baseURL : u}; + this.loadScript(u + "/editor_plugin" + (tinyMCE.srcMode ? '_src' : '') + ".js"); + }, + + hasTheme : function(n) { + return typeof(this.themes[n]) != "undefined" && this.themes[n] != null; + }, + + addTheme : function(n, t) { + this.themes[n] = t; + + this.loadNextScript(); + }, + + addMenu : function(n, m) { + this.menus[n] = m; + }, + + hasMenu : function(n) { + return typeof(this.plugins[n]) != "undefined" && this.plugins[n] != null; + }, + + loadScript : function(url) { + var i; + + for (i=0; i'); + } + + this.loadedFiles[this.loadedFiles.length] = url; + }, + + loadNextScript : function() { + var d = document, se; + + if (!tinyMCE.settings.strict_loading_mode) + return; + + if (this.loadingIndex < this.pendingFiles.length) { + se = d.createElementNS('http://www.w3.org/1999/xhtml', 'script'); + se.setAttribute('language', 'javascript'); + se.setAttribute('type', 'text/javascript'); + se.setAttribute('src', this.pendingFiles[this.loadingIndex++]); + + d.getElementsByTagName("head")[0].appendChild(se); + } else + this.loadingIndex = -1; // Done with loading + }, + + loadCSS : function(url) { + var ar = url.replace(/\s+/, '').split(','); + var lflen = 0, csslen = 0; + var skip = false; + var x = 0, i = 0, nl, le; + + for (x = 0,csslen = ar.length; x 0) { + /* Make sure it doesn't exist. */ + for (i=0, lflen=this.loadedFiles.length; i'); + } + + this.loadedFiles[this.loadedFiles.length] = ar[x]; + } + } + } + }, + + importCSS : function(doc, css) { + var css_ary = css.replace(/\s+/, '').split(','); + var csslen, elm, headArr, x, css_file; + + for (x = 0, csslen = css_ary.length; x 0) { + // Is relative, make absolute + if (css_file.indexOf('://') == -1 && css_file.charAt(0) != '/') + css_file = this.documentBasePath + "/" + css_file; + + if (typeof(doc.createStyleSheet) == "undefined") { + elm = doc.createElement("link"); + + elm.rel = "stylesheet"; + elm.href = css_file; + + if ((headArr = doc.getElementsByTagName("head")) != null && headArr.length > 0) + headArr[0].appendChild(elm); + } else + doc.createStyleSheet(css_file); + } + } + }, + + confirmAdd : function(e, settings) { + var elm = tinyMCE.isIE ? event.srcElement : e.target; + var elementId = elm.name ? elm.name : elm.id; + + tinyMCE.settings = settings; + + if (tinyMCE.settings['convert_on_click'] || (!elm.getAttribute('mce_noask') && confirm(tinyMCELang['lang_edit_confirm']))) + tinyMCE.addMCEControl(elm, elementId); + + elm.setAttribute('mce_noask', 'true'); + }, + + updateContent : function(form_element_name) { + // Find MCE instance linked to given form element and copy it's value + var formElement = document.getElementById(form_element_name); + for (var n in tinyMCE.instances) { + var inst = tinyMCE.instances[n]; + if (!tinyMCE.isInstance(inst)) + continue; + + inst.switchSettings(); + + if (inst.formElement == formElement) { + var doc = inst.getDoc(); + + tinyMCE._setHTML(doc, inst.formElement.value); + + if (!tinyMCE.isIE) + doc.body.innerHTML = tinyMCE._cleanupHTML(inst, doc, this.settings, doc.body, inst.visualAid); + } + } + }, + + addMCEControl : function(replace_element, form_element_name, target_document) { + var id = "mce_editor_" + tinyMCE.idCounter++; + var inst = new TinyMCE_Control(tinyMCE.settings); + + inst.editorId = id; + this.instances[id] = inst; + + inst._onAdd(replace_element, form_element_name, target_document); + }, + + removeInstance : function(ti) { + var t = [], n, i; + + // Remove from instances + for (n in tinyMCE.instances) { + i = tinyMCE.instances[n]; + + if (tinyMCE.isInstance(i) && ti != i) + t[n] = i; + } + + tinyMCE.instances = t; + + // Remove from global undo/redo + n = []; + t = tinyMCE.undoLevels; + + for (i=0; i 0) { + tinyMCE.nextUndoRedoAction = 'Undo'; + inst = this.undoLevels[--this.undoIndex]; + inst.select(); + + if (!tinyMCE.nextUndoRedoInstanceId) + inst.execCommand('Undo'); + } + } else + inst.execCommand('Undo'); + return true; + + case "Redo": + if (this.getParam('custom_undo_redo_global')) { + if (this.undoIndex <= this.undoLevels.length - 1) { + tinyMCE.nextUndoRedoAction = 'Redo'; + inst = this.undoLevels[this.undoIndex++]; + inst.select(); + + if (!tinyMCE.nextUndoRedoInstanceId) + inst.execCommand('Redo'); + } + } else + inst.execCommand('Redo'); + + return true; + + case 'mceFocus': + var inst = tinyMCE.getInstanceById(value); + if (inst) + inst.getWin().focus(); + return; + + case "mceAddControl": + case "mceAddEditor": + tinyMCE.addMCEControl(tinyMCE._getElementById(value), value); + return; + + case "mceAddFrameControl": + tinyMCE.addMCEControl(tinyMCE._getElementById(value['element'], value['document']), value['element'], value['document']); + return; + + case "mceRemoveControl": + case "mceRemoveEditor": + tinyMCE.removeMCEControl(value); + return; + + case "mceToggleEditor": + var inst = tinyMCE.getInstanceById(value), pe, te; + + if (inst) { + pe = document.getElementById(inst.editorId + '_parent'); + te = inst.oldTargetElement; + + if (typeof(inst.enabled) == 'undefined') + inst.enabled = true; + + inst.enabled = !inst.enabled; + + if (!inst.enabled) { + pe.style.display = 'none'; + te.value = inst.getHTML(); + te.style.display = inst.oldTargetDisplay; + tinyMCE.dispatchCallback(inst, 'hide_instance_callback', 'hideInstance', inst); + } else { + pe.style.display = 'block'; + te.style.display = 'none'; + inst.setHTML(te.value); + inst.useCSS = false; + tinyMCE.dispatchCallback(inst, 'show_instance_callback', 'showInstance', inst); + } + } else + tinyMCE.addMCEControl(tinyMCE._getElementById(value), value); + + return; + + case "mceResetDesignMode": + // Resets the designmode state of the editors in Gecko + if (!tinyMCE.isIE) { + for (var n in tinyMCE.instances) { + if (!tinyMCE.isInstance(tinyMCE.instances[n])) + continue; + + try { + tinyMCE.instances[n].getDoc().designMode = "on"; + } catch (e) { + // Ignore any errors + } + } + } + + return; + } + + if (inst) { + inst.execCommand(command, user_interface, value); + } else if (tinyMCE.settings['focus_alert']) + alert(tinyMCELang['lang_focus_alert']); + }, + + _createIFrame : function(replace_element, doc, win) { + var iframe, id = replace_element.getAttribute("id"); + var aw, ah; + + if (typeof(doc) == "undefined") + doc = document; + + if (typeof(win) == "undefined") + win = window; + + iframe = doc.createElement("iframe"); + + aw = "" + tinyMCE.settings['area_width']; + ah = "" + tinyMCE.settings['area_height']; + + if (aw.indexOf('%') == -1) { + aw = parseInt(aw); + aw = (isNaN(aw) || aw < 0) ? 300 : aw; + aw = aw + "px"; + } + + if (ah.indexOf('%') == -1) { + ah = parseInt(ah); + ah = (isNaN(ah) || ah < 0) ? 240 : ah; + ah = ah + "px"; + } + + iframe.setAttribute("id", id); + iframe.setAttribute("name", id); + iframe.setAttribute("class", "mceEditorIframe"); + iframe.setAttribute("border", "0"); + iframe.setAttribute("frameBorder", "0"); + iframe.setAttribute("marginWidth", "0"); + iframe.setAttribute("marginHeight", "0"); + iframe.setAttribute("leftMargin", "0"); + iframe.setAttribute("topMargin", "0"); + iframe.setAttribute("width", aw); + iframe.setAttribute("height", ah); + iframe.setAttribute("allowtransparency", "true"); + iframe.className = 'mceEditorIframe'; + + if (tinyMCE.settings["auto_resize"]) + iframe.setAttribute("scrolling", "no"); + + // Must have a src element in MSIE HTTPs breaks aswell as absoute URLs + if (tinyMCE.isRealIE) + iframe.setAttribute("src", this.settings['default_document']); + + iframe.style.width = aw; + iframe.style.height = ah; + + // Ugly hack for Gecko problem in strict mode + if (tinyMCE.settings.strict_loading_mode) + iframe.style.marginBottom = '-5px'; + + // MSIE 5.0 issue + if (tinyMCE.isRealIE) + replace_element.outerHTML = iframe.outerHTML; + else + replace_element.parentNode.replaceChild(iframe, replace_element); + + if (tinyMCE.isRealIE) + return win.frames[id]; + else + return iframe; + }, + + setupContent : function(editor_id) { + var inst = tinyMCE.instances[editor_id], i; + var doc = inst.getDoc(); + var head = doc.getElementsByTagName('head').item(0); + var content = inst.startContent; + + // HTML values get XML encoded in strict mode + if (tinyMCE.settings.strict_loading_mode) { + content = content.replace(/</g, '<'); + content = content.replace(/>/g, '>'); + content = content.replace(/"/g, '"'); + content = content.replace(/&/g, '&'); + } + + tinyMCE.selectedInstance = inst; + inst.switchSettings(); + + // Not loaded correctly hit it again, Mozilla bug #997860 + if (!tinyMCE.isIE && tinyMCE.getParam("setupcontent_reload", false) && doc.title != "blank_page") { + // This part will remove the designMode status + // Failes first time in Firefox 1.5b2 on Mac + try {doc.location.href = tinyMCE.baseURL + "/blank.htm";} catch (ex) {} + window.setTimeout("tinyMCE.setupContent('" + editor_id + "');", 1000); + return; + } + + if (!head) { + window.setTimeout("tinyMCE.setupContent('" + editor_id + "');", 10); + return; + } + + // Import theme specific content CSS the user specific + tinyMCE.importCSS(inst.getDoc(), tinyMCE.baseURL + "/themes/" + inst.settings['theme'] + "/css/editor_content.css"); + tinyMCE.importCSS(inst.getDoc(), inst.settings['content_css']); + tinyMCE.dispatchCallback(inst, 'init_instance_callback', 'initInstance', inst); + + // Setup keyboard shortcuts + if (tinyMCE.getParam('custom_undo_redo_keyboard_shortcuts')) { + inst.addShortcut('ctrl', 'z', 'lang_undo_desc', 'Undo'); + inst.addShortcut('ctrl', 'y', 'lang_redo_desc', 'Redo'); + } + + // BlockFormat shortcuts keys + for (i=1; i<=6; i++) + inst.addShortcut('ctrl', '' + i, '', 'FormatBlock', false, ''); + + inst.addShortcut('ctrl', '7', '', 'FormatBlock', false, '

'); + inst.addShortcut('ctrl', '8', '', 'FormatBlock', false, '

'); + inst.addShortcut('ctrl', '9', '', 'FormatBlock', false, '
'); + + // Add default shortcuts for gecko + if (tinyMCE.isGecko) { + inst.addShortcut('ctrl', 'b', 'lang_bold_desc', 'Bold'); + inst.addShortcut('ctrl', 'i', 'lang_italic_desc', 'Italic'); + inst.addShortcut('ctrl', 'u', 'lang_underline_desc', 'Underline'); + } + + // Setup span styles + if (tinyMCE.getParam("convert_fonts_to_spans")) + inst.getBody().setAttribute('id', 'mceSpanFonts'); + + if (tinyMCE.settings['nowrap']) + doc.body.style.whiteSpace = "nowrap"; + + doc.body.dir = this.settings['directionality']; + doc.editorId = editor_id; + + // Add on document element in Mozilla + if (!tinyMCE.isIE) + doc.documentElement.editorId = editor_id; + + inst.setBaseHREF(tinyMCE.settings['base_href']); + + // Replace new line characters to BRs + if (tinyMCE.settings['convert_newlines_to_brs']) { + content = tinyMCE.regexpReplace(content, "\r\n", "
", "gi"); + content = tinyMCE.regexpReplace(content, "\r", "
", "gi"); + content = tinyMCE.regexpReplace(content, "\n", "
", "gi"); + } + + // Open closed anchors + // content = content.replace(new RegExp('', 'gi'), ''); + + // Call custom cleanup code + content = tinyMCE.storeAwayURLs(content); + content = tinyMCE._customCleanup(inst, "insert_to_editor", content); + + if (tinyMCE.isIE) { + // Ugly!!! + window.setInterval('try{tinyMCE.getCSSClasses(tinyMCE.instances["' + editor_id + '"].getDoc(), "' + editor_id + '");}catch(e){}', 500); + + if (tinyMCE.settings["force_br_newlines"]) + doc.styleSheets[0].addRule("p", "margin: 0;"); + + var body = inst.getBody(); + body.editorId = editor_id; + } + + content = tinyMCE.cleanupHTMLCode(content); + + // Fix for bug #958637 + if (!tinyMCE.isIE) { + var contentElement = inst.getDoc().createElement("body"); + var doc = inst.getDoc(); + + contentElement.innerHTML = content; + + // Remove weridness! + if (tinyMCE.isGecko && tinyMCE.settings['remove_lt_gt']) + content = content.replace(new RegExp('<>', 'g'), ""); + + if (tinyMCE.settings['cleanup_on_startup']) + tinyMCE.setInnerHTML(inst.getBody(), tinyMCE._cleanupHTML(inst, doc, this.settings, contentElement)); + else + tinyMCE.setInnerHTML(inst.getBody(), content); + + tinyMCE.convertAllRelativeURLs(inst.getBody()); + } else { + if (tinyMCE.settings['cleanup_on_startup']) { + tinyMCE._setHTML(inst.getDoc(), content); + + // Produces permission denied error in MSIE 5.5 + eval('try {tinyMCE.setInnerHTML(inst.getBody(), tinyMCE._cleanupHTML(inst, inst.contentDocument, this.settings, inst.getBody()));} catch(e) {}'); + } else + tinyMCE._setHTML(inst.getDoc(), content); + } + + // Fix for bug #957681 + //inst.getDoc().designMode = inst.getDoc().designMode; + + tinyMCE.handleVisualAid(inst.getBody(), true, tinyMCE.settings['visual'], inst); + tinyMCE.dispatchCallback(inst, 'setupcontent_callback', 'setupContent', editor_id, inst.getBody(), inst.getDoc()); + + // Re-add design mode on mozilla + if (!tinyMCE.isIE) + tinyMCE.addEventHandlers(inst); + + // Add blur handler + if (tinyMCE.isIE) { + tinyMCE.addEvent(inst.getBody(), "blur", TinyMCE_Engine.prototype._eventPatch); + tinyMCE.addEvent(inst.getBody(), "beforedeactivate", TinyMCE_Engine.prototype._eventPatch); // Bug #1439953 + + // Workaround for drag drop/copy paste base href bug + if (!tinyMCE.isOpera) { + tinyMCE.addEvent(doc.body, "mousemove", TinyMCE_Engine.prototype.onMouseMove); + tinyMCE.addEvent(doc.body, "beforepaste", TinyMCE_Engine.prototype._eventPatch); + tinyMCE.addEvent(doc.body, "drop", TinyMCE_Engine.prototype._eventPatch); + } + } + + // Trigger node change, this call locks buttons for tables and so forth + inst.select(); + tinyMCE.selectedElement = inst.contentWindow.document.body; + + // Call custom DOM cleanup + tinyMCE._customCleanup(inst, "insert_to_editor_dom", inst.getBody()); + tinyMCE._customCleanup(inst, "setup_content_dom", inst.getBody()); + tinyMCE._setEventsEnabled(inst.getBody(), false); + tinyMCE.cleanupAnchors(inst.getDoc()); + + if (tinyMCE.getParam("convert_fonts_to_spans")) + tinyMCE.convertSpansToFonts(inst.getDoc()); + + inst.startContent = tinyMCE.trim(inst.getBody().innerHTML); + inst.undoRedo.add({ content : inst.startContent }); + + // Cleanup any mess left from storyAwayURLs + if (tinyMCE.isGecko) { + // Remove mce_src from textnodes and comments + tinyMCE.selectNodes(inst.getBody(), function(n) { + if (n.nodeType == 3 || n.nodeType == 8) + n.nodeValue = n.nodeValue.replace(new RegExp('\\s(mce_src|mce_href)=\"[^\"]*\"', 'gi'), ""); + + return false; + }); + } + + // Remove Gecko spellchecking + if (tinyMCE.isGecko) + inst.getBody().spellcheck = tinyMCE.getParam("gecko_spellcheck"); + + // Cleanup any mess left from storyAwayURLs + tinyMCE._removeInternal(inst.getBody()); + + inst.select(); + tinyMCE.triggerNodeChange(false, true); + }, + + storeAwayURLs : function(s) { + // Remove all mce_src, mce_href and replace them with new ones + // s = s.replace(new RegExp('mce_src\\s*=\\s*\"[^ >\"]*\"', 'gi'), ''); + // s = s.replace(new RegExp('mce_href\\s*=\\s*\"[^ >\"]*\"', 'gi'), ''); + + if (!s.match(/(mce_src|mce_href)/gi, s)) { + s = s.replace(new RegExp('src\\s*=\\s*\"([^ >\"]*)\"', 'gi'), 'src="$1" mce_src="$1"'); + s = s.replace(new RegExp('href\\s*=\\s*\"([^ >\"]*)\"', 'gi'), 'href="$1" mce_href="$1"'); + } + + return s; + }, + + _removeInternal : function(n) { + if (tinyMCE.isGecko) { + // Remove mce_src from textnodes and comments + tinyMCE.selectNodes(n, function(n) { + if (n.nodeType == 3 || n.nodeType == 8) + n.nodeValue = n.nodeValue.replace(new RegExp('\\s(mce_src|mce_href)=\"[^\"]*\"', 'gi'), ""); + + return false; + }); + } + }, + + removeTinyMCEFormElements : function(form_obj) { + var i, elementId; + + // Skip form element removal + if (!tinyMCE.getParam('hide_selects_on_submit')) + return; + + // Check if form is valid + if (typeof(form_obj) == "undefined" || form_obj == null) + return; + + // If not a form, find the form + if (form_obj.nodeName != "FORM") { + if (form_obj.form) + form_obj = form_obj.form; + else + form_obj = tinyMCE.getParentElement(form_obj, "form"); + } + + // Still nothing + if (form_obj == null) + return; + + // Disable all UI form elements that TinyMCE created + for (i=0; i"); + rng.collapse(false); + rng.select(); + + tinyMCE.execCommand("mceAddUndoLevel"); + tinyMCE.triggerNodeChange(false); + return false; + } + } + + // Backspace or delete + if (e.keyCode == 8 || e.keyCode == 46) { + tinyMCE.selectedElement = e.target; + tinyMCE.linkElement = tinyMCE.getParentElement(e.target, "a"); + tinyMCE.imgElement = tinyMCE.getParentElement(e.target, "img"); + tinyMCE.triggerNodeChange(false); + } + + return false; + break; + + case "keyup": + case "keydown": + tinyMCE.hideMenus(); + tinyMCE.hasMouseMoved = false; + + if (inst && inst.handleShortcut(e)) + return false; + + if (e.target.editorId) + tinyMCE.instances[e.target.editorId].select(); + + if (tinyMCE.selectedInstance) + tinyMCE.selectedInstance.switchSettings(); + + var inst = tinyMCE.selectedInstance; + + // Handle backspace + if (tinyMCE.isGecko && tinyMCE.settings['force_p_newlines'] && (e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey) { + // Insert P element instead of BR + if (TinyMCE_ForceParagraphs._handleBackSpace(tinyMCE.selectedInstance, e.type)) { + // Cancel event + tinyMCE.execCommand("mceAddUndoLevel"); + e.preventDefault(); + return false; + } + } + + tinyMCE.selectedElement = null; + tinyMCE.selectedNode = null; + var elm = tinyMCE.selectedInstance.getFocusElement(); + tinyMCE.linkElement = tinyMCE.getParentElement(elm, "a"); + tinyMCE.imgElement = tinyMCE.getParentElement(elm, "img"); + tinyMCE.selectedElement = elm; + + // Update visualaids on tabs + if (tinyMCE.isGecko && e.type == "keyup" && e.keyCode == 9) + tinyMCE.handleVisualAid(tinyMCE.selectedInstance.getBody(), true, tinyMCE.settings['visual'], tinyMCE.selectedInstance); + + // Fix empty elements on return/enter, check where enter occured + if (tinyMCE.isIE && e.type == "keydown" && e.keyCode == 13) + tinyMCE.enterKeyElement = tinyMCE.selectedInstance.getFocusElement(); + + // Fix empty elements on return/enter + if (tinyMCE.isIE && e.type == "keyup" && e.keyCode == 13) { + var elm = tinyMCE.enterKeyElement; + if (elm) { + var re = new RegExp('^HR|IMG|BR$','g'); // Skip these + var dre = new RegExp('^H[1-6]$','g'); // Add double on these + + if (!elm.hasChildNodes() && !re.test(elm.nodeName)) { + if (dre.test(elm.nodeName)) + elm.innerHTML = "  "; + else + elm.innerHTML = " "; + } + } + } + + // Check if it's a position key + var keys = tinyMCE.posKeyCodes; + var posKey = false; + for (var i=0; i'; + h += ''; + h += ''; + } else { + // Normal button + h += ''; + h += ''; + h += ''; + } + + return h; + }, + + getMenuButtonHTML : function(id, lang, img, mcmd, cmd, ui, val) { + var h = '', m, x; + + mcmd = 'tinyMCE.execInstanceCommand(\'{$editor_id}\',\'' + mcmd + '\');'; + cmd = 'tinyMCE.execInstanceCommand(\'{$editor_id}\',\'' + cmd + '\''; + + if (typeof(ui) != "undefined" && ui != null) + cmd += ',' + ui; + + if (typeof(val) != "undefined" && val != null) + cmd += ",'" + val + "'"; + + cmd += ');'; + + // Use tilemaps when enabled and found and never in MSIE since it loads the tile each time from cache if cahce is disabled + if (tinyMCE.getParam('button_tile_map') && (!tinyMCE.isIE || tinyMCE.isOpera) && (m = tinyMCE.buttonMap[id]) != null && (tinyMCE.getParam("language") == "en" || img.indexOf('$lang') == -1)) { + x = 0 - (m * 20) == 0 ? '0' : 0 - (m * 20); + + if (tinyMCE.isRealIE) + h += ''; + else + h += ''; + + h += ''; + h += ''; + h += ''; + h += ''; + } else { + if (tinyMCE.isRealIE) + h += ''; + else + h += ''; + + h += ''; + h += ''; + h += ''; + h += ''; + } + + return h; + }, + + _menuButtonEvent : function(e, o) { + if (o.className == 'mceMenuButtonFocus') + return; + + if (e == 'over') + o.className = o.className + ' mceMenuHover'; + else + o.className = o.className.replace(/\s.*$/, ''); + }, + + addButtonMap : function(m) { + var i, a = m.replace(/\s+/, '').split(','); + + for (i=0; i 0); + + if (tinyMCE.settings['custom_undo_redo']) { + undoIndex = inst.undoRedo.undoIndex; + undoLevels = inst.undoRedo.undoLevels.length; + } + + tinyMCE.dispatchCallback(inst, 'handle_node_change_callback', 'handleNodeChange', editorId, elm, undoIndex, undoLevels, inst.visualAid, anySelection, setup_content); + } + + if (this.selectedInstance && (typeof(focus) == "undefined" || focus)) + this.selectedInstance.contentWindow.focus(); + }, + + _customCleanup : function(inst, type, content) { + var pl, po, i; + + // Call custom cleanup + var customCleanup = tinyMCE.settings['cleanup_callback']; + if (customCleanup != "" && eval("typeof(" + customCleanup + ")") != "undefined") + content = eval(customCleanup + "(type, content, inst);"); + + // Trigger theme cleanup + po = tinyMCE.themes[tinyMCE.settings['theme']]; + if (po && po.cleanup) + content = po.cleanup(type, content, inst); + + // Trigger plugin cleanups + pl = inst.plugins; + for (i=0; i 0) + className += " "; + + className += classNames[i]; + } + + return className; + }, + + handleVisualAid : function(el, deep, state, inst, skip_dispatch) { + if (!el) + return; + + if (!skip_dispatch) + tinyMCE.dispatchCallback(inst, 'handle_visual_aid_callback', 'handleVisualAid', el, deep, state, inst); + + var tableElement = null; + + switch (el.nodeName) { + case "TABLE": + var oldW = el.style.width; + var oldH = el.style.height; + var bo = tinyMCE.getAttrib(el, "border"); + + bo = bo == "" || bo == "0" ? true : false; + + tinyMCE.setAttrib(el, "class", tinyMCE.getVisualAidClass(tinyMCE.getAttrib(el, "class"), state && bo)); + + el.style.width = oldW; + el.style.height = oldH; + + for (var y=0; y<\/o:p>", "
"); + html = tinyMCE.regexpReplace(html, " <\/o:p>", ""); + html = tinyMCE.regexpReplace(html, "", ""); + html = tinyMCE.regexpReplace(html, "

<\/p>", ""); + html = tinyMCE.regexpReplace(html, "

<\/p>\r\n

<\/p>", ""); + html = tinyMCE.regexpReplace(html, "

 <\/p>", "
"); + html = tinyMCE.regexpReplace(html, "

\s*(

\s*)?", "

"); + html = tinyMCE.regexpReplace(html, "<\/p>\s*(<\/p>\s*)?", "

"); + }*/ + + // Always set the htmlText output + tinyMCE.setInnerHTML(doc.body, html); + } + + tinyMCE.cleanupAnchors(doc); + + if (tinyMCE.getParam("convert_fonts_to_spans")) + tinyMCE.convertSpansToFonts(doc); + }, + + getEditorId : function(form_element) { + var inst = this.getInstanceById(form_element); + if (!inst) + return null; + + return inst.editorId; + }, + + getInstanceById : function(editor_id) { + var inst = this.instances[editor_id]; + if (!inst) { + for (var n in tinyMCE.instances) { + var instance = tinyMCE.instances[n]; + if (!tinyMCE.isInstance(instance)) + continue; + + if (instance.formTargetElementId == editor_id) { + inst = instance; + break; + } + } + } + + return inst; + }, + + queryInstanceCommandValue : function(editor_id, command) { + var inst = tinyMCE.getInstanceById(editor_id); + if (inst) + return inst.queryCommandValue(command); + + return false; + }, + + queryInstanceCommandState : function(editor_id, command) { + var inst = tinyMCE.getInstanceById(editor_id); + if (inst) + return inst.queryCommandState(command); + + return null; + }, + + setWindowArg : function(n, v) { + this.windowArgs[n] = v; + }, + + getWindowArg : function(n, d) { + return (typeof(this.windowArgs[n]) == "undefined") ? d : this.windowArgs[n]; + }, + + getCSSClasses : function(editor_id, doc) { + var inst = tinyMCE.getInstanceById(editor_id); + + // Is cached, use that + if (inst && inst.cssClasses.length > 0) + return inst.cssClasses; + + if (typeof(editor_id) == "undefined" && typeof(doc) == "undefined") { + var instance; + + for (var instanceName in tinyMCE.instances) { + instance = tinyMCE.instances[instanceName]; + if (!tinyMCE.isInstance(instance)) + continue; + + break; + } + + doc = instance.getDoc(); + } + + if (typeof(doc) == "undefined") { + var instance = tinyMCE.getInstanceById(editor_id); + doc = instance.getDoc(); + } + + if (doc) { + var styles = doc.styleSheets; + + if (styles && styles.length > 0) { + for (var x=0; x' + tinyMCE.replaceVar(v, "pluginurl", o.baseURL) + '
'; + + return tinyMCE.replaceVar(v, "pluginurl", o.baseURL); + } + } + + o = tinyMCE.themes[tinyMCE.settings['theme']]; + if (o.getControlHTML && (v = o.getControlHTML(c)) != '') { + if (rtl) + return '' + v + ''; + + return v; + } + + return ''; + }, + + evalFunc : function(f, idx, a, o) { + o = !o ? window : o; + f = typeof(f) == 'function' ? f : o[f]; + + return f.apply(o, Array.prototype.slice.call(a, idx)); + }, + + dispatchCallback : function(i, p, n) { + return this.callFunc(i, p, n, 0, this.dispatchCallback.arguments); + }, + + executeCallback : function(i, p, n) { + return this.callFunc(i, p, n, 1, this.executeCallback.arguments); + }, + + execCommandCallback : function(i, p, n) { + return this.callFunc(i, p, n, 2, this.execCommandCallback.arguments); + }, + + callFunc : function(ins, p, n, m, a) { + var l, i, on, o, s, v; + + s = m == 2; + + l = tinyMCE.getParam(p, ''); + + if (l != '' && (v = tinyMCE.evalFunc(l, 3, a)) == s && m > 0) + return true; + + if (ins != null) { + for (i=0, l = ins.plugins; i 0) + return true; + } + } + + l = tinyMCE.themes; + for (on in l) { + o = l[on]; + + if (o[n] && (v = tinyMCE.evalFunc(n, 3, a, o)) == s && m > 0) + return true; + } + + return false; + }, + + xmlEncode : function(s, skip_apos) { + return s ? ('' + s).replace(!skip_apos ? this.xmlEncodeAposRe : this.xmlEncodeRe, function (c, b) { + switch (c) { + case '&': + return '&'; + + case '"': + return '"'; + + case '\'': + return '''; // ' is not working in MSIE + + case '<': + return '<'; + + case '>': + return '>'; + } + + return c; + }) : s; + }, + + extend : function(p, np) { + var o = {}; + + o.parent = p; + + for (n in p) + o[n] = p[n]; + + for (n in np) + o[n] = np[n]; + + return o; + }, + + hideMenus : function() { + var e = tinyMCE.lastSelectedMenuBtn; + + if (tinyMCE.lastMenu) { + tinyMCE.lastMenu.hide(); + tinyMCE.lastMenu = null; + } + + if (e) { + tinyMCE.switchClass(e, tinyMCE.lastMenuBtnClass); + tinyMCE.lastSelectedMenuBtn = null; + } + } + + }; + +// Global instances +var TinyMCE = TinyMCE_Engine; // Compatiblity with gzip compressors +var tinyMCE = new TinyMCE_Engine(); +var tinyMCELang = {}; + +/* file:jscripts/tiny_mce/classes/TinyMCE_Control.class.js */ + +function TinyMCE_Control(settings) { + var t, i, to, fu, p, x, fn, fu, pn, s = settings; + + this.undoRedoLevel = true; + this.isTinyMCE_Control = true; + + // Default settings + this.settings = s; + this.settings['theme'] = tinyMCE.getParam("theme", "default"); + this.settings['width'] = tinyMCE.getParam("width", -1); + this.settings['height'] = tinyMCE.getParam("height", -1); + this.selection = new TinyMCE_Selection(this); + this.undoRedo = new TinyMCE_UndoRedo(this); + this.cleanup = new TinyMCE_Cleanup(); + this.shortcuts = new Array(); + this.hasMouseMoved = false; + this.foreColor = this.backColor = "#999999"; + this.data = {}; + this.cssClasses = []; + + this.cleanup.init({ + valid_elements : s.valid_elements, + extended_valid_elements : s.extended_valid_elements, + valid_child_elements : s.valid_child_elements, + entities : s.entities, + entity_encoding : s.entity_encoding, + debug : s.cleanup_debug, + indent : s.apply_source_formatting, + invalid_elements : s.invalid_elements, + verify_html : s.verify_html, + fix_content_duplication : s.fix_content_duplication, + convert_fonts_to_spans : s.convert_fonts_to_spans + }); + + // Wrap old theme + t = this.settings['theme']; + if (!tinyMCE.hasTheme(t)) { + fn = tinyMCE.callbacks; + to = {}; + + for (i=0; i 0) { + for (i=0; i 1 && tinyMCE.currentConfig != this.settings['index']) { + tinyMCE.settings = this.settings; + tinyMCE.currentConfig = this.settings['index']; + } + }, + + select : function() { + var oldInst = tinyMCE.selectedInstance; + + if (oldInst != this) { + if (oldInst) + oldInst.execCommand('mceEndTyping'); + + tinyMCE.dispatchCallback(this, 'select_instance_callback', 'selectInstance', this, oldInst); + tinyMCE.selectedInstance = this; + } + }, + + getBody : function() { + return this.contentBody ? this.contentBody : this.getDoc().body; + }, + + getDoc : function() { +// return this.contentDocument ? this.contentDocument : this.contentWindow.document; // Removed due to IE 5.5 ? + return this.contentWindow.document; + }, + + getWin : function() { + return this.contentWindow; + }, + + getContainerWin : function() { + return this.containerWindow ? this.containerWindow : window; + }, + + getViewPort : function() { + return tinyMCE.getViewPort(this.getWin()); + }, + + getParentNode : function(n, f) { + return tinyMCE.getParentNode(n, f, this.getBody()); + }, + + getParentElement : function(n, na, f) { + return tinyMCE.getParentElement(n, na, f, this.getBody()); + }, + + getParentBlockElement : function(n) { + return tinyMCE.getParentBlockElement(n, this.getBody()); + }, + + resizeToContent : function() { + var d = this.getDoc(), b = d.body, de = d.documentElement; + + this.iframeElement.style.height = (tinyMCE.isRealIE) ? b.scrollHeight : de.offsetHeight + 'px'; + }, + + addShortcut : function(m, k, d, cmd, ui, va) { + var n = typeof(k) == "number", ie = tinyMCE.isIE, c, sc, i, scl = this.shortcuts; + + if (!tinyMCE.getParam('custom_shortcuts')) + return false; + + m = m.toLowerCase(); + k = ie && !n ? k.toUpperCase() : k; + c = n ? null : k.charCodeAt(0); + d = d && d.indexOf('lang_') == 0 ? tinyMCE.getLang(d) : d; + + sc = { + alt : m.indexOf('alt') != -1, + ctrl : m.indexOf('ctrl') != -1, + shift : m.indexOf('shift') != -1, + charCode : c, + keyCode : n ? k : (ie ? c : null), + desc : d, + cmd : cmd, + ui : ui, + val : va + }; + + for (i=0; i 0) + rng.pasteHTML('
' + rng.htmlText + "
"); + + tinyMCE.triggerNodeChange(); + return; + } + } + } + + switch (command) { + case "mceRepaint": + this.repaint(); + return true; + + case "unlink": + // Unlink if caret is inside link + if (tinyMCE.isGecko && this.getSel().isCollapsed) { + focusElm = tinyMCE.getParentElement(focusElm, 'A'); + + if (focusElm) + this.selection.selectNode(focusElm, false); + } + + this.getDoc().execCommand(command, user_interface, value); + + tinyMCE.isGecko && this.getSel().collapseToEnd(); + + tinyMCE.triggerNodeChange(); + + return true; + + case "InsertUnorderedList": + case "InsertOrderedList": + this.getDoc().execCommand(command, user_interface, value); + tinyMCE.triggerNodeChange(); + break; + + case "Strikethrough": + this.getDoc().execCommand(command, user_interface, value); + tinyMCE.triggerNodeChange(); + break; + + case "mceSelectNode": + this.selection.selectNode(value); + tinyMCE.triggerNodeChange(); + tinyMCE.selectedNode = value; + break; + + case "FormatBlock": + if (value == null || value == "") { + var elm = tinyMCE.getParentElement(this.getFocusElement(), "p,div,h1,h2,h3,h4,h5,h6,pre,address,blockquote,dt,dl,dd,samp"); + + if (elm) + this.execCommand("mceRemoveNode", false, elm); + } else { + if (!this.cleanup.isValid(value)) + return true; + + if (tinyMCE.isGecko && new RegExp('<(div|blockquote|code|dt|dd|dl|samp)>', 'gi').test(value)) + value = value.replace(/[^a-z]/gi, ''); + + if (tinyMCE.isIE && new RegExp('blockquote|code|samp', 'gi').test(value)) { + var b = this.selection.getBookmark(); + this.getDoc().execCommand("FormatBlock", false, '

'); + tinyMCE.renameElement(tinyMCE.getParentBlockElement(this.getFocusElement()), value); + this.selection.moveToBookmark(b); + } else + this.getDoc().execCommand("FormatBlock", false, value); + } + + tinyMCE.triggerNodeChange(); + + break; + + case "mceRemoveNode": + if (!value) + value = tinyMCE.getParentElement(this.getFocusElement()); + + if (tinyMCE.isIE) { + value.outerHTML = value.innerHTML; + } else { + var rng = value.ownerDocument.createRange(); + rng.setStartBefore(value); + rng.setEndAfter(value); + rng.deleteContents(); + rng.insertNode(rng.createContextualFragment(value.innerHTML)); + } + + tinyMCE.triggerNodeChange(); + + break; + + case "mceSelectNodeDepth": + var parentNode = this.getFocusElement(); + for (var i=0; parentNode; i++) { + if (parentNode.nodeName.toLowerCase() == "body") + break; + + if (parentNode.nodeName.toLowerCase() == "#text") { + i--; + parentNode = parentNode.parentNode; + continue; + } + + if (i == value) { + this.selection.selectNode(parentNode, false); + tinyMCE.triggerNodeChange(); + tinyMCE.selectedNode = parentNode; + return; + } + + parentNode = parentNode.parentNode; + } + + break; + + case "mceSetStyleInfo": + case "SetStyleInfo": + var rng = this.getRng(); + var sel = this.getSel(); + var scmd = value['command']; + var sname = value['name']; + var svalue = value['value'] == null ? '' : value['value']; + //var svalue = value['value'] == null ? '' : value['value']; + var wrapper = value['wrapper'] ? value['wrapper'] : "span"; + var parentElm = null; + var invalidRe = new RegExp("^BODY|HTML$", "g"); + var invalidParentsRe = tinyMCE.settings['merge_styles_invalid_parents'] != '' ? new RegExp(tinyMCE.settings['merge_styles_invalid_parents'], "gi") : null; + + // Whole element selected check + if (tinyMCE.isIE) { + // Control range + if (rng.item) + parentElm = rng.item(0); + else { + var pelm = rng.parentElement(); + var prng = doc.selection.createRange(); + prng.moveToElementText(pelm); + + if (rng.htmlText == prng.htmlText || rng.boundingWidth == 0) { + if (invalidParentsRe == null || !invalidParentsRe.test(pelm.nodeName)) + parentElm = pelm; + } + } + } else { + var felm = this.getFocusElement(); + if (sel.isCollapsed || (new RegExp('td|tr|tbody|table', 'gi').test(felm.nodeName) && sel.anchorNode == felm.parentNode)) + parentElm = felm; + } + + // Whole element selected + if (parentElm && !invalidRe.test(parentElm.nodeName)) { + if (scmd == "setstyle") + tinyMCE.setStyleAttrib(parentElm, sname, svalue); + + if (scmd == "setattrib") + tinyMCE.setAttrib(parentElm, sname, svalue); + + if (scmd == "removeformat") { + parentElm.style.cssText = ''; + tinyMCE.setAttrib(parentElm, 'class', ''); + } + + // Remove style/attribs from all children + var ch = tinyMCE.getNodeTree(parentElm, new Array(), 1); + for (var z=0; z=0; i--) { + var elm = nodes[i]; + var isNew = tinyMCE.getAttrib(elm, "mce_new") == "true"; + + elm.removeAttribute("mce_new"); + + // Is only child a element + if (elm.childNodes && elm.childNodes.length == 1 && elm.childNodes[0].nodeType == 1) { + //tinyMCE.debug("merge1" + isNew); + this._mergeElements(scmd, elm, elm.childNodes[0], isNew); + continue; + } + + // Is I the only child + if (elm.parentNode.childNodes.length == 1 && !invalidRe.test(elm.nodeName) && !invalidRe.test(elm.parentNode.nodeName)) { + //tinyMCE.debug("merge2" + isNew + "," + elm.nodeName + "," + elm.parentNode.nodeName); + if (invalidParentsRe == null || !invalidParentsRe.test(elm.parentNode.nodeName)) + this._mergeElements(scmd, elm.parentNode, elm, false); + } + } + + // Remove empty wrappers + var nodes = doc.getElementsByTagName(wrapper); + for (var i=nodes.length-1; i>=0; i--) { + var elm = nodes[i]; + var isEmpty = true; + + // Check if it has any attribs + var tmp = doc.createElement("body"); + tmp.appendChild(elm.cloneNode(false)); + + // Is empty span, remove it + tmp.innerHTML = tmp.innerHTML.replace(new RegExp('style=""|class=""', 'gi'), ''); + //tinyMCE.debug(tmp.innerHTML); + if (new RegExp('', 'gi').test(tmp.innerHTML)) { + for (var x=0; x 0) { + value = tinyMCE.replaceVar(value, "selection", selectedText); + tinyMCE.execCommand('mceInsertContent', false, value); + } + + tinyMCE.triggerNodeChange(); + break; + + case "mceSetAttribute": + if (typeof(value) == 'object') { + var targetElms = (typeof(value['targets']) == "undefined") ? "p,img,span,div,td,h1,h2,h3,h4,h5,h6,pre,address" : value['targets']; + var targetNode = tinyMCE.getParentElement(this.getFocusElement(), targetElms); + + if (targetNode) { + targetNode.setAttribute(value['name'], value['value']); + tinyMCE.triggerNodeChange(); + } + } + break; + + case "mceSetCSSClass": + this.execCommand("mceSetStyleInfo", false, {command : "setattrib", name : "class", value : value}); + break; + + case "mceInsertRawHTML": + var key = 'tiny_mce_marker'; + + this.execCommand('mceBeginUndoLevel'); + + // Insert marker key + this.execCommand('mceInsertContent', false, key); + + // Store away scroll pos + var scrollX = this.getBody().scrollLeft + this.getDoc().documentElement.scrollLeft; + var scrollY = this.getBody().scrollTop + this.getDoc().documentElement.scrollTop; + + // Find marker and replace with RAW HTML + var html = this.getBody().innerHTML; + if ((pos = html.indexOf(key)) != -1) + tinyMCE.setInnerHTML(this.getBody(), html.substring(0, pos) + value + html.substring(pos + key.length)); + + // Restore scoll pos + this.contentWindow.scrollTo(scrollX, scrollY); + + this.execCommand('mceEndUndoLevel'); + + break; + + case "mceInsertContent": + // Force empty string + if (!value) + value = ''; + + var insertHTMLFailed = false; + + // Removed since it produced problems in IE + // this.getWin().focus(); + + if (tinyMCE.isGecko || tinyMCE.isOpera) { + try { + // Is plain text or HTML, &,   etc will be encoded wrong in FF + if (value.indexOf('<') == -1 && !value.match(/(&| |<|>)/g)) { + var r = this.getRng(); + var n = this.getDoc().createTextNode(tinyMCE.entityDecode(value)); + var s = this.getSel(); + var r2 = r.cloneRange(); + + // Insert text at cursor position + s.removeAllRanges(); + r.deleteContents(); + r.insertNode(n); + + // Move the cursor to the end of text + r2.selectNode(n); + r2.collapse(false); + s.removeAllRanges(); + s.addRange(r2); + } else { + value = tinyMCE.fixGeckoBaseHREFBug(1, this.getDoc(), value); + this.getDoc().execCommand('inserthtml', false, value); + tinyMCE.fixGeckoBaseHREFBug(2, this.getDoc(), value); + } + } catch (ex) { + insertHTMLFailed = true; + } + + if (!insertHTMLFailed) { + tinyMCE.triggerNodeChange(); + return; + } + } + + if (!tinyMCE.isIE) { + var isHTML = value.indexOf('<') != -1; + var sel = this.getSel(); + var rng = this.getRng(); + + if (isHTML) { + if (tinyMCE.isSafari) { + var tmpRng = this.getDoc().createRange(); + + tmpRng.setStart(this.getBody(), 0); + tmpRng.setEnd(this.getBody(), 0); + + value = tmpRng.createContextualFragment(value); + } else + value = rng.createContextualFragment(value); + } else { + // Setup text node + var el = document.createElement("div"); + el.innerHTML = value; + value = el.firstChild.nodeValue; + value = doc.createTextNode(value); + } + + // Insert plain text in Safari + if (tinyMCE.isSafari && !isHTML) { + this.execCommand('InsertText', false, value.nodeValue); + tinyMCE.triggerNodeChange(); + return true; + } else if (tinyMCE.isSafari && isHTML) { + rng.deleteContents(); + rng.insertNode(value); + tinyMCE.triggerNodeChange(); + return true; + } + + rng.deleteContents(); + + // If target node is text do special treatment, (Mozilla 1.3 fix) + if (rng.startContainer.nodeType == 3) { + var node = rng.startContainer.splitText(rng.startOffset); + node.parentNode.insertBefore(value, node); + } else + rng.insertNode(value); + + if (!isHTML) { + // Removes weird selection trails + sel.selectAllChildren(doc.body); + sel.removeAllRanges(); + + // Move cursor to end of content + var rng = doc.createRange(); + + rng.selectNode(value); + rng.collapse(false); + + sel.addRange(rng); + } else + rng.collapse(false); + + tinyMCE.fixGeckoBaseHREFBug(2, this.getDoc(), value); + } else { + var rng = doc.selection.createRange(), tmpRng = null; + var c = value.indexOf('"; + } + + if (hc) { + cn = n.childNodes; + + for (i=0, l=cn.length; i'; + + return h; + }, + + _serializeAttribute : function(n, r, an) { + var av = '', t, os = this.settings.on_save; + + if (os && (an.indexOf('mce_') == 0 || an.indexOf('_moz') == 0)) + return ''; + + if (os && this.mceAttribs[an]) + av = this._getAttrib(n, this.mceAttribs[an]); + + if (av.length == 0) + av = this._getAttrib(n, an); + + if (av.length == 0 && r.defaultAttribs && (t = r.defaultAttribs[an])) { + av = t; + + if (av == "mce_empty") + return " " + an + '=""'; + } + + if (r.forceAttribs && (t = r.forceAttribs[an])) + av = t; + + if (os && av.length != 0 && /^(src|href|longdesc)$/.test(an)) + av = this._urlConverter(this, n, av); + + if (av.length != 0 && r.validAttribValues && r.validAttribValues[an] && !r.validAttribValues[an].test(av)) + return ""; + + if (av.length != 0 && av == "{$uid}") + av = "uid_" + (this.idCount++); + + if (av.length != 0) { + if (an.indexOf('on') != 0) + av = this.xmlEncode(av, 1); + + return " " + an + "=" + '"' + av + '"'; + } + + return ""; + }, + + formatHTML : function(h) { + var s = this.settings, p = '', i = 0, li = 0, o = '', l; + + // Replace BR in pre elements to \n + h = h.replace(/]*)>(.*?)<\/pre>/gi, function (a, b, c) { + c = c.replace(//gi, '\n'); + return '' + c + ''; + }); + + h = h.replace(/\r/g, ''); // Windows sux, isn't carriage return a thing of the past :) + h = '\n' + h; + h = h.replace(new RegExp('\\n\\s+', 'gi'), '\n'); // Remove previous formatting + h = h.replace(this.nlBeforeRe, '\n<$1$2>'); + h = h.replace(this.nlAfterRe, '<$1$2>\n'); + h = h.replace(this.nlBeforeAfterRe, '\n<$1$2$3>\n'); + h += '\n'; + + //tinyMCE.debug(h); + + while ((i = h.indexOf('\n', i + 1)) != -1) { + if ((l = h.substring(li + 1, i)).length != 0) { + if (this.ouRe.test(l) && p.length >= s.indent_levels) + p = p.substring(s.indent_levels); + + o += p + l + '\n'; + + if (this.inRe.test(l)) + p += this.inStr; + } + + li = i; + } + + //tinyMCE.debug(h); + + return o; + }, + + xmlEncode : function(s, skip_apos) { + var cl = this, re = !skip_apos ? this.xmlEncodeAposRe : this.xmlEncodeRe; + + this._setupEntities(); // Will intialize lookup table + + switch (this.settings.entity_encoding) { + case "raw": + return tinyMCE.xmlEncode(s, skip_apos); + + case "named": + return s.replace(re, function (c, b) { + b = cl.entities[c.charCodeAt(0)]; + + return b ? '&' + b + ';' : c; + }); + + case "numeric": + return s.replace(re, function (c, b) { + return b ? '&#' + c.charCodeAt(0) + ';' : c; + }); + } + + return s; + }, + + split : function(re, s) { + var c = s.split(re); + var i, l, o = new Array(); + + for (i=0, l=c.length; i' : '>' + h + ''; + + return o; +}; + +TinyMCE_Engine.prototype.createTag = function(d, tn, a, h) { + var o = d.createElement(tn); + + if (a) { + for (n in a) { + if (typeof(a[n]) != 'function' && a[n] != null) + tinyMCE.setAttrib(o, n, a[n]); + } + } + + if (h) + o.innerHTML = h; + + return o; +}; + +TinyMCE_Engine.prototype.getElementByAttributeValue = function(n, e, a, v) { + return (n = this.getElementsByAttributeValue(n, e, a, v)).length == 0 ? null : n[0]; +}; + +TinyMCE_Engine.prototype.getElementsByAttributeValue = function(n, e, a, v) { + var i, nl = n.getElementsByTagName(e), o = new Array(); + + for (i=0; i]*)>/gi, ''); + h = h.replace(/]*)>/gi, ''); + h = h.replace(/]*)>/gi, ''); + h = h.replace(/]*)>/gi, ''); + h = h.replace(/<\/strong>/gi, ''); + h = h.replace(/<\/em>/gi, ''); + } + + if (tinyMCE.isRealIE) { + // Since MSIE handles invalid HTML better that valid XHTML we + // need to make some things invalid.


gets converted to
. + h = h.replace(/\s\/>/g, '>'); + + // Since MSIE auto generated emtpy P tags some times we must tell it to keep the real ones + h = h.replace(/]*)>\u00A0?<\/p>/gi, ' 

'); // Keep empty paragraphs + h = h.replace(/]*)>\s* \s*<\/p>/gi, ' 

'); // Keep empty paragraphs + h = h.replace(/]*)>\s+<\/p>/gi, ' 

'); // Keep empty paragraphs + + // Remove first comment + e.innerHTML = tinyMCE.uniqueTag + h; + e.firstChild.removeNode(true); + + // Remove weird auto generated empty paragraphs unless it's supposed to be there + nl = e.getElementsByTagName("p"); + for (i=nl.length-1; i>=0; i--) { + n = nl[i]; + + if (n.nodeName == 'P' && !n.hasChildNodes() && !n.mce_keep) + n.parentNode.removeChild(n); + } + } else { + h = this.fixGeckoBaseHREFBug(1, e, h); + e.innerHTML = h; + this.fixGeckoBaseHREFBug(2, e, h); + } +}; + +TinyMCE_Engine.prototype.getOuterHTML = function(e) { + if (tinyMCE.isIE) + return e.outerHTML; + + var d = e.ownerDocument.createElement("body"); + d.appendChild(e.cloneNode(true)); + return d.innerHTML; +}; + +TinyMCE_Engine.prototype.setOuterHTML = function(e, h, d) { + var d = typeof(d) == "undefined" ? e.ownerDocument : d, i, nl, t; + + if (tinyMCE.isIE && e.nodeType == 1) + e.outerHTML = h; + else { + t = d.createElement("body"); + t.innerHTML = h; + + for (i=0, nl=t.childNodes; i-1; i--) { + if (ar[i].specified && ar[i].nodeValue) + ne.setAttribute(ar[i].nodeName.toLowerCase(), ar[i].nodeValue); + } + + ar = e.childNodes; + for (i=0; i= strTok2.length) { + for (var i=0; i= strTok2.length || strTok1[i] != strTok2[i]) { + breakPoint = i + 1; + break; + } + } + } + + if (strTok1.length < strTok2.length) { + for (var i=0; i= strTok1.length || strTok1[i] != strTok2[i]) { + breakPoint = i + 1; + break; + } + } + } + + if (breakPoint == 1) + return targetURL.path; + + for (var i=0; i<(strTok1.length-(breakPoint-1)); i++) + outPath += "../"; + + for (var i=breakPoint-1; i=0; i--) { + if (baseURLParts[i].length == 0) + continue; + + newBaseURLParts[newBaseURLParts.length] = baseURLParts[i]; + } + baseURLParts = newBaseURLParts.reverse(); + + // Merge relURLParts chunks + var newRelURLParts = new Array(); + var numBack = 0; + for (var i=relURLParts.length-1; i>=0; i--) { + if (relURLParts[i].length == 0 || relURLParts[i] == ".") + continue; + + if (relURLParts[i] == '..') { + numBack++; + continue; + } + + if (numBack > 0) { + numBack--; + continue; + } + + newRelURLParts[newRelURLParts.length] = relURLParts[i]; + } + + relURLParts = newRelURLParts.reverse(); + + // Remove end from absolute path + var len = baseURLParts.length-numBack; + var absPath = (len <= 0 ? "" : "/") + baseURLParts.slice(0, len).join('/') + "/" + relURLParts.join('/'); + var start = "", end = ""; + + // Build output URL + relURL.protocol = baseURL.protocol; + relURL.host = baseURL.host; + relURL.port = baseURL.port; + + // Re-add trailing slash if it's removed + if (relURL.path.charAt(relURL.path.length-1) == "/") + absPath += "/"; + + relURL.path = absPath; + + return this.serializeURL(relURL); +}; + +TinyMCE_Engine.prototype.convertURL = function(url, node, on_save) { + var prot = document.location.protocol; + var host = document.location.hostname; + var port = document.location.port; + + // Pass through file protocol + if (prot == "file:") + return url; + + // Something is wrong, remove weirdness + url = tinyMCE.regexpReplace(url, '(http|https):///', '/'); + + // Mailto link or anchor (Pass through) + if (url.indexOf('mailto:') != -1 || url.indexOf('javascript:') != -1 || tinyMCE.regexpReplace(url,'[ \t\r\n\+]|%20','').charAt(0) == "#") + return url; + + // Fix relative/Mozilla + if (!tinyMCE.isIE && !on_save && url.indexOf("://") == -1 && url.charAt(0) != '/') + return tinyMCE.settings['base_href'] + url; + + // Handle relative URLs + if (on_save && tinyMCE.getParam('relative_urls')) { + var curl = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], url); + if (curl.charAt(0) == '/') + curl = tinyMCE.settings['document_base_prefix'] + curl; + + var urlParts = tinyMCE.parseURL(curl); + var tmpUrlParts = tinyMCE.parseURL(tinyMCE.settings['document_base_url']); + + // Force relative + if (urlParts['host'] == tmpUrlParts['host'] && (urlParts['port'] == tmpUrlParts['port'])) + return tinyMCE.convertAbsoluteURLToRelativeURL(tinyMCE.settings['document_base_url'], curl); + } + + // Handle absolute URLs + if (!tinyMCE.getParam('relative_urls')) { + var urlParts = tinyMCE.parseURL(url); + var baseUrlParts = tinyMCE.parseURL(tinyMCE.settings['base_href']); + + // Force absolute URLs from relative URLs + url = tinyMCE.convertRelativeToAbsoluteURL(tinyMCE.settings['base_href'], url); + + // If anchor and path is the same page + if (urlParts['anchor'] && urlParts['path'] == baseUrlParts['path']) + return "#" + urlParts['anchor']; + } + + // Remove current domain + if (tinyMCE.getParam('remove_script_host')) { + var start = "", portPart = ""; + + if (port != "") + portPart = ":" + port; + + start = prot + "//" + host + portPart + "/"; + + if (url.indexOf(start) == 0) + url = url.substring(start.length-1); + } + + return url; +}; + +TinyMCE_Engine.prototype.convertAllRelativeURLs = function(body) { + var i, elms, src, href, mhref, msrc; + + // Convert all image URL:s to absolute URL + elms = body.getElementsByTagName("img"); + for (i=0; i bookmark.index) { + try { + rng.addElement(nl[bookmark.index]); + } catch (ex) { + // Might be thrown if the node no longer exists + } + } + } else { + // Try/catch needed since this operation breaks when TinyMCE is placed in hidden divs/tabs + try { + // Incorrect bookmark + if (bookmark.start < 0) + return true; + + rng = inst.getSel().createRange(); + rng.moveToElementText(inst.getBody()); + rng.collapse(true); + rng.moveStart('character', bookmark.start); + rng.moveEnd('character', bookmark.length); + } catch (ex) { + return true; + } + } + + rng.select(); + + win.scrollTo(bookmark.scrollX, bookmark.scrollY); + return true; + } + + if (tinyMCE.isGecko || tinyMCE.isOpera) { + if (!sel) + return false; + + if (bookmark.rng) { + sel.removeAllRanges(); + sel.addRange(bookmark.rng); + } + + if (bookmark.start != -1 && bookmark.end != -1) { + try { + sd = this._getTextPos(b, bookmark.start, bookmark.end); + rng = doc.createRange(); + rng.setStart(sd.startNode, sd.startOffset); + rng.setEnd(sd.endNode, sd.endOffset); + sel.removeAllRanges(); + sel.addRange(rng); + win.focus(); + } catch (ex) { + // Ignore + } + } + + /* + if (typeof(bookmark.index) != 'undefined') { + tinyMCE.selectElements(b, 'IMG', function (n) { + if (bookmark.index-- == 0) { + // Select image in Gecko here + } + + return false; + }); + } + */ + + win.scrollTo(bookmark.scrollX, bookmark.scrollY); + return true; + } + + return false; + }, + + _getPosText : function(r, sn, en) { + var w = document.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {}; + + while ((n = w.nextNode()) != null) { + if (n == sn) + d.start = p; + + if (n == en) { + d.end = p; + return d; + } + + p += n.nodeValue ? n.nodeValue.length : 0; + } + + return null; + }, + + _getTextPos : function(r, sp, ep) { + var w = document.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {}; + + while ((n = w.nextNode()) != null) { + p += n.nodeValue ? n.nodeValue.length : 0; + + if (p >= sp && !d.startNode) { + d.startNode = n; + d.startOffset = sp - (p - n.nodeValue.length); + } + + if (p >= ep) { + d.endNode = n; + d.endOffset = ep - (p - n.nodeValue.length); + + return d; + } + } + + return null; + }, + + selectNode : function(node, collapse, select_text_node, to_start) { + var inst = this.instance, sel, rng, nodes; + + if (!node) + return; + + if (typeof(collapse) == "undefined") + collapse = true; + + if (typeof(select_text_node) == "undefined") + select_text_node = false; + + if (typeof(to_start) == "undefined") + to_start = true; + + if (inst.settings.auto_resize) + inst.resizeToContent(); + + if (tinyMCE.isRealIE) { + rng = inst.getDoc().body.createTextRange(); + + try { + rng.moveToElementText(node); + + if (collapse) + rng.collapse(to_start); + + rng.select(); + } catch (e) { + // Throws illigal agrument in MSIE some times + } + } else { + sel = this.getSel(); + + if (!sel) + return; + + if (tinyMCE.isSafari) { + sel.setBaseAndExtent(node, 0, node, node.innerText.length); + + if (collapse) { + if (to_start) + sel.collapseToStart(); + else + sel.collapseToEnd(); + } + + this.scrollToNode(node); + + return; + } + + rng = inst.getDoc().createRange(); + + if (select_text_node) { + // Find first textnode in tree + nodes = tinyMCE.getNodeTree(node, new Array(), 3); + if (nodes.length > 0) + rng.selectNodeContents(nodes[0]); + else + rng.selectNodeContents(node); + } else + rng.selectNode(node); + + if (collapse) { + // Special treatment of textnode collapse + if (!to_start && node.nodeType == 3) { + rng.setStart(node, node.nodeValue.length); + rng.setEnd(node, node.nodeValue.length); + } else + rng.collapse(to_start); + } + + sel.removeAllRanges(); + sel.addRange(rng); + } + + this.scrollToNode(node); + + // Set selected element + tinyMCE.selectedElement = null; + if (node.nodeType == 1) + tinyMCE.selectedElement = node; + }, + + scrollToNode : function(node) { + var inst = this.instance, w = inst.getWin(), vp = inst.getViewPort(), pos = tinyMCE.getAbsPosition(node), cvp, p, cwin; + + // Only scroll if out of visible area + if (pos.absLeft < vp.left || pos.absLeft > vp.left + vp.width || pos.absTop < vp.top || pos.absTop > vp.top + (vp.height-25)) + w.scrollTo(pos.absLeft, pos.absTop - vp.height + 25); + + // Scroll container window + if (inst.settings.auto_resize) { + cwin = inst.getContainerWin(); + cvp = tinyMCE.getViewPort(cwin); + p = this.getAbsPosition(node); + + if (p.absLeft < cvp.left || p.absLeft > cvp.left + cvp.width || p.absTop < cvp.top || p.absTop > cvp.top + cvp.height) + cwin.scrollTo(p.absLeft, p.absTop - cvp.height + 25); + } + }, + + getAbsPosition : function(n) { + var pos = tinyMCE.getAbsPosition(n), ipos = tinyMCE.getAbsPosition(this.instance.iframeElement); + + return { + absLeft : ipos.absLeft + pos.absLeft, + absTop : ipos.absTop + pos.absTop + }; + }, + + getSel : function() { + var inst = this.instance; + + if (tinyMCE.isRealIE) + return inst.getDoc().selection; + + return inst.contentWindow.getSelection(); + }, + + getRng : function() { + var s = this.getSel(); + + if (s == null) + return null; + + if (tinyMCE.isRealIE) + return s.createRange(); + + if (tinyMCE.isSafari && !s.getRangeAt) + return '' + window.getSelection(); + + if (s.rangeCount > 0) + return s.getRangeAt(0); + + return null; + }, + + isCollapsed : function() { + var r = this.getRng(); + + if (r.item) + return false; + + return r.boundingWidth == 0 || this.getSel().isCollapsed; + }, + + collapse : function(b) { + var r = this.getRng(), s = this.getSel(); + + if (r.select) { + r.collapse(b); + r.select(); + } else { + if (b) + s.collapseToStart(); + else + s.collapseToEnd(); + } + }, + + getFocusElement : function() { + var inst = this.instance, doc, rng, sel, elm; + + if (tinyMCE.isRealIE) { + doc = inst.getDoc(); + rng = doc.selection.createRange(); + + // if (rng.collapse) + // rng.collapse(true); + + elm = rng.item ? rng.item(0) : rng.parentElement(); + } else { + if (!tinyMCE.isSafari && inst.isHidden()) + return inst.getBody(); + + sel = this.getSel(); + rng = this.getRng(); + + if (!sel || !rng) + return null; + + elm = rng.commonAncestorContainer; + //elm = (sel && sel.anchorNode) ? sel.anchorNode : null; + + // Handle selection a image or other control like element such as anchors + if (!rng.collapsed) { + // Is selection small + if (rng.startContainer == rng.endContainer) { + if (rng.startOffset - rng.endOffset < 2) { + if (rng.startContainer.hasChildNodes()) + elm = rng.startContainer.childNodes[rng.startOffset]; + } + } + } + + // Get the element parent of the node + elm = tinyMCE.getParentElement(elm); + + //if (tinyMCE.selectedElement != null && tinyMCE.selectedElement.nodeName.toLowerCase() == "img") + // elm = tinyMCE.selectedElement; + } + + return elm; + } + + }; + +/* file:jscripts/tiny_mce/classes/TinyMCE_UndoRedo.class.js */ + +function TinyMCE_UndoRedo(inst) { + this.instance = inst; + this.undoLevels = new Array(); + this.undoIndex = 0; + this.typingUndoIndex = -1; + this.undoRedo = true; +}; + +TinyMCE_UndoRedo.prototype = { + add : function(l) { + var b, customUndoLevels, newHTML, inst = this.instance, i, ul, ur; + + if (l) { + this.undoLevels[this.undoLevels.length] = l; + return true; + } + + if (this.typingUndoIndex != -1) { + this.undoIndex = this.typingUndoIndex; + + if (tinyMCE.typingUndoIndex != -1) + tinyMCE.undoIndex = tinyMCE.typingUndoIndex; + } + + newHTML = tinyMCE.trim(inst.getBody().innerHTML); + if (this.undoLevels[this.undoIndex] && newHTML != this.undoLevels[this.undoIndex].content) { + //tinyMCE.debug(newHTML, this.undoLevels[this.undoIndex].content); + + tinyMCE.dispatchCallback(inst, 'onchange_callback', 'onChange', inst); + + // Time to compress + customUndoLevels = tinyMCE.settings['custom_undo_redo_levels']; + if (customUndoLevels != -1 && this.undoLevels.length > customUndoLevels) { + for (i=0; i 0) { + this.undoIndex--; + + tinyMCE.setInnerHTML(inst.getBody(), this.undoLevels[this.undoIndex].content); + inst.repaint(); + + if (inst.settings.custom_undo_redo_restore_selection) + inst.selection.moveToBookmark(this.undoLevels[this.undoIndex].bookmark); + } + }, + + redo : function() { + var inst = this.instance; + + tinyMCE.execCommand("mceEndTyping"); + + if (this.undoIndex < (this.undoLevels.length-1)) { + this.undoIndex++; + + tinyMCE.setInnerHTML(inst.getBody(), this.undoLevels[this.undoIndex].content); + inst.repaint(); + + if (inst.settings.custom_undo_redo_restore_selection) + inst.selection.moveToBookmark(this.undoLevels[this.undoIndex].bookmark); + } + + tinyMCE.triggerNodeChange(); + } + + }; + +/* file:jscripts/tiny_mce/classes/TinyMCE_ForceParagraphs.class.js */ + +var TinyMCE_ForceParagraphs = { + _insertPara : function(inst, e) { + var doc = inst.getDoc(), sel = inst.getSel(), body = inst.getBody(), win = inst.contentWindow, rng = sel.getRangeAt(0); + var rootElm = doc.documentElement, blockName = "P", startNode, endNode, startBlock, endBlock; + var rngBefore, rngAfter, direct, startNode, startOffset, endNode, endOffset, b = tinyMCE.isOpera ? inst.selection.getBookmark() : null; + var paraBefore, paraAfter, startChop, endChop, contents; + + function isEmpty(para) { + function isEmptyHTML(html) { + return html.replace(new RegExp('[ \t\r\n]+', 'g'), '').toLowerCase() == ""; + } + + // Check for images + if (para.getElementsByTagName("img").length > 0) + return false; + + // Check for tables + if (para.getElementsByTagName("table").length > 0) + return false; + + // Check for HRs + if (para.getElementsByTagName("hr").length > 0) + return false; + + // Check all textnodes + var nodes = tinyMCE.getNodeTree(para, new Array(), 3); + for (var i=0; i <" + blockName + "> "; + paraAfter = body.childNodes[1]; + } + + inst.selection.moveToBookmark(b); + inst.selection.selectNode(paraAfter, true, true); + + return true; + } + + // Place first part within new paragraph + if (startChop.nodeName == blockName) + rngBefore.setStart(startChop, 0); + else + rngBefore.setStartBefore(startChop); + + rngBefore.setEnd(startNode, startOffset); + paraBefore.appendChild(rngBefore.cloneContents()); + + // Place secound part within new paragraph + rngAfter.setEndAfter(endChop); + rngAfter.setStart(endNode, endOffset); + contents = rngAfter.cloneContents(); + + if (contents.firstChild && contents.firstChild.nodeName == blockName) { + /* var nodes = contents.firstChild.childNodes; + for (var i=0; i= r.startOffset && nv.charAt(r.startOffset - 1) == ' ') + s = true;*/ + + // Only remove BRs if we are at the end of line #bug 1464152 + if (nv != null && r.startOffset == nv.length) + sn.nextSibling.parentNode.removeChild(sn.nextSibling); + } + + if (inst.settings.auto_resize) + inst.resizeToContent(); + + return s; + } + + }; + +/* file:jscripts/tiny_mce/classes/TinyMCE_Layer.class.js */ + +function TinyMCE_Layer(id, bm) { + this.id = id; + this.blockerElement = null; + this.events = false; + this.element = null; + this.blockMode = typeof(bm) != 'undefined' ? bm : true; + this.doc = document; +}; + +TinyMCE_Layer.prototype = { + moveRelativeTo : function(re, p) { + var rep = this.getAbsPosition(re); + var w = parseInt(re.offsetWidth); + var h = parseInt(re.offsetHeight); + var e = this.getElement(); + var ew = parseInt(e.offsetWidth); + var eh = parseInt(e.offsetHeight); + var x, y; + + switch (p) { + case "tl": + x = rep.absLeft; + y = rep.absTop; + break; + + case "tr": + x = rep.absLeft + w; + y = rep.absTop; + break; + + case "bl": + x = rep.absLeft; + y = rep.absTop + h; + break; + + case "br": + x = rep.absLeft + w; + y = rep.absTop + h; + break; + + case "cc": + x = rep.absLeft + (w / 2) - (ew / 2); + y = rep.absTop + (h / 2) - (eh / 2); + break; + } + + this.moveTo(x, y); + }, + + moveBy : function(x, y) { + var e = this.getElement(); + this.moveTo(parseInt(e.style.left) + x, parseInt(e.style.top) + y); + }, + + moveTo : function(x, y) { + var e = this.getElement(); + + e.style.left = x + "px"; + e.style.top = y + "px"; + + this.updateBlocker(); + }, + + resizeBy : function(w, h) { + var e = this.getElement(); + this.resizeTo(parseInt(e.style.width) + w, parseInt(e.style.height) + h); + }, + + resizeTo : function(w, h) { + var e = this.getElement(); + + if (w != null) + e.style.width = w + "px"; + + if (h != null) + e.style.height = h + "px"; + + this.updateBlocker(); + }, + + show : function() { + var el = this.getElement(); + + if (el) { + el.style.display = 'block'; + this.updateBlocker(); + } + }, + + hide : function() { + var el = this.getElement(); + + if (el) { + el.style.display = 'none'; + this.updateBlocker(); + } + }, + + isVisible : function() { + return this.getElement().style.display == 'block'; + }, + + getElement : function() { + if (!this.element) + this.element = this.doc.getElementById(this.id); + + return this.element; + }, + + setBlockMode : function(s) { + this.blockMode = s; + }, + + updateBlocker : function() { + var e, b, x, y, w, h; + + b = this.getBlocker(); + if (b) { + if (this.blockMode) { + e = this.getElement(); + x = this.parseInt(e.style.left); + y = this.parseInt(e.style.top); + w = this.parseInt(e.offsetWidth); + h = this.parseInt(e.offsetHeight); + + b.style.left = x + 'px'; + b.style.top = y + 'px'; + b.style.width = w + 'px'; + b.style.height = h + 'px'; + b.style.display = e.style.display; + } else + b.style.display = 'none'; + } + }, + + getBlocker : function() { + var d, b; + + if (!this.blockerElement && this.blockMode) { + d = this.doc; + b = d.getElementById(this.id + "_blocker"); + + if (!b) { + b = d.createElement("iframe"); + + b.setAttribute('id', this.id + "_blocker"); + b.style.cssText = 'display: none; position: absolute; left: 0; top: 0'; + b.src = 'javascript:false;'; + b.frameBorder = '0'; + b.scrolling = 'no'; + + d.body.appendChild(b); + } + + this.blockerElement = b; + } + + return this.blockerElement; + }, + + getAbsPosition : function(n) { + var p = {absLeft : 0, absTop : 0}; + + while (n) { + p.absLeft += n.offsetLeft; + p.absTop += n.offsetTop; + n = n.offsetParent; + } + + return p; + }, + + create : function(n, c, p, h) { + var d = this.doc, e = d.createElement(n); + + e.setAttribute('id', this.id); + + if (c) + e.className = c; + + if (!p) + p = d.body; + + if (h) + e.innerHTML = h; + + p.appendChild(e); + + return this.element = e; + }, + + exists : function() { + return this.doc.getElementById(this.id) != null; + }, + + parseInt : function(s) { + if (s == null || s == '') + return 0; + + return parseInt(s); + }, + + remove : function() { + var e = this.getElement(), b = this.getBlocker(); + + if (e) + e.parentNode.removeChild(e); + + if (b) + b.parentNode.removeChild(b); + } + + }; + +/* file:jscripts/tiny_mce/classes/TinyMCE_Menu.class.js */ + +function TinyMCE_Menu() { + var id; + + if (typeof(tinyMCE.menuCounter) == "undefined") + tinyMCE.menuCounter = 0; + + id = "mc_menu_" + tinyMCE.menuCounter++; + + TinyMCE_Layer.call(this, id, true); + + this.id = id; + this.items = new Array(); + this.needsUpdate = true; +}; + +TinyMCE_Menu.prototype = tinyMCE.extend(TinyMCE_Layer.prototype, { + init : function(s) { + var n; + + // Default params + this.settings = { + separator_class : 'mceMenuSeparator', + title_class : 'mceMenuTitle', + disabled_class : 'mceMenuDisabled', + menu_class : 'mceMenu', + drop_menu : true + }; + + for (n in s) + this.settings[n] = s[n]; + + this.create('div', this.settings.menu_class); + }, + + clear : function() { + this.items = new Array(); + }, + + addTitle : function(t) { + this.add({type : 'title', text : t}); + }, + + addDisabled : function(t) { + this.add({type : 'disabled', text : t}); + }, + + addSeparator : function() { + this.add({type : 'separator'}); + }, + + addItem : function(t, js) { + this.add({text : t, js : js}); + }, + + add : function(mi) { + this.items[this.items.length] = mi; + this.needsUpdate = true; + }, + + update : function() { + var e = this.getElement(), h = '', i, t, m = this.items, s = this.settings; + + if (this.settings.drop_menu) + h += ''; + + h += ''; + + for (i=0; i'; + } + + h += '
'; + break; + + case 'title': + h += '
' + t + ''; + break; + + case 'disabled': + h += '
' + t + ''; + break; + + default: + h += '
' + t + ''; + } + + h += '
'; + + e.innerHTML = h; + + this.needsUpdate = false; + this.updateBlocker(); + }, + + show : function() { + var nl, i; + + if (tinyMCE.lastMenu == this) + return; + + if (this.needsUpdate) + this.update(); + + if (tinyMCE.lastMenu && tinyMCE.lastMenu != this) + tinyMCE.lastMenu.hide(); + + TinyMCE_Layer.prototype.show.call(this); + + if (!tinyMCE.isOpera) { + // Accessibility stuff +/* nl = this.getElement().getElementsByTagName("a"); + if (nl.length > 0) + nl[0].focus();*/ + } + + tinyMCE.lastMenu = this; + } + + }); + +/* file:jscripts/tiny_mce/classes/TinyMCE_Compatibility.class.js */ + +if (!Function.prototype.call) { + Function.prototype.call = function() { + var a = arguments, s = a[0], i, as = '', r, o; + + for (i=1; i 1 ? ',' : '') + 'a[' + i + ']'; + + o = s._fu; + s._fu = this; + r = eval('s._fu(' + as + ')'); + s._fu = o; + + return r; + }; +}; + +/* file:jscripts/tiny_mce/classes/TinyMCE_Debug.class.js */ + +TinyMCE_Engine.prototype.debug = function() { + var m = "", a, i, l = tinyMCE.log.length; + + for (i=0, a = this.debug.arguments; i'; + h += ''; + + return h; +} + +function pickColor(e, target_form_element) { + if ((e.keyCode == 32 || e.keyCode == 13) || e.type == "mousedown") + tinyMCEPopup.pickColor(e, target_form_element); +} + +function updateColor(img_id, form_element_id) { + document.getElementById(img_id).style.backgroundColor = document.forms[0].elements[form_element_id].value; +} + +function setBrowserDisabled(id, state) { + var img = document.getElementById(id); + var lnk = document.getElementById(id + "_link"); + + if (lnk) { + if (state) { + lnk.setAttribute("realhref", lnk.getAttribute("href")); + lnk.removeAttribute("href"); + tinyMCE.switchClass(img, 'mceButtonDisabled', true); + } else { + lnk.setAttribute("href", lnk.getAttribute("realhref")); + tinyMCE.switchClass(img, 'mceButtonNormal', false); + } + } +} + +function getBrowserHTML(id, target_form_element, type, prefix) { + var option = prefix + "_" + type + "_browser_callback"; + var cb = tinyMCE.getParam(option, tinyMCE.getParam("file_browser_callback")); + if (cb == null) + return ""; + + var html = ""; + + html += ''; + html += ''; + + return html; +} + +function openBrower(img_id, target_form_element, type, option) { + var img = document.getElementById(img_id); + + if (img.className != "mceButtonDisabled") + tinyMCEPopup.openBrowser(target_form_element, type, option); +} + +function selectByValue(form_obj, field_name, value, add_custom, ignore_case) { + if (!form_obj || !form_obj.elements[field_name]) + return; + + var sel = form_obj.elements[field_name]; + + var found = false; + for (var i=0; i x && mx < x + w && my > y && my < y + h)) { + MCLayer.visibleLayer = null; + + if (l.autoHideCallback && l.autoHideCallback(l, e, mx, my)) + return true; + + l.hide(); + } + } + }, + + addCSSClass : function(e, c) { + this.removeCSSClass(e, c); + var a = this.explode(' ', e.className); + a[a.length] = c; + e.className = a.join(' '); + }, + + removeCSSClass : function(e, c) { + var a = this.explode(' ', e.className), i; + + for (i=0; i parseInt(v)) + st = this.mark(f, n); + } + } + + return st; + }, + + hasClass : function(n, c, d) { + return new RegExp('\\b' + c + (d ? '[0-9]+' : '') + '\\b', 'g').test(n.className); + }, + + getNum : function(n, c) { + c = n.className.match(new RegExp('\\b' + c + '([0-9]+)\\b', 'g'))[0]; + c = c.replace(/[^0-9]/g, ''); + + return c; + }, + + addClass : function(n, c, b) { + var o = this.removeClass(n, c); + n.className = b ? c + (o != '' ? (' ' + o) : '') : (o != '' ? (o + ' ') : '') + c; + }, + + removeClass : function(n, c) { + c = n.className.replace(new RegExp("(^|\\s+)" + c + "(\\s+|$)"), ' '); + return n.className = c != ' ' ? c : ''; + }, + + tags : function(f, s) { + return f.getElementsByTagName(s); + }, + + mark : function(f, n) { + var s = this.settings; + + this.addClass(n, s.invalid_cls); + this.markLabels(f, n, s.invalid_cls); + + return false; + }, + + markLabels : function(f, n, ic) { + var nl, i; + + nl = this.tags(f, "label"); + for (i=0; i + */ + +class Comments +{ + # + # VARIABLES + # + + /** + * Current list of comments. + * @var array + */ + + var $comments = Array(); + + /** + * Object to track permissions. + * @var object + */ + + var $perms; + + # + # METHODS + # + + /** + * Constructor. + * @param string Page ID of the page to load comments for + * @param string Namespace of the page to load comments for + */ + + function __construct($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + // Initialize permissions + if ( $page_id == $paths->cpage['urlname_nons'] && $namespace == $paths->namespace ) + $this->perms =& $GLOBALS['session']; + else + $this->perms = $session->fetch_page_acl($page_id, $namespace); + + $this->page_id = $db->escape($page_id); + $this->namespace = $db->escape($namespace); + } + + /** + * PHP 4 constructor. + * @see Comments::__construct + */ + function Comments($page_id, $namespace) + { + $this->__construct($page_id, $namespace); + } + + /** + * Processes a command in JSON format. + * @param string The JSON-encoded input, probably something sent from the Javascript/AJAX frontend + */ + + function process_json($json) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $parser = new Services_JSON(SERVICES_JSON_LOOSE_TYPE); + $data = $parser->decode($json); + if ( !isset($data['mode']) ) + { + return $parser->encode(Array('mode'=>'error','error'=>'No mode defined!')); + } + $ret = Array(); + $ret['mode'] = $data['mode']; + switch ( $data['mode'] ) + { + case 'fetch': + if ( !$template->theme_loaded ) + $template->load_theme(); + if ( !isset($data['have_template']) ) + { + $ret['template'] = file_get_contents(ENANO_ROOT . '/themes/' . $template->theme . '/comment.tpl'); + } + $q = $db->sql_query('SELECT c.comment_id,c.name,c.subject,c.comment_data,c.time,c.approved,u.user_level,u.user_id,u.signature FROM '.table_prefix.'comments AS c + LEFT JOIN '.table_prefix.'users AS u + ON (u.user_id=c.user_id) + WHERE page_id=\'' . $this->page_id . '\' + AND namespace=\'' . $this->namespace . '\';'); + $count_appr = 0; + $count_total = 0; + $count_unappr = 0; + $ret['comments'] = Array(); + if (!$q) + $db->die_json(); + if ( $row = $db->fetchrow() ) + { + do { + + // Increment counters + $count_total++; + ( $row['approved'] == 1 ) ? $count_appr++ : $count_unappr++; + + if ( !$this->perms->get_permissions('mod_comments') && $row['approved'] == 0 ) + continue; + + // Send the source + $row['comment_source'] = $row['comment_data']; + + // Format text + $row['comment_data'] = RenderMan::render($row['comment_data']); + + // Format date + $row['time'] = date('F d, Y h:i a', $row['time']); + + // Format signature + $row['signature'] = ( !empty($row['signature']) ) ? RenderMan::render($row['signature']) : ''; + + // Add the comment to the list + $ret['comments'][] = $row; + + } while ( $row = $db->fetchrow() ); + } + $db->free_result(); + $ret['count_appr'] = $count_appr; + $ret['count_total'] = $count_total; + $ret['count_unappr'] = $count_unappr; + $ret['auth_mod_comments'] = $this->perms->get_permissions('mod_comments'); + $ret['auth_post_comments'] = $this->perms->get_permissions('post_comments'); + $ret['auth_edit_comments'] = $this->perms->get_permissions('edit_comments'); + $ret['user_id'] = $session->user_id; + $ret['username'] = $session->username; + $ret['logged_in'] = $session->user_logged_in; + + $ret['user_level'] = Array(); + $ret['user_level']['guest'] = USER_LEVEL_GUEST; + $ret['user_level']['member'] = USER_LEVEL_MEMBER; + $ret['user_level']['mod'] = USER_LEVEL_MOD; + $ret['user_level']['admin'] = USER_LEVEL_ADMIN; + + $ret['approval_needed'] = ( getConfig('approve_comments') == '1' ); + $ret['guest_posting'] = getConfig('comments_need_login'); + + if ( $ret['guest_posting'] == '1' && !$session->user_logged_in ) + { + $session->kill_captcha(); + $ret['captcha'] = $session->make_captcha(); + } + break; + case 'edit': + $cid = (string)$data['id']; + if ( !preg_match('#^([0-9]+)$#i', $cid) || intval($cid) < 1 ) + { + echo '{"mode":"error","error":"HACKING ATTEMPT"}'; + return false; + } + $cid = intval($cid); + $q = $db->sql_query('SELECT c.user_id,c.approved FROM '.table_prefix.'comments c LEFT JOIN '.table_prefix.'users u ON (u.user_id=c.user_id) WHERE comment_id='.$cid.';'); + if(!$q) + $db->die_json(); + $row = $db->fetchrow(); + $uid = intval($row['user_id']); + $can_edit = ( ( $uid == $session->user_id && $uid != 1 && $this->perms->get_permissions('edit_comments') ) || ( $this->perms->get_permissions('mod_comments') ) ); + if(!$can_edit) + { + echo '{"mode":"error","error":"HACKING ATTEMPT"}'; + return false; + } + $data['data'] = str_replace("\r", '', $data['data']); // Windows compatibility + $text = RenderMan::preprocess_text($data['data'], true, false); + $text2 = $db->escape($text); + $subj = $db->escape(htmlspecialchars($data['subj'])); + $q = $db->sql_query('UPDATE '.table_prefix.'comments SET subject=\'' . $subj . '\',comment_data=\'' . $text2 . '\' WHERE comment_id=' . $cid . ';'); + if(!$q) + $db->die_json(); + $ret = Array( + 'mode' => 'redraw', + 'id' => $data['local_id'], + 'subj' => htmlspecialchars($data['subj']), + 'text' => RenderMan::render($text), + 'src' => $text, + 'approved' => $row['approved'] + ); + break; + case 'delete': + $cid = (string)$data['id']; + if ( !preg_match('#^([0-9]+)$#i', $cid) || intval($cid) < 1 ) + { + echo '{"mode":"error","error":"HACKING ATTEMPT"}'; + return false; + } + $cid = intval($cid); + $q = $db->sql_query('SELECT c.user_id FROM '.table_prefix.'comments c LEFT JOIN '.table_prefix.'users u ON (u.user_id=c.user_id) WHERE comment_id='.$cid.';'); + if(!$q) + $db->die_json(); + $row = $db->fetchrow(); + $uid = intval($row['user_id']); + $can_edit = ( ( $uid == $session->user_id && $uid != 1 && $this->perms->get_permissions('edit_comments') ) || ( $this->perms->get_permissions('mod_comments') ) ); + if(!$can_edit) + { + echo '{"mode":"error","error":"HACKING ATTEMPT"}'; + return false; + } + $q = $db->sql_query('DELETE FROM '.table_prefix.'comments WHERE comment_id='.$cid.';'); + if(!$q) + $db->die_json(); + $ret = Array( + 'mode' => 'annihilate', + 'id' => $data['local_id'] + ); + break; + case 'submit': + + // Now for a huge round of security checks... + + $errors = Array(); + + // Authorization + // Like the rest of the ACL system, this call is a one-stop check for ALL ACL entries. + if ( !$this->perms->get_permissions('post_comments') ) + $errors[] = 'An ACL entry is preventing the comment from being posted.'; + + // Guest authorization + if ( getConfig('comments_need_login') == '2' && !$session->user_logged_in ) + $errors[] = 'You need to log in before posting comments.'; + + // CAPTCHA code + if ( getConfig('comments_need_login') == '1' && !$session->user_logged_in ) + { + $real_code = $session->get_captcha($data['captcha_id']); + if ( $real_code != $data['captcha_code'] ) + $errors[] = 'The confirmation code you entered was incorrect.'; + } + + if ( count($errors) > 0 ) + { + $ret = Array( + 'mode' => 'error', + 'error' => implode("\n", $errors) + ); + } + else + { + // We're authorized! + + // Preprocess + $name = ( $session->user_logged_in ) ? htmlspecialchars($session->username) : htmlspecialchars($data['name']); + $subj = htmlspecialchars($data['subj']); + $text = RenderMan::preprocess_text($data['text'], true, false); + $src = $text; + $sql_text = $db->escape($text); + $text = RenderMan::render($text); + $appr = ( getConfig('approve_comments') == '1' ) ? '0' : '1'; + $time = time(); + $date = date('F d, Y h:i a', $time); + + // Send it to the database + $q = $db->sql_query('INSERT INTO '.table_prefix.'comments(page_id,namespace,name,subject,comment_data,approved, time, user_id) VALUES' . + "('$this->page_id', '$this->namespace', '$name', '$subj', '$sql_text', $appr, $time, $session->user_id);"); + if(!$q) + $db->die_json(); + + // Re-fetch + $q = $db->sql_query('SELECT c.comment_id,c.name,c.subject,c.comment_data,c.time,c.approved,u.user_level,u.user_id,u.signature FROM '.table_prefix.'comments AS c + LEFT JOIN '.table_prefix.'users AS u + ON (u.user_id=c.user_id) + WHERE page_id=\'' . $this->page_id . '\' + AND namespace=\'' . $this->namespace . '\' + AND time='.$time.' ORDER BY comment_id DESC LIMIT 1;'); + if(!$q) + $db->die_json(); + + $row = $db->fetchrow(); + $db->free_result(); + $row['time'] = $date; + $row['comment_data'] = $text; + $row['comment_source'] = $src; + $ret = Array( + 'mode' => 'materialize' + ); + $ret = enano_safe_array_merge($ret, $row); + + $ret['auth_mod_comments'] = $this->perms->get_permissions('mod_comments'); + $ret['auth_post_comments'] = $this->perms->get_permissions('post_comments'); + $ret['auth_edit_comments'] = $this->perms->get_permissions('edit_comments'); + $ret['user_id'] = $session->user_id; + $ret['username'] = $session->username; + $ret['logged_in'] = $session->user_logged_in; + $ret['signature'] = RenderMan::render($row['signature']); + + $ret['user_level_list'] = Array(); + $ret['user_level_list']['guest'] = USER_LEVEL_GUEST; + $ret['user_level_list']['member'] = USER_LEVEL_MEMBER; + $ret['user_level_list']['mod'] = USER_LEVEL_MOD; + $ret['user_level_list']['admin'] = USER_LEVEL_ADMIN; + + } + + break; + case 'approve': + if ( !$this->perms->get_permissions('mod_comments') ) + { + $ret = Array( + 'mode' => 'error', + 'error' => 'You are not authorized to moderate comments.' + ); + echo $parser->encode($ret); + return $ret; + } + + $cid = (string)$data['id']; + if ( !preg_match('#^([0-9]+)$#i', $cid) || intval($cid) < 1 ) + { + echo '{"mode":"error","error":"HACKING ATTEMPT"}'; + return false; + } + $cid = intval($cid); + $q = $db->sql_query('SELECT subject,approved FROM '.table_prefix.'comments WHERE comment_id='.$cid.';'); + if(!$q || $db->numrows() < 1) + $db->die_json(); + $row = $db->fetchrow(); + $db->free_result(); + $appr = ( $row['approved'] == '1' ) ? '0' : '1'; + $q = $db->sql_query('UPDATE '.table_prefix."comments SET approved=$appr WHERE comment_id=$cid;"); + if (!$q) + $db->die_json(); + + $ret = Array( + 'mode' => 'redraw', + 'approved' => $appr, + 'subj' => $row['subject'], + 'id' => $data['local_id'] + ); + + break; + default: + $ret = Array( + 'mode' => 'error', + 'error' => $data['mode'] . ' is not a valid request mode' + ); + break; + } + echo $parser->encode($ret); + return $ret; + } + +} // class Comments + diff -r 902822492a68 -r fe660c52c48f includes/common.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/common.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,228 @@ + + Hacking Attempt

Hacking attempt using PHP $GLOBALS overwrite vulnerability detected, reported to admin

You're worse than this guy! Unless you are this guy...

Powered by Enano and valid XHTML 1.1

+ Powered by debugConsole'); +dc_here('common: including files'); +require_once(ENANO_ROOT.'/includes/functions.php'); +require_once(ENANO_ROOT.'/includes/dbal.php'); +require_once(ENANO_ROOT.'/includes/paths.php'); +require_once(ENANO_ROOT.'/includes/sessions.php'); +require_once(ENANO_ROOT.'/includes/template.php'); +require_once(ENANO_ROOT.'/includes/plugins.php'); +require_once(ENANO_ROOT.'/includes/comment.php'); +require_once(ENANO_ROOT.'/includes/wikiformat.php'); +require_once(ENANO_ROOT.'/includes/diff.php'); +require_once(ENANO_ROOT.'/includes/render.php'); +require_once(ENANO_ROOT.'/includes/stats.php'); +require_once(ENANO_ROOT.'/includes/pageutils.php'); +require_once(ENANO_ROOT.'/includes/js-compressor.php'); +require_once(ENANO_ROOT.'/includes/rijndael.php'); +require_once(ENANO_ROOT.'/includes/email.php'); +require_once(ENANO_ROOT.'/includes/search.php'); +require_once(ENANO_ROOT.'/includes/json.php'); +require_once(ENANO_ROOT.'/includes/wikiengine/Tables.php'); +require_once(ENANO_ROOT.'/includes/pageprocess.php'); + +strip_magic_quotes_gpc(); + +// Enano has five parts: the database abstraction layer (DBAL), the session manager, the path/URL manager, the template engine, and the plugin manager. +// Each part has its own class and a global var; nearly all Enano functions are handled by one of these five components. + +global $db, $session, $paths, $template, $plugins; // Common objects +global $enano_config; // A global used to cache config information without making loads of queries ;-) + // In addition, $enano_config is used to fetch config information if die_semicritical() is called. + +global $email; + +if(!isset($_SERVER['HTTP_HOST'])) grinding_halt('Cannot get hostname', '

Your web browser did not provide the HTTP Host: field. This site requires a modern browser that supports the HTTP 1.1 standard.

'); + +$db = new mysql(); +dc_here('common: calling $db->connect();'); +$db->connect(); // Redirects to install.php if an installation is not detected + +if(strstr(contentPath, '?')) $sep = '&'; +else $sep = '?'; +define('urlSeparator', $sep); +unset($sep); // save 10 bytes of memory... + +// See if any diagnostic actions have been requested +if ( isset($_GET['do']) && $_GET['do'] == 'diag' && isset($_GET['sub']) ) +{ + switch($_GET['sub']) + { + case 'cookie_destroy': + unset($_COOKIE['sid']); + setcookie('sid', '', time()-3600*24, scriptPath); + setcookie('sid', '', time()-3600*24, scriptPath.'/'); + die('Session cookie cleared. Continue'); + break; + } +} + +// Select and fetch the site configuration +dc_here('common: selecting global config data'); +$e = $db->sql_query('SELECT config_name, config_value FROM '.table_prefix.'config;'); +if(!$e) $db->_die('Some critical configuration information could not be selected.'); +else define('ENANO_CONFIG_FETCHED', ''); // Used in die_semicritical to figure out whether to call getConfig() or not + +dc_here('common: fetching $enano_config'); +$enano_config = Array(); +while($r = $db->fetchrow()) +{ + $enano_config[$r['config_name']] = $r['config_value']; +} + +$db->free_result(); + +if(enano_version(false, true) != $version) +{ + grinding_halt('Version mismatch', '

It seems that the Enano release we\'re trying to run ('.$version.') is different from the version specified in your database ('.enano_version().'). Perhaps you need to upgrade?

'); +} + +// Our list of tables included in Enano +$system_table_list = Array( + table_prefix.'categories', + table_prefix.'comments', + table_prefix.'config', + table_prefix.'logs', + table_prefix.'page_text', + table_prefix.'session_keys', + table_prefix.'pages', + table_prefix.'users', + table_prefix.'themes', + table_prefix.'buddies', + table_prefix.'banlist', + table_prefix.'files', + table_prefix.'privmsgs', + table_prefix.'sidebar', + table_prefix.'hits', + table_prefix.'search_index', + table_prefix.'groups', + table_prefix.'group_members', + table_prefix.'acl', + table_prefix.'search_cache' + ); + +dc_here('common: initializing base classes'); +$plugins = new pluginLoader(); + +// So where does the majority of Enano get executed? How about the next nine lines of code :) +dc_here('common: ok, we\'re set up, starting mainstream execution'); + +$plugins->loadAll(); +dc_here('common: loading plugins'); + global $plugins; + foreach($plugins->load_list as $f) { include_once $f; } // Can't be in object context when this is done + +$session = new sessionManager(); +$paths = new pathManager(); +$template = new template(); +$email = new EmailEncryptor(); + +define('ENANO_BASE_CLASSES_INITIALIZED', ''); + +$code = $plugins->setHook('base_classes_initted'); +foreach ( $code as $cmd ) +{ + eval($cmd); +} + +$p = RenderMan::strToPageId($paths->get_pageid_from_url()); +if( ( $p[1] == 'Admin' || $p[1] == 'Special' ) && function_exists('page_'.$p[1].'_'.$p[0].'_preloader')) +{ + @call_user_func('page_'.$p[1].'_'.$p[0].'_preloader'); +} + +$session->start(); +$paths->init(); + +define('ENANO_MAINSTREAM', ''); + +// If the site is disabled, bail out, unless we're trying to log in or administer the site +if(getConfig('site_disabled') == '1') +{ + if ( $paths->namespace == 'Admin' || ( $paths->namespace == 'Special' && ( $paths->cpage['urlname_nons'] == 'CSS' || $paths->cpage['urlname_nons'] == 'Administration' || $paths->cpage['urlname_nons'] == 'Login' ) ) ) + { + // do nothing; allow execution to continue + } + else + { + if(!$n = getConfig('site_disabled_notice')) $n = 'The administrator has disabled the site. Please check back later.'; + + $text = RenderMan::render($n) . ' +
+ If you have an administrative account, you may log in to the site or use the administration panel. +
'; + $paths->wiki_mode = 0; + die_semicritical('Site disabled', $text); + } +} + +$code = $plugins->setHook('session_started'); +foreach ( $code as $cmd ) +{ + eval($cmd); +} + +if(isset($_GET['noheaders'])) $template->no_headers = true; + +?> diff -r 902822492a68 -r fe660c52c48f includes/common.php~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/common.php~ Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,227 @@ + + Hacking Attempt

Hacking attempt using PHP $GLOBALS overwrite vulnerability detected, reported to admin

You're worse than this guy! Unless you are this guy...

Powered by Enano and valid XHTML 1.1

+ Powered by debugConsole'); +dc_here('common: including files'); +require_once(ENANO_ROOT.'/includes/functions.php'); +require_once(ENANO_ROOT.'/includes/dbal.php'); +require_once(ENANO_ROOT.'/includes/paths.php'); +require_once(ENANO_ROOT.'/includes/sessions.php'); +require_once(ENANO_ROOT.'/includes/template.php'); +require_once(ENANO_ROOT.'/includes/plugins.php'); +require_once(ENANO_ROOT.'/includes/comment.php'); +require_once(ENANO_ROOT.'/includes/wikiformat.php'); +require_once(ENANO_ROOT.'/includes/diff.php'); +require_once(ENANO_ROOT.'/includes/render.php'); +require_once(ENANO_ROOT.'/includes/stats.php'); +require_once(ENANO_ROOT.'/includes/pageutils.php'); +require_once(ENANO_ROOT.'/includes/js-compressor.php'); +require_once(ENANO_ROOT.'/includes/rijndael.php'); +require_once(ENANO_ROOT.'/includes/email.php'); +require_once(ENANO_ROOT.'/includes/search.php'); +require_once(ENANO_ROOT.'/includes/json.php'); +require_once(ENANO_ROOT.'/includes/wikiengine/Tables.php'); + +strip_magic_quotes_gpc(); + +// Enano has five parts: the database abstraction layer (DBAL), the session manager, the path/URL manager, the template engine, and the plugin manager. +// Each part has its own class and a global var; nearly all Enano functions are handled by one of these five components. + +global $db, $session, $paths, $template, $plugins; // Common objects +global $enano_config; // A global used to cache config information without making loads of queries ;-) + // In addition, $enano_config is used to fetch config information if die_semicritical() is called. + +global $email; + +if(!isset($_SERVER['HTTP_HOST'])) grinding_halt('Cannot get hostname', '

Your web browser did not provide the HTTP Host: field. This site requires a modern browser that supports the HTTP 1.1 standard.

'); + +$db = new mysql(); +dc_here('common: calling $db->connect();'); +$db->connect(); // Redirects to install.php if an installation is not detected + +if(strstr(contentPath, '?')) $sep = '&'; +else $sep = '?'; +define('urlSeparator', $sep); +unset($sep); // save 10 bytes of memory... + +// See if any diagnostic actions have been requested +if ( isset($_GET['do']) && $_GET['do'] == 'diag' && isset($_GET['sub']) ) +{ + switch($_GET['sub']) + { + case 'cookie_destroy': + unset($_COOKIE['sid']); + setcookie('sid', '', time()-3600*24, scriptPath); + setcookie('sid', '', time()-3600*24, scriptPath.'/'); + die('Session cookie cleared. Continue'); + break; + } +} + +// Select and fetch the site configuration +dc_here('common: selecting global config data'); +$e = $db->sql_query('SELECT config_name, config_value FROM '.table_prefix.'config;'); +if(!$e) $db->_die('Some critical configuration information could not be selected.'); +else define('ENANO_CONFIG_FETCHED', ''); // Used in die_semicritical to figure out whether to call getConfig() or not + +dc_here('common: fetching $enano_config'); +$enano_config = Array(); +while($r = $db->fetchrow()) +{ + $enano_config[$r['config_name']] = $r['config_value']; +} + +$db->free_result(); + +if(enano_version(false, true) != $version) +{ + grinding_halt('Version mismatch', '

It seems that the Enano release we\'re trying to run ('.$version.') is different from the version specified in your database ('.enano_version().'). Perhaps you need to upgrade?

'); +} + +// Our list of tables included in Enano +$system_table_list = Array( + table_prefix.'categories', + table_prefix.'comments', + table_prefix.'config', + table_prefix.'logs', + table_prefix.'page_text', + table_prefix.'session_keys', + table_prefix.'pages', + table_prefix.'users', + table_prefix.'themes', + table_prefix.'buddies', + table_prefix.'banlist', + table_prefix.'files', + table_prefix.'privmsgs', + table_prefix.'sidebar', + table_prefix.'hits', + table_prefix.'search_index', + table_prefix.'groups', + table_prefix.'group_members', + table_prefix.'acl', + table_prefix.'search_cache' + ); + +dc_here('common: initializing base classes'); +$plugins = new pluginLoader(); + +// So where does the majority of Enano get executed? How about the next nine lines of code :) +dc_here('common: ok, we\'re set up, starting mainstream execution'); + +$plugins->loadAll(); +dc_here('common: loading plugins'); + global $plugins; + foreach($plugins->load_list as $f) { include_once $f; } // Can't be in object context when this is done + +$session = new sessionManager(); +$paths = new pathManager(); +$template = new template(); +$email = new EmailEncryptor(); + +define('ENANO_BASE_CLASSES_INITIALIZED', ''); + +$code = $plugins->setHook('base_classes_initted'); +foreach ( $code as $cmd ) +{ + eval($cmd); +} + +$p = RenderMan::strToPageId($paths->get_pageid_from_url()); +if( ( $p[1] == 'Admin' || $p[1] == 'Special' ) && function_exists('page_'.$p[1].'_'.$p[0].'_preloader')) +{ + @call_user_func('page_'.$p[1].'_'.$p[0].'_preloader'); +} + +$session->start(); +$paths->init(); + +define('ENANO_MAINSTREAM', ''); + +// If the site is disabled, bail out, unless we're trying to log in or administer the site +if(getConfig('site_disabled') == '1') +{ + if ( $paths->namespace == 'Admin' || ( $paths->namespace == 'Special' && ( $paths->cpage['urlname_nons'] == 'CSS' || $paths->cpage['urlname_nons'] == 'Administration' || $paths->cpage['urlname_nons'] == 'Login' ) ) ) + { + // do nothing; allow execution to continue + } + else + { + if(!$n = getConfig('site_disabled_notice')) $n = 'The administrator has disabled the site. Please check back later.'; + + $text = RenderMan::render($n) . ' +
+ If you have an administrative account, you may log in to the site or use the administration panel. +
'; + $paths->wiki_mode = 0; + die_semicritical('Site disabled', $text); + } +} + +$code = $plugins->setHook('session_started'); +foreach ( $code as $cmd ) +{ + eval($cmd); +} + +if(isset($_GET['noheaders'])) $template->no_headers = true; + +?> diff -r 902822492a68 -r fe660c52c48f includes/constants.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/constants.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,420 @@ + 'application/postscript', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'au' => 'audio/basic', + 'avi' => 'video/x-msvideo', + 'bcpio' => 'application/x-bcpio', + 'bin' => 'application/octet-stream', + 'bmp' => 'image/bmp', + 'bz2' => 'application/x-bzip', + 'cdf' => 'application/x-netcdf', + 'cgm' => 'image/cgm', + 'class' => 'application/octet-stream', + 'cpio' => 'application/x-cpio', + 'cpt' => 'application/mac-compactpro', + 'csh' => 'application/x-csh', + 'css' => 'text/css', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'doc' => 'application/msword', + 'dtd' => 'application/xml-dtd', + 'dvi' => 'application/x-dvi', + 'dxr' => 'application/x-director', + 'eps' => 'application/postscript', + 'etx' => 'text/x-setext', + 'exe' => 'application/octet-stream', + 'ez' => 'application/andrew-inset', + 'gif' => 'image/gif', + 'gram' => 'application/srgs', + 'grxml' => 'application/srgs+xml', + 'gtar' => 'application/x-gtar', + 'gz' => 'application/x-gzip', + 'hdf' => 'application/x-hdf', + 'hqx' => 'application/mac-binhex40', + 'htm' => 'text/html', + 'html' => 'text/html', + 'ice' => 'x-conference/x-cooltalk', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ief' => 'image/ief', + 'ifb' => 'text/calendar', + 'iges' => 'model/iges', + 'igs' => 'model/iges', + 'jar' => 'application/zip', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'js' => 'application/x-javascript', + 'kar' => 'audio/midi', + 'latex' => 'application/x-latex', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'm3u' => 'audio/x-mpegurl', + 'man' => 'application/x-troff-man', + 'mathml' => 'application/mathml+xml', + 'me' => 'application/x-troff-me', + 'mesh' => 'model/mesh', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mif' => 'application/vnd.mif', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpga' => 'audio/mpeg', + 'ms' => 'application/x-troff-ms', + 'msh' => 'model/mesh', + 'mxu' => 'video/vnd.mpegurl', + 'nc' => 'application/x-netcdf', + 'oda' => 'application/oda', + 'ogg' => 'application/ogg', + 'ogm' => 'application/ogg', + 'pbm' => 'image/x-portable-bitmap', + 'pdb' => 'chemical/x-pdb', + 'pdf' => 'application/pdf', + 'pgm' => 'image/x-portable-graymap', + 'pgn' => 'application/x-chess-pgn', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'ppm' => 'image/x-portable-pixmap', + 'ppt' => 'application/vnd.ms-powerpoint', + 'ps' => 'application/postscript', + 'psd' => 'image/x-photoshop', + 'qt' => 'video/quicktime', + 'ra' => 'audio/x-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'ras' => 'image/x-cmu-raster', + 'rdf' => 'text/xml', + 'rgb' => 'image/x-rgb', + 'rm' => 'audio/x-pn-realaudio', + 'roff' => 'application/x-troff', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'rss' => 'text/xml', + 'rtf' => 'text/rtf', + 'rtx' => 'text/richtext', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'silo' => 'model/mesh', + 'sit' => 'application/x-stuffit', + 'skd' => 'application/x-koan', + 'skm' => 'application/x-koan', + 'skp' => 'application/x-koan', + 'skt' => 'application/x-koan', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'snd' => 'audio/basic', + 'so' => 'application/octet-stream', + 'spl' => 'application/x-futuresplash', + 'src' => 'application/x-wais-source', + 'stc' => 'application/zip', + 'std' => 'application/zip', + 'sti' => 'application/zip', + 'stm' => 'application/zip', + 'stw' => 'application/zip', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svg' => 'image/svg+xml', + 'swf' => 'application/x-shockwave-flash', + 'sxc' => 'application/zip', + 'sxd' => 'application/zip', + 'sxi' => 'application/zip', + 'sxm' => 'application/zip', + 'sxw' => 'application/zip', + 't' => 'application/x-troff', + 'tar' => 'application/x-tar', + 'tcl' => 'application/x-tcl', + 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'tr' => 'application/x-troff', + 'tsv' => 'text/tab-separated-values', + 'txt' => 'text/plain', + 'ustar' => 'application/x-ustar', + 'vcd' => 'application/x-cdlink', + 'vrml' => 'model/vrml', + 'vxml' => 'application/voicexml+xml', + 'wav' => 'audio/x-wav', + 'wbmp' => 'image/vnd.wap.wbmp', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wml' => 'text/vnd.wap.wml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmls' => 'text/vnd.wap.wmlscript', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wrl' => 'model/vrml', + 'xbm' => 'image/x-xbitmap', + 'xcf' => 'image/xcf', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xls' => 'application/vnd.ms-excel', + 'xml' => 'text/xml', + 'xpi' => 'application/zip', + 'xpm' => 'image/x-xpixmap', + 'xsl' => 'text/xml', + 'xslt' => 'text/xml', + 'xwd' => 'image/x-xwindowdump', + 'xyz' => 'chemical/x-xyz', + 'zip' => 'application/zip', +); + +$mimetype_extlist = Array( + 'application/andrew-inset'=>'ez', + 'application/mac-binhex40'=>'hqx', + 'application/mac-compactpro'=>'cpt', + 'application/mathml+xml'=>'mathml', + 'application/msword'=>'doc', + 'application/octet-stream'=>'bin dms lha lzh exe class so dll', + 'application/oda'=>'oda', + 'application/ogg'=>'ogg ogm', + 'application/pdf'=>'pdf', + 'application/postscript'=>'ai eps ps', + 'application/rdf+xml'=>'rdf', + 'application/smil'=>'smi smil', + 'application/srgs'=>'gram', + 'application/srgs+xml'=>'grxml', + 'application/vnd.mif'=>'mif', + 'application/vnd.ms-excel'=>'xls', + 'application/vnd.ms-powerpoint'=>'ppt', + 'application/vnd.wap.wbxml'=>'wbxml', + 'application/vnd.wap.wmlc'=>'wmlc', + 'application/vnd.wap.wmlscriptc'=>'wmlsc', + 'application/voicexml+xml'=>'vxml', + 'application/x-bcpio'=>'bcpio', + 'application/x-bzip'=>'gz bz2', + 'application/x-cdlink'=>'vcd', + 'application/x-chess-pgn'=>'pgn', + 'application/x-cpio'=>'cpio', + 'application/x-csh'=>'csh', + 'application/x-director'=>'dcr dir dxr', + 'application/x-dvi'=>'dvi', + 'application/x-futuresplash'=>'spl', + 'application/x-gtar'=>'gtar tar', + 'application/x-gzip'=>'gz', + 'application/x-hdf'=>'hdf', + 'application/x-jar'=>'jar', + 'application/x-javascript'=>'js', + 'application/x-koan'=>'skp skd skt skm', + 'application/x-latex'=>'latex', + 'application/x-netcdf'=>'nc cdf', + 'application/x-sh'=>'sh', + 'application/x-shar'=>'shar', + 'application/x-shockwave-flash'=>'swf', + 'application/x-stuffit'=>'sit', + 'application/x-sv4cpio'=>'sv4cpio', + 'application/x-sv4crc'=>'sv4crc', + 'application/x-tar'=>'tar', + 'application/x-tcl'=>'tcl', + 'application/x-tex'=>'tex', + 'application/x-texinfo'=>'texinfo texi', + 'application/x-troff'=>'t tr roff', + 'application/x-troff-man'=>'man', + 'application/x-troff-me'=>'me', + 'application/x-troff-ms'=>'ms', + 'application/x-ustar'=>'ustar', + 'application/x-wais-source'=>'src', + 'application/x-xpinstall'=>'xpi', + 'application/xhtml+xml'=>'xhtml xht', + 'application/xslt+xml'=>'xslt', + 'application/xml'=>'xml xsl', + 'application/xml-dtd'=>'dtd', + 'application/zip'=>'zip jar xpi sxc stc sxd std sxi sti sxm stm sxw stw ', + 'audio/basic'=>'au snd', + 'audio/midi'=>'mid midi kar', + 'audio/mpeg'=>'mpga mp2 mp3', + 'audio/ogg'=>'ogg ', + 'audio/x-aiff'=>'aif aiff aifc', + 'audio/x-mpegurl'=>'m3u', + 'audio/x-ogg'=>'ogg ', + 'audio/x-pn-realaudio'=>'ram rm', + 'audio/x-pn-realaudio-plugin'=>'rpm', + 'audio/x-realaudio'=>'ra', + 'audio/x-wav'=>'wav', + 'chemical/x-pdb'=>'pdb', + 'chemical/x-xyz'=>'xyz', + 'image/bmp'=>'bmp', + 'image/cgm'=>'cgm', + 'image/gif'=>'gif', + 'image/ief'=>'ief', + 'image/jpeg'=>'jpeg jpg jpe', + 'image/png'=>'png', + 'image/svg+xml'=>'svg', + 'image/tiff'=>'tiff tif', + 'image/vnd.djvu'=>'djvu djv', + 'image/vnd.wap.wbmp'=>'wbmp', + 'image/x-cmu-raster'=>'ras', + 'image/x-icon'=>'ico', + 'image/x-portable-anymap'=>'pnm', + 'image/x-portable-bitmap'=>'pbm', + 'image/x-portable-graymap'=>'pgm', + 'image/x-portable-pixmap'=>'ppm', + 'image/x-rgb'=>'rgb', + 'image/x-photoshop'=>'psd', + 'image/x-xbitmap'=>'xbm', + 'image/x-xpixmap'=>'xpm', + 'image/x-xwindowdump'=>'xwd', + 'model/iges'=>'igs iges', + 'model/mesh'=>'msh mesh silo', + 'model/vrml'=>'wrl vrml', + 'text/calendar'=>'ics ifb', + 'text/css'=>'css', + 'text/html'=>'html htm', + 'text/plain'=>'txt', + 'text/richtext'=>'rtx', + 'text/rtf'=>'rtf', + 'text/sgml'=>'sgml sgm', + 'text/tab-separated-values'=>'tsv', + 'text/vnd.wap.wml'=>'wml', + 'text/vnd.wap.wmlscript'=>'wmls', + 'text/xml'=>'xml xsl xslt rss rdf', + 'text/x-setext'=>'etx', + 'video/mpeg'=>'mpeg mpg mpe', + 'video/ogg'=>'ogm ogg', + 'video/quicktime'=>'qt mov', + 'video/vnd.mpegurl'=>'mxu', + 'video/x-msvideo'=>'avi', + 'video/x-ogg'=>'ogm ogg', + 'video/x-sgi-movie'=>'movie', + 'x-conference/x-cooltalk'=>'ice', + // Added for Enano + 'image/xcf' => 'xcf xcfbz2 xcf.bz2', +); + +$k = array_keys($mime_types); +$mimetype_exps = Array(); +foreach($k as $s => $x) +{ + $mimetype_exps[$x] = pow(2, $s); +} + +unset($k, $s, $x); diff -r 902822492a68 -r fe660c52c48f includes/dbal.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/dbal.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,598 @@ +$errtype: $errstr
Error source:
$debug
"; +} + +class mysql { + var $num_queries, $query_backtrace, $latest_result, $latest_query, $_conn, $sql_stack_fields, $sql_stack_values; + var $row = array(); + var $rowset = array(); + var $errhandler; + + function enable_errorhandler() + { + if ( function_exists('debug_backtrace') ) + { + $this->errhandler = set_error_handler('db_error_handler'); + } + } + + function disable_errorhandler() + { + if ( $this->errhandler ) + { + set_error_handler($this->errhandler); + } + else + { + restore_error_handler(); + } + } + + function sql_backtrace() { + $qb = explode("\n", $this->query_backtrace); + $bt = ''; + //for($i=sizeof($qb)-1;$i>=0;$i--) { + for($i=0;$i_conn) + { + $this->connect(); + } + } + + function _die($t = '') { + if(defined('ENANO_HEADERS_SENT')) { + ob_clean(); + } + header('HTTP/1.1 500 Internal Server Error'); + $bt = $this->sql_backtrace(); + $e = htmlspecialchars(mysql_error()); + if($e=='') $e='<none>'; + if(defined('ENANO_CONFIG_FETCHED')) die_semicritical('Database error', '

An error occurred during a database query.

'.$t.'
Error returned by MySQL: '.$e.'
SQL Backtrace:

'.$bt.'
'); + else grinding_halt('Database error', '

An error occurred during a database query.

'.$t.'
Error returned by MySQL: '.$e.'
SQL Backtrace:

'.$bt.'
'); + exit; + } + + function die_json() + { + $e = addslashes(htmlspecialchars(mysql_error())); + $q = addslashes($this->latest_query); + $t = "{'mode':'error','error':'An error occurred during database query.\nQuery was:\n $q\n\nError returned by MySQL: $e'}"; + die($t); + } + + function get_error($t = '') { + header('HTTP/1.1 500 Internal Server Error'); + $bt = $this->sql_backtrace(); + $e = htmlspecialchars(mysql_error()); + if($e=='') $e='<none>'; + $text = '

An error occurred during a database query.

'.$t.'
Error returned by MySQL: '.$e.'
SQL Backtrace:

'.$bt.'
'; + return $text; + } + + function connect() { + $this->enable_errorhandler(); + dc_here('dbal: trying to connect....'); + @include(ENANO_ROOT.'/config.php'); + if(isset($crypto_key)) + unset($crypto_key); // Get this sucker out of memory fast + if(!defined('ENANO_INSTALLED') && !defined('MIDGET_INSTALLED') && !defined('IN_ENANO_INSTALL') ) + { + dc_here('dbal: oops, looks like Enano isn\'t set up. Constants ENANO_INSTALLED, MIDGET_INSTALLED, and IN_ENANO_INSTALL are all undefined.'); + header('Location: install.php'); + exit; + } + $this->_conn = @mysql_connect($dbhost, $dbuser, $dbpasswd); + unset($dbuser); + unset($dbpasswd); // Security + if(!$this->_conn) { dc_here('dbal: uhoh!
'.mysql_error()); grinding_halt('Enano is having a problem', '

Error: couldn\'t connect to MySQL.
'.mysql_error().'

'); } + $this->query_backtrace = ''; + $this->num_queries = 0; + dc_here('dbal: we\'re in, selecting database...'); + $q = $this->sql_query('USE '.$dbname.';'); + if(!$q) $this->_die('The database could not be selected.'); + dc_here('dbal: connected to MySQL'); + $this->disable_errorhandler(); + } + + function sql_query($q) { + $this->enable_errorhandler(); + $this->num_queries++; + $this->query_backtrace .= $q."\n"; + $this->latest_query = $q; + dc_here('dbal: making SQL query:
'.$q.''); + if(!$this->_conn) $this->_die('A database connection has not yet been established.'); + if(!$this->check_query($q)) + { + $this->report_query($q); + grinding_halt('SQL Injection attempt', '

Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.

Query was:

'.htmlspecialchars($q).'
'); + } + $r = mysql_query($q, $this->_conn); + $this->latest_result = $r; + $this->disable_errorhandler(); + return $r; + } + + function sql_unbuffered_query($q) { + $this->enable_errorhandler(); + $this->num_queries++; + $this->query_backtrace .= '(UNBUFFERED) ' . $q."\n"; + $this->latest_query = $q; + dc_here('dbal: making SQL query:
'.$q.''); + if(!$this->_conn) $this->_die('A database connection has not yet been established.'); + if(!$this->check_query($q)) + { + $this->report_query($q); + grinding_halt('SQL Injection attempt', '

Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.

Query was:

'.htmlspecialchars($q).'
'); + } + $r = mysql_unbuffered_query($q, $this->_conn); + $this->latest_result = $r; + $this->disable_errorhandler(); + return $r; + } + + /** + * Checks a SQL query for possible signs of injection attempts + * @param string $q the query to check + * @return bool true if query passed check, otherwise false + */ + + function check_query($q, $debug = false) + { + if($debug) echo "\$db->check_query(): checking query: ".htmlspecialchars($q).'
'."\n"; + $sz = strlen($q); + $quotechar = false; + $quotepos = 0; + $prev_is_quote = false; + $just_started = false; + for($i=0;$i'); + $q = substr($q, 0, $quotepos) . 'SAFE_QUOTE' . substr($q, $i + 1, strlen($q)); + if($debug) echo('$db->check_query(): Filtered query: '.$q.'
'); + $i = $quotepos; + } + } + else + { + $quotechar = $c; + $quotepos = $i; + $just_entered = true; + } + if($debug) echo '$db->check_query(): found quote char as pos: '.$i.'
'; + continue; + } + $just_entered = false; + } + if(substr(trim($q), strlen(trim($q))-1, 1) == ';') $q = substr(trim($q), 0, strlen(trim($q))-1); + for($i=0;$i'; + else $e .= $c; + } + echo 'Injection attempt caught at pos: '.$i.'
'; + } + return false; + } + } + return true; + } + + /** + * Set the internal result pointer to X + * @param int $pos The number of the row + * @param resource $result The MySQL result resource - if not given, the latest cached query is assumed + * @return true on success, false on failure + */ + + function sql_data_seek($pos, $result = false) + { + $this->enable_errorhandler(); + if(!$result) + $result = $this->latest_result; + if(!$result) + { + $this->disable_errorhandler(); + return false; + } + if(mysql_data_seek($result, $pos)) + { + $this->disable_errorhandler(); + return true; + } + else + { + $this->disable_errorhandler(); + return false; + } + } + + /** + * Reports a bad query to the admin + * @param string $query the naughty query + * @access private + */ + + function report_query($query) + { + global $session; + if(is_object($session) && defined('ENANO_MAINSTREAM')) + $username = $session->username; + else + $username = 'Unavailable'; + $query = $this->escape($query); + $q = $this->sql_query('INSERT INTO '.table_prefix.'logs(log_type, action, time_id, date_string, page_text, author, edit_summary) + VALUES(\'security\', \'sql_inject\', '.time().', \'\', \''.$query.'\', \''.$username.'\', \''.$_SERVER['REMOTE_ADDR'].'\');'); + } + + function fetchrow($r = false) { + $this->enable_errorhandler(); + if(!$this->_conn) return false; + if(!$r) $r = $this->latest_result; + if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.'); + $row = mysql_fetch_assoc($r); + $this->disable_errorhandler(); + return $row; + } + + function fetchrow_num($r = false) { + $this->enable_errorhandler(); + if(!$r) $r = $this->latest_result; + if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.'); + $row = mysql_fetch_row($r); + $this->disable_errorhandler(); + return $row; + } + + function numrows($r = false) { + $this->enable_errorhandler(); + if(!$r) $r = $this->latest_result; + if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.'); + $n = mysql_num_rows($r); + $this->disable_errorhandler(); + return $n; + } + + function escape($str) + { + $this->enable_errorhandler(); + $str = mysql_real_escape_string($str); + $this->disable_errorhandler(); + return $str; + } + + function free_result($result = false) + { + $this->enable_errorhandler(); + if(!$result) + $result = $this->latest_result; + if(!$result) + { + $this->disable_errorhandler(); + return null; + } + mysql_free_result($result); + $this->disable_errorhandler(); + return null; + } + + function close() { + dc_here('dbal: closing MySQL connection'); + mysql_close($this->_conn); + unset($this->_conn); + } + + // phpBB DBAL compatibility + function sql_fetchrow($r = false) + { + return $this->fetchrow($r); + } + function sql_freeresult($r = false) + { + if(!$this->_conn) return false; + if(!$r) $r = $this->latest_result; + if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.'); + mysql_free_result($r); + } + function sql_numrows($r = false) + { + if(!$this->_conn) return false; + if(!$r) $r = $this->latest_result; + if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.'); + return mysql_num_rows($r); + } + function sql_affectedrows($r = false, $f, $n) + { + if(!$this->_conn) return false; + if(!$r) $r = $this->latest_result; + if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.'); + return mysql_affected_rows(); + } + + function sql_type_cast(&$value) + { + if ( is_float($value) ) + { + return doubleval($value); + } + if ( is_integer($value) || is_bool($value) ) + { + return intval($value); + } + if ( is_string($value) || empty($value) ) + { + return '\'' . $this->sql_escape_string($value) . '\''; + } + // uncastable var : let's do a basic protection on it to prevent sql injection attempt + return '\'' . $this->sql_escape_string(htmlspecialchars($value)) . '\''; + } + + function sql_statement(&$fields, $fields_inc='') + { + // init result + $this->sql_fields = $this->sql_values = $this->sql_update = ''; + if ( empty($fields) && empty($fields_inc) ) + { + return; + } + + // process + if ( !empty($fields) ) + { + $first = true; + foreach ( $fields as $field => $value ) + { + // field must contain a field name + if ( !empty($field) && is_string($field) ) + { + $value = $this->sql_type_cast($value); + $this->sql_fields .= ( $first ? '' : ', ' ) . $field; + $this->sql_values .= ( $first ? '' : ', ' ) . $value; + $this->sql_update .= ( $first ? '' : ', ' ) . $field . ' = ' . $value; + $first = false; + } + } + } + if ( !empty($fields_inc) ) + { + foreach ( $fields_inc as $field => $indent ) + { + if ( $indent != 0 ) + { + $this->sql_update .= (empty($this->sql_update) ? '' : ', ') . $field . ' = ' . $field . ($indent < 0 ? ' - ' : ' + ') . abs($indent); + } + } + } + } + + function sql_stack_reset($id='') + { + if ( empty($id) ) + { + $this->sql_stack_fields = array(); + $this->sql_stack_values = array(); + } + else + { + $this->sql_stack_fields[$id] = array(); + $this->sql_stack_values[$id] = array(); + } + } + + function sql_stack_statement(&$fields, $id='') + { + $this->sql_statement($fields); + if ( empty($id) ) + { + $this->sql_stack_fields = $this->sql_fields; + $this->sql_stack_values[] = '(' . $this->sql_values . ')'; + } + else + { + $this->sql_stack_fields[$id] = $this->sql_fields; + $this->sql_stack_values[$id][] = '(' . $this->sql_values . ')'; + } + } + + function sql_stack_insert($table, $transaction=false, $line='', $file='', $break_on_error=true, $id='') + { + if ( (empty($id) && empty($this->sql_stack_values)) || (!empty($id) && empty($this->sql_stack_values[$id])) ) + { + return false; + } + switch( SQL_LAYER ) + { + case 'mysql': + case 'mysql4': + if ( empty($id) ) + { + $sql = 'INSERT INTO ' . $table . ' + (' . $this->sql_stack_fields . ') VALUES ' . implode(",\n", $this->sql_stack_values); + } + else + { + $sql = 'INSERT INTO ' . $table . ' + (' . $this->sql_stack_fields[$id] . ') VALUES ' . implode(",\n", $this->sql_stack_values[$id]); + } + $this->sql_stack_reset($id); + return $this->sql_query($sql, $transaction, $line, $file, $break_on_error); + break; + default: + $count_sql_stack_values = empty($id) ? count($this->sql_stack_values) : count($this->sql_stack_values[$id]); + $result = !empty($count_sql_stack_values); + for ( $i = 0; $i < $count_sql_stack_values; $i++ ) + { + if ( empty($id) ) + { + $sql = 'INSERT INTO ' . $table . ' + (' . $this->sql_stack_fields . ') VALUES ' . $this->sql_stack_values[$i]; + } + else + { + $sql = 'INSERT INTO ' . $table . ' + (' . $this->sql_stack_fields[$id] . ') VALUES ' . $this->sql_stack_values[$id][$i]; + } + $result &= $this->sql_query($sql, $transaction, $line, $file, $break_on_error); + } + $this->sql_stack_reset($id); + return $result; + break; + } + } + + function sql_subquery($field, $sql, $line='', $file='', $break_on_error=true, $type=TYPE_INT) + { + // sub-queries doable + $this->sql_get_version(); + if ( !in_array(SQL_LAYER, array('mysql', 'mysql4')) || (($this->sql_version[0] + ($this->sql_version[1] / 100)) >= 4.01) ) + { + return $sql; + } + + // no sub-queries + $ids = array(); + $result = $this->sql_query(trim($sql), false, $line, $file, $break_on_error); + while ( $row = $this->sql_fetchrow($result) ) + { + $ids[] = $type == TYPE_INT ? intval($row[$field]) : '\'' . $this->sql_escape_string($row[$field]) . '\''; + } + $this->sql_freeresult($result); + return empty($ids) ? 'NULL' : implode(', ', $ids); + } + + function sql_col_id($expr, $alias) + { + $this->sql_get_version(); + return in_array(SQL_LAYER, array('mysql', 'mysql4')) && (($this->sql_version[0] + ($this->sql_version[1] / 100)) <= 4.01) ? $alias : $expr; + } + + function sql_get_version() + { + if ( empty($this->sql_version) ) + { + $this->sql_version = array(0, 0, 0); + switch ( SQL_LAYER ) + { + case 'mysql': + case 'mysql4': + if ( function_exists('mysql_get_server_info') ) + { + $lo_version = explode('-', mysql_get_server_info()); + $this->sql_version = explode('.', $lo_version[0]); + $this->sql_version = array(intval($this->sql_version[0]), intval($this->sql_version[1]), intval($this->sql_version[2]), $lo_version[1]); + } + break; + + case 'postgresql': + case 'mssql': + case 'mssql-odbc': + default: + break; + } + } + return $this->sql_version; + } + + function sql_error() + { + if ( $this->_conn ) + { + return mysql_error(); + } + else + { + return array(); + } + } + function sql_escape_string($t) + { + return mysql_real_escape_string($t); + } + function sql_close() + { + $this->close(); + } + function sql_fetchrowset($query_id = 0) + { + if( !$query_id ) + { + $query_id = $this->query_result; + } + + if( $query_id ) + { + unset($this->rowset[$query_id]); + unset($this->row[$query_id]); + + while($this->rowset[$query_id] = mysql_fetch_array($query_id, MYSQL_ASSOC)) + { + $result[] = $this->rowset[$query_id]; + } + + return $result; + } + else + { + return false; + } + } +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/debugger/debugConsole.class.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/debugger/debugConsole.class.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,479 @@ + + * @see + * @version 1.2.1 + * @package debugConsole_1.2.1 + */ +class debugConsole { + /** + * events which are shown in debug console + * + * @var array + */ + protected $filters; + + /** + * all watched variables with their current content + * + * @var array + */ + protected $watches; + + /** + * debugConsole configuration values + * + * @var array + */ + protected $config; + + /** + * URL where template can be found + * + * @var string + */ + protected $template; + + /** + * javascripts to control popup + * + * @var array + */ + protected $javascripts; + + /** + * html for popup + * + * @var array + */ + protected $html; + + /** + * time of debugrun start in milliseconds + * + * @var string + */ + protected $starttime; + + /** + * time of timer start in milliseconds + * + * @var array + */ + protected $timers; + + /** + * constructor, opens popup window + */ + public function __construct () { + /* initialize class vars */ + $this->starttime = $this->getMicrotime(); + $this->watches = array (); + $this->config = $GLOBALS['_debugConsoleConfig']; + $this->html = $this->config['html']; + $this->html['header'] = str_replace("\n\r", NULL, $this->html['header']); + $this->html['header'] = str_replace("\n", NULL, $this->html['header']); + $this->javascripts = $this->config['javascripts']; + + /* replace PHP's errorhandler */ + $errorhandler = array ( + $this, + 'errorHandlerCallback' + ); + + set_error_handler($errorhandler); + + /* open popup */ + $popupOptions = "', 'debugConsole', 'width=" . $this->config['dimensions']['width'] . ",height=" . $this->config['dimensions']['height'] . ',scrollbars=yes'; + + $this->sendCommand('openPopup', $popupOptions); + $this->sendCommand('write', $this->html['header']); + + $this->startDebugRun(); + } + + /** + * destructor, shows runtime and finishes html document in popup window + */ + public function __destruct () { + $runtime = $this->getMicrotime() - $this->starttime; + $runtime = number_format((float)$runtime, 4, '.', NULL); + + $info = '

This debug-run took ' . $runtime . ' seconds to complete.

'; + + $this->sendCommand('write', $info); + $this->sendCommand('write', '
'); + $this->sendCommand('scroll', "0','100000"); + $this->sendCommand('write', $this->html['footer']); + + if ($this->config['focus']) { + $this->sendCommand('focus'); + } + } + + /** + * show new debug run header in console + */ + + protected function startDebugRun () { + $info = '

new debug-run (' . date('H:i') . ' hours)

'; + $this->sendCommand('write', '
'); + $this->sendCommand('write', $info); + } + + /** + * adds a variable to the watchlist + * + * Watched variables must be in a declare(ticks=n) + * block so that every n ticks the watched variables + * are checked for changes. If any changes were made, + * the new value of the variable is shown in the + * debugConsole with additional information where the + * changes happened. + * + * @param string $variableName + */ + public function watchVariable ($variableName) { + if (count($this->watches) === 0) { + $watchMethod = array ( + $this, + 'watchesCallback' + ); + + register_tick_function($watchMethod); + } + + if (isset($GLOBALS[$variableName])) { + $this->watches[$variableName] = $GLOBALS[$variableName]; + } else { + $this->watches[$variableName] = NULL; + } + } + + /** + * tick callback: process watches and show changes + */ + public function watchesCallback () { + if ($this->config['filters']['watches']) { + foreach ($this->watches as $variableName => $variableValue) { + if ($GLOBALS[$variableName] !== $this->watches[$variableName]) { + $info = '

$' . $variableName; + $info .= ' changed from "'; + $info .= $this->watches[$variableName]; + $info .= '" (' . gettype($this->watches[$variableName]) . ')'; + $info .= ' to "' . $GLOBALS[$variableName] . '" ('; + $info .= gettype($GLOBALS[$variableName]) . ')'; + $info .= $this->getTraceback() . '

'; + + $this->watches[$variableName] = $GLOBALS[$variableName]; + $this->sendCommand('write', $info); + } + } + } + } + + /** + * sends a javascript command to browser + * + * @param string $command + * @param string $value + */ + protected function sendCommand ($command, $value = FALSE) { + if($command == 'write') $value = '\'+unescape(\''.rawurlencode($value).'\')+\''; + $value = str_replace('\\', '\\\\', $value); + $value = nl2br($value); + + if ((bool)$value) { + /* write optionally logfile */ + $this->writeLogfileEntry($command, $value); + + $command = $this->javascripts[$command] . "('" . $value . "');"; + } else { + $command = $this->javascripts[$command] . ';'; + } + + $command = str_replace("\n\r", NULL, $command); + $command = str_replace("\n", NULL, $command); + + if (!$this->config['logfile']['disablePopup']) { + echo $this->javascripts['openTag'], "\n"; + echo $command, "\n"; + echo $this->javascripts['closeTag'], "\n"; + } + + flush(); + } + + /** + * writes html output as text entry into logfile + * + * @param string $command + * @param string $value + */ + protected function writeLogfileEntry ($command, $value) { + if ($this->config['logfile']['enable']) { + $logfile = $this->config['logfile']['path'] . $this->config['logfile']['filename']; + /* log only useful entries, no html header and footer */ + if ( + $command === 'write' + && !strpos($value, '') + && !strpos($value, '') + ) { + /* convert html to text */ + $value = html_entity_decode($value); + $value = str_replace('>', '> ', $value); + $value = strip_tags($value); + + $fp = fopen($logfile, 'a+'); + fputs($fp, $value . "\n\n"); + fclose($fp); + } elseif (strpos($value, '')) { + $fp = fopen($logfile, 'a+'); + fputs($fp, "-----------\n"); + fclose($fp); + } + } + } + + /** + * shows in console that a checkpoint has been passed, + * additional info is the file and line which triggered + * the output + * + * @param string $message + */ + public function passedCheckpoint ($message = NULL) { + if ($this->config['filters']['checkpoints']) { + $message = (bool)$message ? $message : 'Checkpoint passed!'; + + $info = '

' . $message . ''; + $info .= $this->getTraceback() . '

'; + + $this->sendCommand('write', $info); + } + } + + /** + * returns microtime as float value + * + * @return float + */ + protected function getMicrotime () { + list($usec, $sec) = explode(' ', microtime()); + return ((float)$usec + (float)$sec); + } + + /** + * returns all possible filter events for debugConsole::setFilter() method + * + * @return array + */ + public function getFilters () { + $filters = array_keys($this->config['filters']); + + ksort($filters); + reset($filters); + + return $filters; + } + + /** + * shows or hides an event-type in debugConsole, + * returns previous setting of the given event-type + * + * @param string $event + * @param bool $isShown + * @return bool + */ + public function setFilter ($event, $isShown) { + if (array_key_exists($event, $this->config['filters'])) { + $oldValue = $this->config['filters'][$event]; + $this->config['filters'][$event] = $isShown; + } else { + throw new Exception ('debugConsole: unknown event "' . $event . '" in debugConsole::filter()'); + } + + return $oldValue; + } + + /** + * show debug info for variable in debugConsole, + * added by custom text for documentation and hints + * + * @param mixed $variable + * @param string $text + */ + public function dump ($variable, $text) { + if ($this->config['filters']['debug']) { + @ob_start(); + + /* grab current ob content */ + $obContents = ob_get_contents(); + ob_clean(); + + /* grap var dump from ob */ + var_dump($variable); + $variableDebug = ob_get_contents(); + ob_end_clean(); + + /* restore previous ob content */ + if ((bool)$obContents) echo $obContents; + + /* render debug */ + $variableDebug = htmlspecialchars($variableDebug); + $infos = '

' . $text . '
'; + + if (is_array($variable)) { + $variableDebug = str_replace(' ', ' ', $variableDebug); + $infos .= '' . $variableDebug . ''; + } else { + $infos .= '' . $variableDebug . ''; + } + + $infos .= $this->getTraceback() . '

'; + $this->sendCommand('write', $infos); + } + } + + /** + * callback method for PHP errorhandling + * + * @todo implement more errorlevels + */ + public function errorHandlerCallback () { + $details = func_get_args(); + $details[1] = str_replace("'", '"', $details[1]); + $details[1] = str_replace('href="function.', 'target="_blank" href="http://www.php.net/', $details[1]); + + + /* determine error level */ + switch ($details[0]) { + case 2: + if (!$this->config['filters']['php_warnings']) return; + $errorlevel = 'warning'; + break; + case 8: + if (!$this->config['filters']['php_notices']) return; + $errorlevel = 'notice'; + break; + case 2048: + if (!$this->config['filters']['php_suggestions']) return; + $errorlevel = 'suggestion'; + break; + } + + $file = $this->cropScriptPath($details[2]); + + $infos = '

'; + $infos .= 'PHP ' . strtoupper($errorlevel) . ''; + $infos .= $details[1] . ''; + $infos .= $file . ' on line '; + $infos .= $details[3] . '

'; + + $this->sendCommand('write', $infos); + } + + /** + * start timer clock, returns timer handle + * + * @return mixed + * @param string $comment + */ + public function startTimer ($comment) { + if ($this->config['filters']['timers']) { + $timerHandle = md5(microtime()); + + $this->timers[$timerHandle] = array ( + 'starttime' => $this->getMicrotime(), + 'comment' => $comment + ); + } else { + $timerHandle = FALSE; + } + + return $timerHandle; + } + + /** + * stop timer clock + * + * @return bool + * @param string $timerHandle + */ + public function stopTimer ($timerHandle) { + if ($this->config['filters']['timers']) { + if (array_key_exists($timerHandle, $this->timers)) { + $timerExists = TRUE; + $timespan = $this->getMicrotime() - $this->timers[$timerHandle]['starttime']; + + $info = '

' . $this->timers[$timerHandle]['comment']; + $info .= '
The timer ran '; + $info .= '' . number_format ($timespan, 4, '.', NULL) . ''; + $info .= ' seconds.' . $this->getTraceback() . '

'; + + $this->sendCommand('write', $info); + } else { + $timerExists = FALSE; + } + } else { + $timerExists = FALSE; + } + + return $timerExists; + } + + /** + * returns a formatted traceback string + * + * @return string + */ + public function getTraceback () { + $callStack = debug_backtrace(); + + $debugConsoleFiles = array( + 'debugConsole.class.php', + 'debugConsole.functions.php' + ); + + $call = array ( + 'file' => 'debugConsole.class.php' + ); + + while(in_array(basename($call['file']), $debugConsoleFiles)) { + $call = array_shift($callStack); + } + + $call['file'] = $this->cropScriptPath($call['file']); + + $traceback = ''; + $traceback .= $call['file'] . ' on line '; + $traceback .= $call['line'] . ''; + + return $traceback; + } + + /** + * crops long script path, shows only the last $maxLength chars + * + * @param string $path + * @param int $maxLength + * @return string + */ + protected function cropScriptPath ($path, $maxLength = 30) { + if (strlen($path) > $maxLength) { + $startPos = strlen($path) - $maxLength - 2; + + if ($startPos > 0) { + $path = '...' . substr($path, $startPos); + } + } + + return $path; + } +} +?> \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/debugger/debugConsole.config.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/debugger/debugConsole.config.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,196 @@ + + * @see + * @version 1.2.1 + * @package debugConsole_1.2.1 + */ + +/** + * config container for debugConsole + * + * @var array + */ +$_debugConsoleConfig = array(); + +/** + * use debugConsole + */ +$_debugConsoleConfig['active'] = TRUE; + +/** + * restrict access + */ +$_debugConsoleConfig['restrictions'] = array ( + 'restrictAccess' => FALSE, + 'allowedClientAdresses' => array('127.0.0.1') +); + +/** + * set timezone, used for PHP >= 5.1.x + */ +putenv ('TZ=America/New_York'); + +/** + * focus debugConsole at end of debug-run + */ +$_debugConsoleConfig['focus'] = TRUE; + + +/** + * logfile configuration + */ +$_debugConsoleConfig['logfile'] = array ( + 'enable' => FALSE, + 'path' => './', + 'filename' => 'log.txt', + 'disablePopup' => FALSE +); + +/** + * show or hide certain events + */ +$_debugConsoleConfig['filters'] = array ( + 'debug' => TRUE, + 'watches' => TRUE, + 'checkpoints' => TRUE, + 'timers' => TRUE, + 'php_notices' => TRUE, + 'php_warnings' => TRUE, + 'php_errors' => TRUE, + 'php_suggestions' => FALSE +); + +/** + * popup dimensions in px + */ +$_debugConsoleConfig['dimensions'] = array ( + 'width' => 300, + 'height' => 525 +); + +/** + * javascript snippets, do not touch! + */ +$_debugConsoleConfig['javascripts'] = array ( + 'openTag' => '', + 'openPopup' => 'debugConsole = window.open', + 'closePopup' => 'debugConsole.close()', + 'write' => 'debugConsole.document.write', + 'scroll' => 'debugConsole.scrollBy', + 'focus' => 'debugConsole.focus()' +); + +/** + * html snippets, do not touch! + */ +$_debugConsoleConfig['html'] = array ( + 'header' => ' + + + + debugConsole + + + + + ', + 'footer' => '' +); +?> \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/debugger/debugConsole.functions.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/debugger/debugConsole.functions.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,126 @@ + + * @see + * @version 1.0.0 + * @package debugConsole_1.2.1 + */ + +/** + * show debug info of a variable in debugConsole, + * add own text for documentation or hints + * + * @param mixed $variable + * @param string $text + */ +function dc_dump($variable, $text) { + if(!defined('ENANO_DEBUG')) return false; + $debugConsole = debugConsoleLoader(); + + if (is_object($debugConsole)) { + $debugConsole->dump($variable, $text); + } +} + +/** + * watch value changes of a variable in debugConsole + * + * @param string $variableName + */ +function dc_watch($variableName) { + if(!defined('ENANO_DEBUG')) return false; + $debugConsole = debugConsoleLoader(); + + if (is_object($debugConsole)) { + $debugConsole->watchVariable($variableName); + } +} + +/** + * show checkpoint info in debugConsole to make sure + * that a certain program line has been passed + * + * @param string $message + */ +function dc_here($message = NULL) { + if(!defined('ENANO_DEBUG')) return false; + $debugConsole = debugConsoleLoader(); + + if (is_object($debugConsole)) { + (bool)$message ? $debugConsole->passedCheckpoint($message) : $debugConsole->passedCheckpoint(); + } +} + +/** + * starts a new timer clock and returns its handle + * + * @return mixed + * @param string $comment + */ +function dc_start_timer($comment) { + if(!defined('ENANO_DEBUG')) return false; + $debugConsole = debugConsoleLoader(); + + if (is_object($debugConsole)) { + return $debugConsole->startTimer($comment); + } +} + +/** + * stops and shows a certain timer clock in debugConsole + * + * @return bool + * @param string $timerHandle + */ +function dc_stop_timer($timerHandle) { + if(!defined('ENANO_DEBUG')) return false; + $debugConsole = debugConsoleLoader(); + + if (is_object($debugConsole)) { + return $debugConsole->stopTimer($timerHandle); + } +} + +/** + * singleton loader for debugConsole + * DO NOT USE, private to debugConsole functions + * + * @return mixed + */ +function debugConsoleLoader() { + static $debugConsole; + static $access = 'unset'; + + $config = $GLOBALS['_debugConsoleConfig']; + + /* obey access restrictions */ + if (gettype($access) != 'bool') { + if ($config['active']) { + if ($config['restrictions']['restrictAccess']) { + if (in_array($_SERVER['REMOTE_ADDR'], $config['restrictions']['allowedClientAdresses'])) { + $access = TRUE; + } else { + $access = FALSE; + } + } else { + $access = TRUE; + } + } else { + $access = FALSE; + } + } + + /* access granted */ + if ($access) { + if (!is_object($debugConsole)) { + $debugConsole = new debugConsole(); + } + } else { + $debugConsole = FALSE; + } + + return $debugConsole; +} +?> \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/debugger/debugConsole.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/debugger/debugConsole.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,16 @@ + + * @see + * @version 1.0.0 + * @package debugConsole_1.2.1 + */ + +$path = dirname(__FILE__) . '/'; + +require_once $path . 'debugConsole.config.php'; +require_once $path . 'debugConsole.class.php'; +require_once $path . 'debugConsole.functions.php'; +?> \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/diff.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/diff.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1153 @@ + +// You may copy this code freely under the conditions of the GPL. +// + +define('USE_ASSERTS', function_exists('assert')); + +/** + * @todo document + * @package Enano + * @subpackage DifferenceEngine + */ +class _DiffOp { + var $type; + var $orig; + var $closing; + + function reverse() { + trigger_error('pure virtual', E_USER_ERROR); + } + + function norig() { + return $this->orig ? sizeof($this->orig) : 0; + } + + function nclosing() { + return $this->closing ? sizeof($this->closing) : 0; + } +} + +/** + * @todo document + * @package Enano + * @subpackage DifferenceEngine + */ +class _DiffOp_Copy extends _DiffOp { + var $type = 'copy'; + + function _DiffOp_Copy ($orig, $closing = false) { + if (!is_array($closing)) + $closing = $orig; + $this->orig = $orig; + $this->closing = $closing; + } + + function reverse() { + return new _DiffOp_Copy($this->closing, $this->orig); + } +} + +/** + * @todo document + * @package Enano + * @subpackage DifferenceEngine + */ +class _DiffOp_Delete extends _DiffOp { + var $type = 'delete'; + + function _DiffOp_Delete ($lines) { + $this->orig = $lines; + $this->closing = false; + } + + function reverse() { + return new _DiffOp_Add($this->orig); + } +} + +/** + * @todo document + * @package Enano + * @subpackage DifferenceEngine + */ +class _DiffOp_Add extends _DiffOp { + var $type = 'add'; + + function _DiffOp_Add ($lines) { + $this->closing = $lines; + $this->orig = false; + } + + function reverse() { + return new _DiffOp_Delete($this->closing); + } +} + +/** + * @todo document + * @package Enano + * @subpackage DifferenceEngine + */ +class _DiffOp_Change extends _DiffOp { + var $type = 'change'; + + function _DiffOp_Change ($orig, $closing) { + $this->orig = $orig; + $this->closing = $closing; + } + + function reverse() { + return new _DiffOp_Change($this->closing, $this->orig); + } +} + + +/** + * Class used internally by Diff to actually compute the diffs. + * + * The algorithm used here is mostly lifted from the perl module + * Algorithm::Diff (version 1.06) by Ned Konz, which is available at: + * http://www.perl.com/CPAN/authors/id/N/NE/NEDKONZ/Algorithm-Diff-1.06.zip + * + * More ideas are taken from: + * http://www.ics.uci.edu/~eppstein/161/960229.html + * + * Some ideas are (and a bit of code) are from from analyze.c, from GNU + * diffutils-2.7, which can be found at: + * ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz + * + * closingly, some ideas (subdivision by NCHUNKS > 2, and some optimizations) + * are my own. + * + * Line length limits for robustness added by Tim Starling, 2005-08-31 + * + * @author Geoffrey T. Dairiki, Tim Starling + * @package Enano + * @subpackage DifferenceEngine + */ +define('MAX_XREF_LENGTH', 10000); +class _DiffEngine +{ + function diff ($from_lines, $to_lines) { + $fname = '_DiffEngine::diff'; + // wfProfileIn( $fname ); + + $n_from = sizeof($from_lines); + $n_to = sizeof($to_lines); + + $this->xchanged = $this->ychanged = array(); + $this->xv = $this->yv = array(); + $this->xind = $this->yind = array(); + unset($this->seq); + unset($this->in_seq); + unset($this->lcs); + + // Skip leading common lines. + for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) { + if ($from_lines[$skip] !== $to_lines[$skip]) + break; + $this->xchanged[$skip] = $this->ychanged[$skip] = false; + } + // Skip trailing common lines. + $xi = $n_from; $yi = $n_to; + for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) { + if ($from_lines[$xi] !== $to_lines[$yi]) + break; + $this->xchanged[$xi] = $this->ychanged[$yi] = false; + } + + // Ignore lines which do not exist in both files. + for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { + $xhash[$this->_line_hash($from_lines[$xi])] = 1; + } + + for ($yi = $skip; $yi < $n_to - $endskip; $yi++) { + $line = $to_lines[$yi]; + if ( ($this->ychanged[$yi] = empty($xhash[$this->_line_hash($line)])) ) + continue; + $yhash[$this->_line_hash($line)] = 1; + $this->yv[] = $line; + $this->yind[] = $yi; + } + for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { + $line = $from_lines[$xi]; + if ( ($this->xchanged[$xi] = empty($yhash[$this->_line_hash($line)])) ) + continue; + $this->xv[] = $line; + $this->xind[] = $xi; + } + + // Find the LCS. + $this->_compareseq(0, sizeof($this->xv), 0, sizeof($this->yv)); + + // Merge edits when possible + $this->_shift_boundaries($from_lines, $this->xchanged, $this->ychanged); + $this->_shift_boundaries($to_lines, $this->ychanged, $this->xchanged); + + // Compute the edit operations. + $edits = array(); + $xi = $yi = 0; + while ($xi < $n_from || $yi < $n_to) { + USE_ASSERTS && assert($yi < $n_to || $this->xchanged[$xi]); + USE_ASSERTS && assert($xi < $n_from || $this->ychanged[$yi]); + + // Skip matching "snake". + $copy = array(); + while ( $xi < $n_from && $yi < $n_to + && !$this->xchanged[$xi] && !$this->ychanged[$yi]) { + $copy[] = $from_lines[$xi++]; + ++$yi; + } + if ($copy) + $edits[] = new _DiffOp_Copy($copy); + + // Find deletes & adds. + $delete = array(); + while ($xi < $n_from && $this->xchanged[$xi]) + $delete[] = $from_lines[$xi++]; + + $add = array(); + while ($yi < $n_to && $this->ychanged[$yi]) + $add[] = $to_lines[$yi++]; + + if ($delete && $add) + $edits[] = new _DiffOp_Change($delete, $add); + elseif ($delete) + $edits[] = new _DiffOp_Delete($delete); + elseif ($add) + $edits[] = new _DiffOp_Add($add); + } + // wfProfileOut( $fname ); + return $edits; + } + + /** + * Returns the whole line if it's small enough, or the MD5 hash otherwise + */ + function _line_hash( $line ) { + if ( strlen( $line ) > MAX_XREF_LENGTH ) { + return md5( $line ); + } else { + return $line; + } + } + + + /* Divide the Largest Common Subsequence (LCS) of the sequences + * [XOFF, XLIM) and [YOFF, YLIM) into NCHUNKS approximately equally + * sized segments. + * + * Returns (LCS, PTS). LCS is the length of the LCS. PTS is an + * array of NCHUNKS+1 (X, Y) indexes giving the diving points between + * sub sequences. The first sub-sequence is contained in [X0, X1), + * [Y0, Y1), the second in [X1, X2), [Y1, Y2) and so on. Note + * that (X0, Y0) == (XOFF, YOFF) and + * (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM). + * + * This function assumes that the first lines of the specified portions + * of the two files do not match, and likewise that the last lines do not + * match. The caller must trim matching lines from the beginning and end + * of the portions it is going to specify. + */ + function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) { + $fname = '_DiffEngine::_diag'; + // wfProfileIn( $fname ); + $flip = false; + + if ($xlim - $xoff > $ylim - $yoff) { + // Things seems faster (I'm not sure I understand why) + // when the shortest sequence in X. + $flip = true; + list ($xoff, $xlim, $yoff, $ylim) + = array( $yoff, $ylim, $xoff, $xlim); + } + + if ($flip) + for ($i = $ylim - 1; $i >= $yoff; $i--) + $ymatches[$this->xv[$i]][] = $i; + else + for ($i = $ylim - 1; $i >= $yoff; $i--) + $ymatches[$this->yv[$i]][] = $i; + + $this->lcs = 0; + $this->seq[0]= $yoff - 1; + $this->in_seq = array(); + $ymids[0] = array(); + + $numer = $xlim - $xoff + $nchunks - 1; + $x = $xoff; + for ($chunk = 0; $chunk < $nchunks; $chunk++) { + // wfProfileIn( "$fname-chunk" ); + if ($chunk > 0) + for ($i = 0; $i <= $this->lcs; $i++) + $ymids[$i][$chunk-1] = $this->seq[$i]; + + $x1 = $xoff + (int)(($numer + ($xlim-$xoff)*$chunk) / $nchunks); + for ( ; $x < $x1; $x++) { + $line = $flip ? $this->yv[$x] : $this->xv[$x]; + if (empty($ymatches[$line])) + continue; + $matches = $ymatches[$line]; + reset($matches); + while (list ($junk, $y) = each($matches)) + if (empty($this->in_seq[$y])) { + $k = $this->_lcs_pos($y); + USE_ASSERTS && assert($k > 0); + $ymids[$k] = $ymids[$k-1]; + break; + } + while (list ($junk, $y) = each($matches)) { + if ($y > $this->seq[$k-1]) { + USE_ASSERTS && assert($y < $this->seq[$k]); + // Optimization: this is a common case: + // next match is just replacing previous match. + $this->in_seq[$this->seq[$k]] = false; + $this->seq[$k] = $y; + $this->in_seq[$y] = 1; + } else if (empty($this->in_seq[$y])) { + $k = $this->_lcs_pos($y); + USE_ASSERTS && assert($k > 0); + $ymids[$k] = $ymids[$k-1]; + } + } + } + // wfProfileOut( "$fname-chunk" ); + } + + $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff); + $ymid = $ymids[$this->lcs]; + for ($n = 0; $n < $nchunks - 1; $n++) { + $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks); + $y1 = $ymid[$n] + 1; + $seps[] = $flip ? array($y1, $x1) : array($x1, $y1); + } + $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim); + + // wfProfileOut( $fname ); + return array($this->lcs, $seps); + } + + function _lcs_pos ($ypos) { + $fname = '_DiffEngine::_lcs_pos'; + // wfProfileIn( $fname ); + + $end = $this->lcs; + if ($end == 0 || $ypos > $this->seq[$end]) { + $this->seq[++$this->lcs] = $ypos; + $this->in_seq[$ypos] = 1; + // wfProfileOut( $fname ); + return $this->lcs; + } + + $beg = 1; + while ($beg < $end) { + $mid = (int)(($beg + $end) / 2); + if ( $ypos > $this->seq[$mid] ) + $beg = $mid + 1; + else + $end = $mid; + } + + USE_ASSERTS && assert($ypos != $this->seq[$end]); + + $this->in_seq[$this->seq[$end]] = false; + $this->seq[$end] = $ypos; + $this->in_seq[$ypos] = 1; + // wfProfileOut( $fname ); + return $end; + } + + /* Find LCS of two sequences. + * + * The results are recorded in the vectors $this->{x,y}changed[], by + * storing a 1 in the element for each line that is an insertion + * or deletion (ie. is not in the LCS). + * + * The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1. + * + * Note that XLIM, YLIM are exclusive bounds. + * All line numbers are origin-0 and discarded lines are not counted. + */ + function _compareseq ($xoff, $xlim, $yoff, $ylim) { + $fname = '_DiffEngine::_compareseq'; + // wfProfileIn( $fname ); + + // Slide down the bottom initial diagonal. + while ($xoff < $xlim && $yoff < $ylim + && $this->xv[$xoff] == $this->yv[$yoff]) { + ++$xoff; + ++$yoff; + } + + // Slide up the top initial diagonal. + while ($xlim > $xoff && $ylim > $yoff + && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) { + --$xlim; + --$ylim; + } + + if ($xoff == $xlim || $yoff == $ylim) + $lcs = 0; + else { + // This is ad hoc but seems to work well. + //$nchunks = sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); + //$nchunks = max(2,min(8,(int)$nchunks)); + $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1; + list ($lcs, $seps) + = $this->_diag($xoff,$xlim,$yoff, $ylim,$nchunks); + } + + if ($lcs == 0) { + // X and Y sequences have no common subsequence: + // mark all changed. + while ($yoff < $ylim) + $this->ychanged[$this->yind[$yoff++]] = 1; + while ($xoff < $xlim) + $this->xchanged[$this->xind[$xoff++]] = 1; + } else { + // Use the partitions to split this problem into subproblems. + reset($seps); + $pt1 = $seps[0]; + while ($pt2 = next($seps)) { + $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]); + $pt1 = $pt2; + } + } + // wfProfileOut( $fname ); + } + + /* Adjust inserts/deletes of identical lines to join changes + * as much as possible. + * + * We do something when a run of changed lines include a + * line at one end and has an excluded, identical line at the other. + * We are free to choose which identical line is included. + * `compareseq' usually chooses the one at the beginning, + * but usually it is cleaner to consider the following identical line + * to be the "change". + * + * This is extracted verbatim from analyze.c (GNU diffutils-2.7). + */ + function _shift_boundaries ($lines, &$changed, $other_changed) { + $fname = '_DiffEngine::_shift_boundaries'; + // wfProfileIn( $fname ); + $i = 0; + $j = 0; + + USE_ASSERTS && assert('sizeof($lines) == sizeof($changed)'); + $len = sizeof($lines); + $other_len = sizeof($other_changed); + + while (1) { + /* + * Scan forwards to find beginning of another run of changes. + * Also keep track of the corresponding point in the other file. + * + * Throughout this code, $i and $j are adjusted together so that + * the first $i elements of $changed and the first $j elements + * of $other_changed both contain the same number of zeros + * (unchanged lines). + * Furthermore, $j is always kept so that $j == $other_len or + * $other_changed[$j] == false. + */ + while ($j < $other_len && $other_changed[$j]) + $j++; + + while ($i < $len && ! $changed[$i]) { + USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]'); + $i++; $j++; + while ($j < $other_len && $other_changed[$j]) + $j++; + } + + if ($i == $len) + break; + + $start = $i; + + // Find the end of this run of changes. + while (++$i < $len && $changed[$i]) + continue; + + do { + /* + * Record the length of this run of changes, so that + * we can later determine whether the run has grown. + */ + $runlength = $i - $start; + + /* + * Move the changed region back, so long as the + * previous unchanged line matches the last changed one. + * This merges with previous changed regions. + */ + while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) { + $changed[--$start] = 1; + $changed[--$i] = false; + while ($start > 0 && $changed[$start - 1]) + $start--; + USE_ASSERTS && assert('$j > 0'); + while ($other_changed[--$j]) + continue; + USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]'); + } + + /* + * Set CORRESPONDING to the end of the changed run, at the last + * point where it corresponds to a changed run in the other file. + * CORRESPONDING == LEN means no such point has been found. + */ + $corresponding = $j < $other_len ? $i : $len; + + /* + * Move the changed region forward, so long as the + * first changed line matches the following unchanged one. + * This merges with following changed regions. + * Do this second, so that if there are no merges, + * the changed region is moved forward as far as possible. + */ + while ($i < $len && $lines[$start] == $lines[$i]) { + $changed[$start++] = false; + $changed[$i++] = 1; + while ($i < $len && $changed[$i]) + $i++; + + USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]'); + $j++; + if ($j < $other_len && $other_changed[$j]) { + $corresponding = $i; + while ($j < $other_len && $other_changed[$j]) + $j++; + } + } + } while ($runlength != $i - $start); + + /* + * If possible, move the fully-merged run of changes + * back to a corresponding run in the other file. + */ + while ($corresponding < $i) { + $changed[--$start] = 1; + $changed[--$i] = 0; + USE_ASSERTS && assert('$j > 0'); + while ($other_changed[--$j]) + continue; + USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]'); + } + } + // wfProfileOut( $fname ); + } +} + +/** + * Class representing a 'diff' between two sequences of strings. + * @todo document + * @package Enano + * @subpackage DifferenceEngine + */ +class Diff +{ + var $edits; + + /** + * Constructor. + * Computes diff between sequences of strings. + * + * @param $from_lines array An array of strings. + * (Typically these are lines from a file.) + * @param $to_lines array An array of strings. + */ + function Diff($from_lines, $to_lines) { + $eng = new _DiffEngine; + $this->edits = $eng->diff($from_lines, $to_lines); + //$this->_check($from_lines, $to_lines); + } + + /** + * Compute reversed Diff. + * + * SYNOPSIS: + * + * $diff = new Diff($lines1, $lines2); + * $rev = $diff->reverse(); + * @return object A Diff object representing the inverse of the + * original diff. + */ + function reverse () { + $rev = $this; + $rev->edits = array(); + foreach ($this->edits as $edit) { + $rev->edits[] = $edit->reverse(); + } + return $rev; + } + + /** + * Check for empty diff. + * + * @return bool True iff two sequences were identical. + */ + function isEmpty () { + foreach ($this->edits as $edit) { + if ($edit->type != 'copy') + return false; + } + return true; + } + + /** + * Compute the length of the Longest Common Subsequence (LCS). + * + * This is mostly for diagnostic purposes. + * + * @return int The length of the LCS. + */ + function lcs () { + $lcs = 0; + foreach ($this->edits as $edit) { + if ($edit->type == 'copy') + $lcs += sizeof($edit->orig); + } + return $lcs; + } + + /** + * Get the original set of lines. + * + * This reconstructs the $from_lines parameter passed to the + * constructor. + * + * @return array The original sequence of strings. + */ + function orig() { + $lines = array(); + + foreach ($this->edits as $edit) { + if ($edit->orig) + array_splice($lines, sizeof($lines), 0, $edit->orig); + } + return $lines; + } + + /** + * Get the closing set of lines. + * + * This reconstructs the $to_lines parameter passed to the + * constructor. + * + * @return array The sequence of strings. + */ + function closing() { + $lines = array(); + + foreach ($this->edits as $edit) { + if ($edit->closing) + array_splice($lines, sizeof($lines), 0, $edit->closing); + } + return $lines; + } + + /** + * Check a Diff for validity. + * + * This is here only for debugging purposes. + */ + function _check ($from_lines, $to_lines) { + $fname = 'Diff::_check'; + // wfProfileIn( $fname ); + if (serialize($from_lines) != serialize($this->orig())) + trigger_error("Reconstructed original doesn't match", E_USER_ERROR); + if (serialize($to_lines) != serialize($this->closing())) + trigger_error("Reconstructed closing doesn't match", E_USER_ERROR); + + $rev = $this->reverse(); + if (serialize($to_lines) != serialize($rev->orig())) + trigger_error("Reversed original doesn't match", E_USER_ERROR); + if (serialize($from_lines) != serialize($rev->closing())) + trigger_error("Reversed closing doesn't match", E_USER_ERROR); + + + $prevtype = 'none'; + foreach ($this->edits as $edit) { + if ( $prevtype == $edit->type ) + trigger_error("Edit sequence is non-optimal", E_USER_ERROR); + $prevtype = $edit->type; + } + + $lcs = $this->lcs(); + trigger_error('Diff okay: LCS = '.$lcs, E_USER_NOTICE); + // wfProfileOut( $fname ); + } +} + +/** + * FIXME: bad name. + * @todo document + * @package Enano + * @subpackage DifferenceEngine + */ +class MappedDiff extends Diff +{ + /** + * Constructor. + * + * Computes diff between sequences of strings. + * + * This can be used to compute things like + * case-insensitve diffs, or diffs which ignore + * changes in white-space. + * + * @param $from_lines array An array of strings. + * (Typically these are lines from a file.) + * + * @param $to_lines array An array of strings. + * + * @param $mapped_from_lines array This array should + * have the same size number of elements as $from_lines. + * The elements in $mapped_from_lines and + * $mapped_to_lines are what is actually compared + * when computing the diff. + * + * @param $mapped_to_lines array This array should + * have the same number of elements as $to_lines. + */ + function MappedDiff($from_lines, $to_lines, + $mapped_from_lines, $mapped_to_lines) { + $fname = 'MappedDiff::MappedDiff'; + // wfProfileIn( $fname ); + + assert(sizeof($from_lines) == sizeof($mapped_from_lines)); + assert(sizeof($to_lines) == sizeof($mapped_to_lines)); + + $this->Diff($mapped_from_lines, $mapped_to_lines); + + $xi = $yi = 0; + for ($i = 0; $i < sizeof($this->edits); $i++) { + $orig = &$this->edits[$i]->orig; + if (is_array($orig)) { + $orig = array_slice($from_lines, $xi, sizeof($orig)); + $xi += sizeof($orig); + } + + $closing = &$this->edits[$i]->closing; + if (is_array($closing)) { + $closing = array_slice($to_lines, $yi, sizeof($closing)); + $yi += sizeof($closing); + } + } + // wfProfileOut( $fname ); + } +} + +/** + * A class to format Diffs + * + * This class formats the diff in classic diff format. + * It is intended that this class be customized via inheritance, + * to obtain fancier outputs. + * @todo document + * @package Enano + * @subpackage DifferenceEngine + */ +class DiffFormatter +{ + /** + * Number of leading context "lines" to preserve. + * + * This should be left at zero for this class, but subclasses + * may want to set this to other values. + */ + var $leading_context_lines = 0; + + /** + * Number of trailing context "lines" to preserve. + * + * This should be left at zero for this class, but subclasses + * may want to set this to other values. + */ + var $trailing_context_lines = 0; + + /** + * Format a diff. + * + * @param $diff object A Diff object. + * @return string The formatted output. + */ + function format($diff) { + $fname = 'DiffFormatter::format'; + // wfProfileIn( $fname ); + + $xi = $yi = 1; + $block = false; + $context = array(); + + $nlead = $this->leading_context_lines; + $ntrail = $this->trailing_context_lines; + + $this->_start_diff(); + + foreach ($diff->edits as $edit) { + if ($edit->type == 'copy') { + if (is_array($block)) { + if (sizeof($edit->orig) <= $nlead + $ntrail) { + $block[] = $edit; + } + else{ + if ($ntrail) { + $context = array_slice($edit->orig, 0, $ntrail); + $block[] = new _DiffOp_Copy($context); + } + $this->_block($x0, $ntrail + $xi - $x0, + $y0, $ntrail + $yi - $y0, + $block); + $block = false; + } + } + $context = $edit->orig; + } + else { + if (! is_array($block)) { + $context = array_slice($context, sizeof($context) - $nlead); + $x0 = $xi - sizeof($context); + $y0 = $yi - sizeof($context); + $block = array(); + if ($context) + $block[] = new _DiffOp_Copy($context); + } + $block[] = $edit; + } + + if ($edit->orig) + $xi += sizeof($edit->orig); + if ($edit->closing) + $yi += sizeof($edit->closing); + } + + if (is_array($block)) + $this->_block($x0, $xi - $x0, + $y0, $yi - $y0, + $block); + + $end = $this->_end_diff(); + // wfProfileOut( $fname ); + return $end; + } + + function _block($xbeg, $xlen, $ybeg, $ylen, &$edits) { + $fname = 'DiffFormatter::_block'; + // wfProfileIn( $fname ); + $this->_start_block($this->_block_header($xbeg, $xlen, $ybeg, $ylen)); + foreach ($edits as $edit) { + if ($edit->type == 'copy') + $this->_context($edit->orig); + elseif ($edit->type == 'add') + $this->_added($edit->closing); + elseif ($edit->type == 'delete') + $this->_deleted($edit->orig); + elseif ($edit->type == 'change') + $this->_changed($edit->orig, $edit->closing); + else + trigger_error('Unknown edit type', E_USER_ERROR); + } + $this->_end_block(); + // wfProfileOut( $fname ); + } + + function _start_diff() { + ob_start(); + } + + function _end_diff() { + $val = ob_get_contents(); + ob_end_clean(); + return $val; + } + + function _block_header($xbeg, $xlen, $ybeg, $ylen) { + if ($xlen > 1) + $xbeg .= "," . ($xbeg + $xlen - 1); + if ($ylen > 1) + $ybeg .= "," . ($ybeg + $ylen - 1); + + return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg; + } + + function _start_block($header) { + echo $header; + } + + function _end_block() { + } + + function _lines($lines, $prefix = ' ') { + foreach ($lines as $line) + echo "$prefix $line\n"; + } + + function _context($lines) { + $this->_lines($lines); + } + + function _added($lines) { + $this->_lines($lines, '>'); + } + function _deleted($lines) { + $this->_lines($lines, '<'); + } + + function _changed($orig, $closing) { + $this->_deleted($orig); + echo "---\n"; + $this->_added($closing); + } +} + + +/** + * Additions by Axel Boldt follow, partly taken from diff.php, phpwiki-1.3.3 + * + */ + +define('NBSP', ' '); // iso-8859-x non-breaking space. + +/** + * @todo document + * @package Enano + * @subpackage DifferenceEngine + */ +class _HWLDF_WordAccumulator { + function _HWLDF_WordAccumulator () { + $this->_lines = array(); + $this->_line = ''; + $this->_group = ''; + $this->_tag = ''; + } + + function _flushGroup ($new_tag) { + if ($this->_group !== '') { + if ($this->_tag == 'mark') + $this->_line .= '' . + htmlspecialchars ( $this->_group ) . ''; + else + $this->_line .= htmlspecialchars ( $this->_group ); + } + $this->_group = ''; + $this->_tag = $new_tag; + } + + function _flushLine ($new_tag) { + $this->_flushGroup($new_tag); + if ($this->_line != '') + array_push ( $this->_lines, $this->_line ); + else + # make empty lines visible by inserting an NBSP + array_push ( $this->_lines, NBSP ); + $this->_line = ''; + } + + function addWords ($words, $tag = '') { + if ($tag != $this->_tag) + $this->_flushGroup($tag); + + foreach ($words as $word) { + // new-line should only come as first char of word. + if ($word == '') + continue; + if ($word[0] == "\n") { + $this->_flushLine($tag); + $word = substr($word, 1); + } + assert(!strstr($word, "\n")); + $this->_group .= $word; + } + } + + function getLines() { + $this->_flushLine('~done'); + return $this->_lines; + } +} + +/** + * @todo document + * @package Enano + * @subpackage DifferenceEngine + */ +define('MAX_LINE_LENGTH', 10000); +class WordLevelDiff extends MappedDiff +{ + function WordLevelDiff ($orig_lines, $closing_lines) { + $fname = 'WordLevelDiff::WordLevelDiff'; + // wfProfileIn( $fname ); + + list ($orig_words, $orig_stripped) = $this->_split($orig_lines); + list ($closing_words, $closing_stripped) = $this->_split($closing_lines); + + $this->MappedDiff($orig_words, $closing_words, + $orig_stripped, $closing_stripped); + // wfProfileOut( $fname ); + } + + function _split($lines) { + $fname = 'WordLevelDiff::_split'; + // wfProfileIn( $fname ); + + $words = array(); + $stripped = array(); + $first = true; + foreach ( $lines as $line ) { + # If the line is too long, just pretend the entire line is one big word + # This prevents resource exhaustion problems + if ( $first ) { + $first = false; + } else { + $words[] = "\n"; + $stripped[] = "\n"; + } + if ( strlen( $line ) > MAX_LINE_LENGTH ) { + $words[] = $line; + $stripped[] = $line; + } else { + if (preg_match_all('/ ( [^\S\n]+ | [0-9_A-Za-z\x80-\xff]+ | . ) (?: (?!< \n) [^\S\n])? /xs', + $line, $m)) + { + $words = array_merge( $words, $m[0] ); + $stripped = array_merge( $stripped, $m[1] ); + } + } + } + // wfProfileOut( $fname ); + return array($words, $stripped); + } + + function orig () { + $fname = 'WordLevelDiff::orig'; + // wfProfileIn( $fname ); + $orig = new _HWLDF_WordAccumulator; + + foreach ($this->edits as $edit) { + if ($edit->type == 'copy') + $orig->addWords($edit->orig); + elseif ($edit->orig) + $orig->addWords($edit->orig, 'mark'); + } + $lines = $orig->getLines(); + // wfProfileOut( $fname ); + return $lines; + } + + function closing () { + $fname = 'WordLevelDiff::closing'; + // wfProfileIn( $fname ); + $closing = new _HWLDF_WordAccumulator; + + foreach ($this->edits as $edit) { + if ($edit->type == 'copy') + $closing->addWords($edit->closing); + elseif ($edit->closing) + $closing->addWords($edit->closing, 'mark'); + } + $lines = $closing->getLines(); + // wfProfileOut( $fname ); + return $lines; + } +} + +/** + * Wikipedia Table style diff formatter. + * @todo document + * @package Enano + * @subpackage DifferenceEngine + */ +class TableDiffFormatter extends DiffFormatter +{ + function TableDiffFormatter() { + $this->leading_context_lines = 2; + $this->trailing_context_lines = 2; + } + + function _block_header( $xbeg, $xlen, $ybeg, $ylen ) { + $r = '\n" . + '\n"; + return $r; + } + + function _start_block( $header ) { + echo $header; + } + + function _end_block() { + } + + function _lines( $lines, $prefix=' ', $color='white' ) { + } + + # HTML-escape parameter before calling this + function addedLine( $line ) { + return "+{$line}"; + } + + # HTML-escape parameter before calling this + function deletedLine( $line ) { + return "-{$line}"; + } + + # HTML-escape parameter before calling this + function contextLine( $line ) { + return " {$line}"; + } + + function emptyLine() { + return ' '; + } + + function _added( $lines ) { + foreach ($lines as $line) { + echo '' . $this->emptyLine() . + $this->addedLine( htmlspecialchars ( $line ) ) . "\n"; + } + } + + function _deleted($lines) { + foreach ($lines as $line) { + echo '' . $this->deletedLine( htmlspecialchars ( $line ) ) . + $this->emptyLine() . "\n"; + } + } + + function _context( $lines ) { + foreach ($lines as $line) { + echo '' . + $this->contextLine( htmlspecialchars ( $line ) ) . + $this->contextLine( htmlspecialchars ( $line ) ) . "\n"; + } + } + + function _changed( $orig, $closing ) { + $fname = 'TableDiffFormatter::_changed'; + // wfProfileIn( $fname ); + + $diff = new WordLevelDiff( $orig, $closing ); + $del = $diff->orig(); + $add = $diff->closing(); + + # Notice that WordLevelDiff returns HTML-escaped output. + # Hence, we will be calling addedLine/deletedLine without HTML-escaping. + + while ( $line = array_shift( $del ) ) { + $aline = array_shift( $add ); + echo '' . $this->deletedLine( $line ) . + $this->addedLine( $aline ) . "\n"; + } + foreach ($add as $line) { # If any leftovers + echo '' . $this->emptyLine() . + $this->addedLine( $line ) . "\n"; + } + // wfProfileOut( $fname ); + } +} + diff -r 902822492a68 -r fe660c52c48f includes/diffengine/Engine/native.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/diffengine/Engine/native.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,435 @@ + 2, and some optimizations) are from + * Geoffrey T. Dairiki . The original PHP version of this + * code was written by him, and is used/adapted with his permission. + * + * $Horde: framework/Text_Diff/Diff/Engine/native.php,v 1.3 2006/01/06 15:56:52 jan Exp $ + * + * @author Geoffrey T. Dairiki + * @package Text_Diff + * + * @access private + */ +class Text_Diff_Engine_native { + + function diff($from_lines, $to_lines) + { + array_walk($from_lines, array('Text_Diff', 'trimNewlines')); + array_walk($to_lines, array('Text_Diff', 'trimNewlines')); + + $n_from = count($from_lines); + $n_to = count($to_lines); + + $this->xchanged = $this->ychanged = array(); + $this->xv = $this->yv = array(); + $this->xind = $this->yind = array(); + unset($this->seq); + unset($this->in_seq); + unset($this->lcs); + + // Skip leading common lines. + for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) { + if ($from_lines[$skip] !== $to_lines[$skip]) { + break; + } + $this->xchanged[$skip] = $this->ychanged[$skip] = false; + } + + // Skip trailing common lines. + $xi = $n_from; $yi = $n_to; + for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) { + if ($from_lines[$xi] !== $to_lines[$yi]) { + break; + } + $this->xchanged[$xi] = $this->ychanged[$yi] = false; + } + + // Ignore lines which do not exist in both files. + for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { + $xhash[$from_lines[$xi]] = 1; + } + for ($yi = $skip; $yi < $n_to - $endskip; $yi++) { + $line = $to_lines[$yi]; + if (($this->ychanged[$yi] = empty($xhash[$line]))) { + continue; + } + $yhash[$line] = 1; + $this->yv[] = $line; + $this->yind[] = $yi; + } + for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { + $line = $from_lines[$xi]; + if (($this->xchanged[$xi] = empty($yhash[$line]))) { + continue; + } + $this->xv[] = $line; + $this->xind[] = $xi; + } + + // Find the LCS. + $this->_compareseq(0, count($this->xv), 0, count($this->yv)); + + // Merge edits when possible. + $this->_shiftBoundaries($from_lines, $this->xchanged, $this->ychanged); + $this->_shiftBoundaries($to_lines, $this->ychanged, $this->xchanged); + + // Compute the edit operations. + $edits = array(); + $xi = $yi = 0; + while ($xi < $n_from || $yi < $n_to) { + assert($yi < $n_to || $this->xchanged[$xi]); + assert($xi < $n_from || $this->ychanged[$yi]); + + // Skip matching "snake". + $copy = array(); + while ($xi < $n_from && $yi < $n_to + && !$this->xchanged[$xi] && !$this->ychanged[$yi]) { + $copy[] = $from_lines[$xi++]; + ++$yi; + } + if ($copy) { + $edits[] = &new Text_Diff_Op_copy($copy); + } + + // Find deletes & adds. + $delete = array(); + while ($xi < $n_from && $this->xchanged[$xi]) { + $delete[] = $from_lines[$xi++]; + } + + $add = array(); + while ($yi < $n_to && $this->ychanged[$yi]) { + $add[] = $to_lines[$yi++]; + } + + if ($delete && $add) { + $edits[] = &new Text_Diff_Op_change($delete, $add); + } elseif ($delete) { + $edits[] = &new Text_Diff_Op_delete($delete); + } elseif ($add) { + $edits[] = &new Text_Diff_Op_add($add); + } + } + + return $edits; + } + + /** + * Divides the Largest Common Subsequence (LCS) of the sequences (XOFF, + * XLIM) and (YOFF, YLIM) into NCHUNKS approximately equally sized + * segments. + * + * Returns (LCS, PTS). LCS is the length of the LCS. PTS is an array of + * NCHUNKS+1 (X, Y) indexes giving the diving points between sub + * sequences. The first sub-sequence is contained in (X0, X1), (Y0, Y1), + * the second in (X1, X2), (Y1, Y2) and so on. Note that (X0, Y0) == + * (XOFF, YOFF) and (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM). + * + * This function assumes that the first lines of the specified portions of + * the two files do not match, and likewise that the last lines do not + * match. The caller must trim matching lines from the beginning and end + * of the portions it is going to specify. + */ + function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) + { + $flip = false; + + if ($xlim - $xoff > $ylim - $yoff) { + /* Things seems faster (I'm not sure I understand why) when the + * shortest sequence is in X. */ + $flip = true; + list ($xoff, $xlim, $yoff, $ylim) + = array($yoff, $ylim, $xoff, $xlim); + } + + if ($flip) { + for ($i = $ylim - 1; $i >= $yoff; $i--) { + $ymatches[$this->xv[$i]][] = $i; + } + } else { + for ($i = $ylim - 1; $i >= $yoff; $i--) { + $ymatches[$this->yv[$i]][] = $i; + } + } + + $this->lcs = 0; + $this->seq[0]= $yoff - 1; + $this->in_seq = array(); + $ymids[0] = array(); + + $numer = $xlim - $xoff + $nchunks - 1; + $x = $xoff; + for ($chunk = 0; $chunk < $nchunks; $chunk++) { + if ($chunk > 0) { + for ($i = 0; $i <= $this->lcs; $i++) { + $ymids[$i][$chunk - 1] = $this->seq[$i]; + } + } + + $x1 = $xoff + (int)(($numer + ($xlim-$xoff)*$chunk) / $nchunks); + for (; $x < $x1; $x++) { + $line = $flip ? $this->yv[$x] : $this->xv[$x]; + if (empty($ymatches[$line])) { + continue; + } + $matches = $ymatches[$line]; + foreach ($matches as $y) { + if (empty($this->in_seq[$y])) { + $k = $this->_lcsPos($y); + assert($k > 0); + $ymids[$k] = $ymids[$k - 1]; + break; + } + } + + while (list($junk, $y) = each($matches)) { + if ($y > $this->seq[$k - 1]) { + assert($y < $this->seq[$k]); + /* Optimization: this is a common case: next match is + * just replacing previous match. */ + $this->in_seq[$this->seq[$k]] = false; + $this->seq[$k] = $y; + $this->in_seq[$y] = 1; + } elseif (empty($this->in_seq[$y])) { + $k = $this->_lcsPos($y); + assert($k > 0); + $ymids[$k] = $ymids[$k - 1]; + } + } + } + } + + $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff); + $ymid = $ymids[$this->lcs]; + for ($n = 0; $n < $nchunks - 1; $n++) { + $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks); + $y1 = $ymid[$n] + 1; + $seps[] = $flip ? array($y1, $x1) : array($x1, $y1); + } + $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim); + + return array($this->lcs, $seps); + } + + function _lcsPos($ypos) + { + $end = $this->lcs; + if ($end == 0 || $ypos > $this->seq[$end]) { + $this->seq[++$this->lcs] = $ypos; + $this->in_seq[$ypos] = 1; + return $this->lcs; + } + + $beg = 1; + while ($beg < $end) { + $mid = (int)(($beg + $end) / 2); + if ($ypos > $this->seq[$mid]) { + $beg = $mid + 1; + } else { + $end = $mid; + } + } + + assert($ypos != $this->seq[$end]); + + $this->in_seq[$this->seq[$end]] = false; + $this->seq[$end] = $ypos; + $this->in_seq[$ypos] = 1; + return $end; + } + + /** + * Finds LCS of two sequences. + * + * The results are recorded in the vectors $this->{x,y}changed[], by + * storing a 1 in the element for each line that is an insertion or + * deletion (ie. is not in the LCS). + * + * The subsequence of file 0 is (XOFF, XLIM) and likewise for file 1. + * + * Note that XLIM, YLIM are exclusive bounds. All line numbers are + * origin-0 and discarded lines are not counted. + */ + function _compareseq ($xoff, $xlim, $yoff, $ylim) + { + /* Slide down the bottom initial diagonal. */ + while ($xoff < $xlim && $yoff < $ylim + && $this->xv[$xoff] == $this->yv[$yoff]) { + ++$xoff; + ++$yoff; + } + + /* Slide up the top initial diagonal. */ + while ($xlim > $xoff && $ylim > $yoff + && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) { + --$xlim; + --$ylim; + } + + if ($xoff == $xlim || $yoff == $ylim) { + $lcs = 0; + } else { + /* This is ad hoc but seems to work well. $nchunks = + * sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); $nchunks = + * max(2,min(8,(int)$nchunks)); */ + $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1; + list($lcs, $seps) + = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks); + } + + if ($lcs == 0) { + /* X and Y sequences have no common subsequence: mark all + * changed. */ + while ($yoff < $ylim) { + $this->ychanged[$this->yind[$yoff++]] = 1; + } + while ($xoff < $xlim) { + $this->xchanged[$this->xind[$xoff++]] = 1; + } + } else { + /* Use the partitions to split this problem into subproblems. */ + reset($seps); + $pt1 = $seps[0]; + while ($pt2 = next($seps)) { + $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]); + $pt1 = $pt2; + } + } + } + + /** + * Adjusts inserts/deletes of identical lines to join changes as much as + * possible. + * + * We do something when a run of changed lines include a line at one end + * and has an excluded, identical line at the other. We are free to + * choose which identical line is included. `compareseq' usually chooses + * the one at the beginning, but usually it is cleaner to consider the + * following identical line to be the "change". + * + * This is extracted verbatim from analyze.c (GNU diffutils-2.7). + */ + function _shiftBoundaries($lines, &$changed, $other_changed) + { + $i = 0; + $j = 0; + + assert('count($lines) == count($changed)'); + $len = count($lines); + $other_len = count($other_changed); + + while (1) { + /* Scan forward to find the beginning of another run of + * changes. Also keep track of the corresponding point in the + * other file. + * + * Throughout this code, $i and $j are adjusted together so that + * the first $i elements of $changed and the first $j elements of + * $other_changed both contain the same number of zeros (unchanged + * lines). + * + * Furthermore, $j is always kept so that $j == $other_len or + * $other_changed[$j] == false. */ + while ($j < $other_len && $other_changed[$j]) { + $j++; + } + + while ($i < $len && ! $changed[$i]) { + assert('$j < $other_len && ! $other_changed[$j]'); + $i++; $j++; + while ($j < $other_len && $other_changed[$j]) { + $j++; + } + } + + if ($i == $len) { + break; + } + + $start = $i; + + /* Find the end of this run of changes. */ + while (++$i < $len && $changed[$i]) { + continue; + } + + do { + /* Record the length of this run of changes, so that we can + * later determine whether the run has grown. */ + $runlength = $i - $start; + + /* Move the changed region back, so long as the previous + * unchanged line matches the last changed one. This merges + * with previous changed regions. */ + while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) { + $changed[--$start] = 1; + $changed[--$i] = false; + while ($start > 0 && $changed[$start - 1]) { + $start--; + } + assert('$j > 0'); + while ($other_changed[--$j]) { + continue; + } + assert('$j >= 0 && !$other_changed[$j]'); + } + + /* Set CORRESPONDING to the end of the changed run, at the + * last point where it corresponds to a changed run in the + * other file. CORRESPONDING == LEN means no such point has + * been found. */ + $corresponding = $j < $other_len ? $i : $len; + + /* Move the changed region forward, so long as the first + * changed line matches the following unchanged one. This + * merges with following changed regions. Do this second, so + * that if there are no merges, the changed region is moved + * forward as far as possible. */ + while ($i < $len && $lines[$start] == $lines[$i]) { + $changed[$start++] = false; + $changed[$i++] = 1; + while ($i < $len && $changed[$i]) { + $i++; + } + + assert('$j < $other_len && ! $other_changed[$j]'); + $j++; + if ($j < $other_len && $other_changed[$j]) { + $corresponding = $i; + while ($j < $other_len && $other_changed[$j]) { + $j++; + } + } + } + } while ($runlength != $i - $start); + + /* If possible, move the fully-merged run of changes back to a + * corresponding run in the other file. */ + while ($corresponding < $i) { + $changed[--$start] = 1; + $changed[--$i] = 0; + assert('$j > 0'); + while ($other_changed[--$j]) { + continue; + } + assert('$j >= 0 && !$other_changed[$j]'); + } + } + } + +} diff -r 902822492a68 -r fe660c52c48f includes/diffengine/Engine/string.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/diffengine/Engine/string.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,216 @@ + + * $patch = file_get_contents('example.patch'); + * $diff = &new Text_Diff('string', array($patch)); + * $renderer = &new Text_Diff_Renderer_inline(); + * echo $renderer->render($diff); + * + * + * @author �rjan Persson + * @copyright Copyright 2005 �rjan Persson + * @package Text_Diff + * @since 0.2.0 + * @access private + */ +class Text_Diff_Engine_string { + + /** + * Parses a unified or context diff. + * + * First param contains the whole diff and the second can be used to force + * a specific diff type. If the second parameter is 'autodetect', the + * diff will be examined to find out which type of diff this is. + * + * @param string $diff The diff content. + * @param string $mode The diff mode of the content in $diff. One of + * 'context', 'unified', or 'autodetect'. + * + * @return array List of all diff operations. + */ + function diff($diff, $mode = 'autodetect') + { + if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') { + die_friendly('Text_Diff', '

Type of diff is unsupported

'); + } + + if ($mode == 'autodetect') { + $context = strpos($diff, '***'); + $unified = strpos($diff, '---'); + if ($context === $unified) { + die_friendly('Text_Diff', '

Type of diff could not be detected

'); + } elseif ($context === false || $context === false) { + $mode = $context !== false ? 'context' : 'unified'; + } else { + $mode = $context < $unified ? 'context' : 'unified'; + } + } + + // split by new line and remove the diff header + $diff = explode("\n", $diff); + array_shift($diff); + array_shift($diff); + + if ($mode == 'context') { + return $this->parseContextDiff($diff); + } else { + return $this->parseUnifiedDiff($diff); + } + } + + /** + * Parses an array containing the unified diff. + * + * @param array $diff Array of lines. + * + * @return array List of all diff operations. + */ + function parseUnifiedDiff($diff) + { + $edits = array(); + $end = count($diff) - 1; + for ($i = 0; $i < $end;) { + $diff1 = array(); + switch (substr($diff[$i], 0, 1)) { + case ' ': + do { + $diff1[] = substr($diff[$i], 1); + } while (++$i < $end && substr($diff[$i], 0, 1) == ' '); + $edits[] = &new Text_Diff_Op_copy($diff1); + break; + case '+': + // get all new lines + do { + $diff1[] = substr($diff[$i], 1); + } while (++$i < $end && substr($diff[$i], 0, 1) == '+'); + $edits[] = &new Text_Diff_Op_add($diff1); + break; + case '-': + // get changed or removed lines + $diff2 = array(); + do { + $diff1[] = substr($diff[$i], 1); + } while (++$i < $end && substr($diff[$i], 0, 1) == '-'); + while ($i < $end && substr($diff[$i], 0, 1) == '+') { + $diff2[] = substr($diff[$i++], 1); + } + if (count($diff2) == 0) { + $edits[] = &new Text_Diff_Op_delete($diff1); + } else { + $edits[] = &new Text_Diff_Op_change($diff1, $diff2); + } + break; + default: + $i++; + break; + } + } + return $edits; + } + + /** + * Parses an array containing the context diff. + * + * @param array $diff Array of lines. + * + * @return array List of all diff operations. + */ + function parseContextDiff(&$diff) + { + $edits = array(); + $i = $max_i = $j = $max_j = 0; + $end = count($diff) - 1; + while ($i < $end && $j < $end) { + while ($i >= $max_i && $j >= $max_j) { + // find the boundaries of the diff output of the two files + for ($i = $j; + $i < $end && substr($diff[$i], 0, 3) == '***'; + $i++); + for ($max_i = $i; + $max_i < $end && substr($diff[$max_i], 0, 3) != '---'; + $max_i++); + for ($j = $max_i; + $j < $end && substr($diff[$j], 0, 3) == '---'; + $j++); + for ($max_j = $j; + $max_j < $end && substr($diff[$max_j], 0, 3) != '***'; + $max_j++); + } + + // find what hasn't been changed + $array = array(); + while ($i < $max_i && + $j < $max_j && + strcmp($diff[$i], $diff[$j]) == 0) { + $array[] = substr($diff[$i], 2); + $i++; + $j++; + } + while ($i < $max_i && ($max_j-$j) <= 1) { + if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') { + break; + } + $array[] = substr($diff[$i++], 2); + } + while ($j < $max_j && ($max_i-$i) <= 1) { + if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') { + break; + } + $array[] = substr($diff[$j++], 2); + } + if (count($array) > 0) { + $edits[] = &new Text_Diff_Op_copy($array); + } + + if ($i < $max_i) { + $diff1 = array(); + switch (substr($diff[$i], 0, 1)) { + case '!': + $diff2 = array(); + do { + $diff1[] = substr($diff[$i], 2); + if ($j < $max_j && substr($diff[$j], 0, 1) == '!') { + $diff2[] = substr($diff[$j++], 2); + } + } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!'); + $edits[] = &new Text_Diff_Op_change($diff1, $diff2); + break; + case '+': + do { + $diff1[] = substr($diff[$i], 2); + } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+'); + $edits[] = &new Text_Diff_Op_add($diff1); + break; + case '-': + do { + $diff1[] = substr($diff[$i], 2); + } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-'); + $edits[] = &new Text_Diff_Op_delete($diff1); + break; + } + } + + if ($j < $max_j) { + $diff2 = array(); + switch (substr($diff[$j], 0, 1)) { + case '+': + do { + $diff2[] = substr($diff[$j++], 2); + } while ($j < $max_j && substr($diff[$j], 0, 1) == '+'); + $edits[] = &new Text_Diff_Op_add($diff2); + break; + case '-': + do { + $diff2[] = substr($diff[$j++], 2); + } while ($j < $max_j && substr($diff[$j], 0, 1) == '-'); + $edits[] = &new Text_Diff_Op_delete($diff2); + break; + } + } + } + return $edits; + } +} diff -r 902822492a68 -r fe660c52c48f includes/diffengine/Engine/xdiff.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/diffengine/Engine/xdiff.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,55 @@ + + * @package Text_Diff + * + * @access private + */ +class Text_Diff_Engine_xdiff { + + function diff($from_lines, $to_lines) + { + array_walk($from_lines, array('Text_Diff', 'trimNewlines')); + array_walk($to_lines, array('Text_Diff', 'trimNewlines')); + + /* Convert the two input arrays into strings for xdiff processing. */ + $from_string = implode("\n", $from_lines); + $to_string = implode("\n", $to_lines); + + /* Diff the two strings and convert the result to an array. */ + $diff = xdiff_string_diff($from_string, $to_string, count($to_lines)); + $diff = explode("\n", $diff); + + /* Walk through the diff one line at a time. We build the $edits + * array of diff operations by reading the first character of the + * xdiff output (which is in the "unified diff" format). + * + * Note that we don't have enough information to detect "changed" + * lines using this approach, so we can't add Text_Diff_Op_changed + * instances to the $edits array. The result is still perfectly + * valid, albeit a little less descriptive and efficient. */ + $edits = array(); + foreach ($diff as $line) { + switch ($line[0]) { + case ' ': + $edits[] = &new Text_Diff_Op_copy(array(substr($line, 1))); + break; + + case '+': + $edits[] = &new Text_Diff_Op_add(array(substr($line, 1))); + break; + + case '-': + $edits[] = &new Text_Diff_Op_delete(array(substr($line, 1))); + break; + } + } + + return $edits; + } + +} diff -r 902822492a68 -r fe660c52c48f includes/diffengine/Renderer.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/diffengine/Renderer.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,211 @@ + $value) { + $v = '_' . $param; + if (isset($this->$v)) { + $this->$v = $value; + } + } + } + + /** + * Get any renderer parameters. + * + * @return array All parameters of this renderer object. + */ + function getParams() + { + $params = array(); + foreach (get_object_vars($this) as $k => $v) { + if ($k[0] == '_') { + $params[substr($k, 1)] = $v; + } + } + + return $params; + } + + /** + * Renders a diff. + * + * @param Text_Diff $diff A Text_Diff object. + * + * @return string The formatted output. + */ + function render($diff) + { + $xi = $yi = 1; + $block = false; + $context = array(); + + $nlead = $this->_leading_context_lines; + $ntrail = $this->_trailing_context_lines; + + $output = $this->_startDiff(); + + $diffs = $diff->getDiff(); + foreach ($diffs as $i => $edit) { + if (is_a($edit, 'Text_Diff_Op_copy')) { + if (is_array($block)) { + $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail; + if (count($edit->orig) <= $keep) { + $block[] = $edit; + } else { + if ($ntrail) { + $context = array_slice($edit->orig, 0, $ntrail); + $block[] = &new Text_Diff_Op_copy($context); + } + $output .= $this->_block($x0, $ntrail + $xi - $x0, + $y0, $ntrail + $yi - $y0, + $block); + $block = false; + } + } + $context = $edit->orig; + } else { + if (!is_array($block)) { + $context = array_slice($context, count($context) - $nlead); + $x0 = $xi - count($context); + $y0 = $yi - count($context); + $block = array(); + if ($context) { + $block[] = &new Text_Diff_Op_copy($context); + } + } + $block[] = $edit; + } + + if ($edit->orig) { + $xi += count($edit->orig); + } + if ($edit->final) { + $yi += count($edit->final); + } + } + + if (is_array($block)) { + $output .= $this->_block($x0, $xi - $x0, + $y0, $yi - $y0, + $block); + } + + return $output . $this->_endDiff(); + } + + function _block($xbeg, $xlen, $ybeg, $ylen, &$edits) + { + $output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen)); + + foreach ($edits as $edit) { + switch (strtolower(get_class($edit))) { + case 'text_diff_op_copy': + $output .= $this->_context($edit->orig); + break; + + case 'text_diff_op_add': + $output .= $this->_added($edit->final); + break; + + case 'text_diff_op_delete': + $output .= $this->_deleted($edit->orig); + break; + + case 'text_diff_op_change': + $output .= $this->_changed($edit->orig, $edit->final); + break; + } + } + + return $output . $this->_endBlock(); + } + + function _startDiff() + { + return ''; + } + + function _endDiff() + { + return ''; + } + + function _blockHeader($xbeg, $xlen, $ybeg, $ylen) + { + if ($xlen > 1) { + $xbeg .= ',' . ($xbeg + $xlen - 1); + } + if ($ylen > 1) { + $ybeg .= ',' . ($ybeg + $ylen - 1); + } + + return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg; + } + + function _startBlock($header) + { + return $header . "\n"; + } + + function _endBlock() + { + return ''; + } + + function _lines($lines, $prefix = ' ') + { + return $prefix . implode("\n$prefix", $lines) . "\n"; + } + + function _context($lines) + { + return $this->_lines($lines); + } + + function _added($lines) + { + return $this->_lines($lines, '>'); + } + + function _deleted($lines) + { + return $this->_lines($lines, '<'); + } + + function _changed($orig, $final) + { + return $this->_deleted($orig) . "---\n" . $this->_added($final); + } + +} diff -r 902822492a68 -r fe660c52c48f includes/diffengine/Renderer/inline.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/diffengine/Renderer/inline.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,158 @@ +'; + + /** + * Suffix for inserted text. + */ + var $_ins_suffix = ''; + + /** + * Prefix for deleted text. + */ + var $_del_prefix = ''; + + /** + * Suffix for deleted text. + */ + var $_del_suffix = ''; + + /** + * Header for each change block. + */ + var $_block_header = ''; + + /** + * What are we currently splitting on? Used to recurse to show word-level + * changes. + */ + var $_split_level = 'words'; + + function _blockHeader($xbeg, $xlen, $ybeg, $ylen) + { + return $this->_block_header; + } + + function _startBlock($header) + { + return $header; + } + + function _lines($lines, $prefix = ' ', $encode = true) + { + if ($encode) { + array_walk($lines, array(&$this, '_encode')); + } + + if ($this->_split_level == 'words') { + return implode('', $lines); + } else { + return implode("\n", $lines) . "\n"; + } + } + + function _added($lines) + { + array_walk($lines, array(&$this, '_encode')); + $lines[0] = $this->_ins_prefix . $lines[0]; + $lines[count($lines) - 1] .= $this->_ins_suffix; + return $this->_lines($lines, ' ', false); + } + + function _deleted($lines, $words = false) + { + array_walk($lines, array(&$this, '_encode')); + $lines[0] = $this->_del_prefix . $lines[0]; + $lines[count($lines) - 1] .= $this->_del_suffix; + return $this->_lines($lines, ' ', false); + } + + function _context($lines) + { + return "\n".$this->_lines($lines).''."\n\n\n"; + } + + function _changed($orig, $final) + { + /* If we've already split on words, don't try to do so again - just + * display. */ + if ($this->_split_level == 'words') { + $prefix = ''; + while ($orig[0] !== false && $final[0] !== false && + substr($orig[0], 0, 1) == ' ' && + substr($final[0], 0, 1) == ' ') { + $prefix .= substr($orig[0], 0, 1); + $orig[0] = substr($orig[0], 1); + $final[0] = substr($final[0], 1); + } + return $prefix . $this->_deleted($orig) . $this->_added($final); + } + + $text1 = implode("\n", $orig); + $text2 = implode("\n", $final); + + /* Non-printing newline marker. */ + $nl = "\0"; + + /* We want to split on word boundaries, but we need to + * preserve whitespace as well. Therefore we split on words, + * but include all blocks of whitespace in the wordlist. */ + $diff = &new Text_Diff($this->_splitOnWords($text1, $nl), + $this->_splitOnWords($text2, $nl)); + + /* Get the diff in inline format. */ + $renderer = &new Text_Diff_Renderer_inline(array_merge($this->getParams(), + array('split_level' => 'words'))); + + /* Run the diff and get the output. */ + return str_replace($nl, "\n", $renderer->render($diff)) . "\n"; + } + + function _splitOnWords($string, $newlineEscape = "\n") + { + $words = array(); + $length = strlen($string); + $pos = 0; + + while ($pos < $length) { + // Eat a word with any preceding whitespace. + $spaces = strspn(substr($string, $pos), " \n"); + $nextpos = strcspn(substr($string, $pos + $spaces), " \n"); + $words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos)); + $pos += $spaces + $nextpos; + } + + return $words; + } + + function _encode(&$string) + { + $string = htmlspecialchars($string); + } + +} diff -r 902822492a68 -r fe660c52c48f includes/diffengine/Renderer/unified.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/diffengine/Renderer/unified.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,50 @@ +_lines($lines, '+'); + } + + function _deleted($lines) + { + return $this->_lines($lines, '-'); + } + + function _changed($orig, $final) + { + return $this->_deleted($orig) . $this->_added($final); + } + +} diff -r 902822492a68 -r fe660c52c48f includes/diffengine/Renderer/xhtml.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/diffengine/Renderer/xhtml.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,262 @@ +\n+"; + + /** + * Suffix for inserted text. + */ + var $_ins_suffix = "\n\n\n"; + + /** + * Prefix for deleted text. + */ + var $_del_prefix = "\n-"; + + /** + * Suffix for deleted text. + */ + var $_del_suffix = "\n\n\n"; + + /** + * Header for each change block. + */ + var $_block_header = ''; + + /** + * What are we currently splitting on? Used to recurse to show word-level + * changes. + */ + var $_split_level = 'lines'; + + function _blockHeader($xbeg, $xlen, $ybeg, $ylen) + { + return "\nLine $xbeg: {$this->_block_header}"; + } + + function _startBlock($header) + { + return $header; + } + + function _lines($lines, $prefix = ' ', $encode = true) + { + if ($encode) { + array_walk($lines, array(&$this, '_encode')); + } + + if ($this->_split_level == 'words') { + return implode('', $lines); + } else { + return implode("
", $lines) . "\n"; + } + } + + function _added($lines) + { + array_walk($lines, array(&$this, '_encode')); + $lines[0] = $this->_ins_prefix . $lines[0]; + $lines[count($lines) - 1] .= $this->_ins_suffix; + return $this->_lines($lines, ' ', false); + } + + function _deleted($lines, $words = false) + { + array_walk($lines, array(&$this, '_encode')); + $lines[0] = $this->_del_prefix . $lines[0]; + $lines[count($lines) - 1] .= $this->_del_suffix; + return $this->_lines($lines, ' ', false); + } + + function _context($lines) + { + return "\n".$this->_lines($lines).''."\n\n\n"; + } + + function _changed($orig, $final) + { + /* If we've already split on words, don't try to do so again - just display. */ + if ($this->_split_level == 'words') { + $prefix = ''; + while ($orig[0] !== false && $final[0] !== false && + substr($orig[0], 0, 1) == ' ' && + substr($final[0], 0, 1) == ' ') { + $prefix .= substr($orig[0], 0, 1); + $orig[0] = substr($orig[0], 1); + $final[0] = substr($final[0], 1); + } + $ret = $prefix . $this->_deleted($orig) . $this->_added($final) . "\n"; + //echo 'DEBUG:
'.htmlspecialchars($ret).'
'; + return $ret; + } + + $text1 = implode("\n", $orig); + $text2 = implode("\n", $final); + + /* Non-printing newline marker. */ + $nl = "\0"; + + /* We want to split on word boundaries, but we need to + * preserve whitespace as well. Therefore we split on words, + * but include all blocks of whitespace in the wordlist. */ + $diff = &new Text_Diff($this->_splitOnWords($text1, $nl), + $this->_splitOnWords($text2, $nl)); + + /* Get the diff in inline format. */ + $renderer = &new Text_Diff_Renderer_inline(array_merge($this->getParams(), + array('split_level' => 'words'))); + + /* Run the diff and get the output. */ + $ret = str_replace($nl, "
", $renderer->render($diff)); + //echo 'DEBUG:
'.htmlspecialchars($ret).'
'; + return $ret . "\n"; + } + + function _splitOnWords($string, $newlineEscape = "
") + { + $words = array(); + $length = strlen($string); + $pos = 0; + + while ($pos < $length) { + // Eat a word with any preceding whitespace. + $spaces = strspn(substr($string, $pos), " \n"); + $nextpos = strcspn(substr($string, $pos + $spaces), " \n"); + $words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos)); + $pos += $spaces + $nextpos; + } + + return $words; + } + + function _encode(&$string) + { + $string = htmlspecialchars($string); + } + + /** + * Renders a diff. + * + * @param Text_Diff $diff A Text_Diff object. + * + * @return string The formatted output. + */ + + function render($diff) + { + $xi = $yi = 1; + $block = false; + $context = array(); + + $nlead = $this->_leading_context_lines; + $ntrail = $this->_trailing_context_lines; + + $output = $this->_startDiff(); + + $diffs = $diff->getDiff(); + foreach ($diffs as $i => $edit) { + if (is_a($edit, 'Text_Diff_Op_copy')) { + if (is_array($block)) { + $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail; + if (count($edit->orig) <= $keep) { + $block[] = $edit; + } else { + if ($ntrail) { + $context = array_slice($edit->orig, 0, $ntrail); + $block[] = &new Text_Diff_Op_copy($context); + } + $bk = $this->_block($x0, $ntrail + $xi - $x0, + $y0, $ntrail + $yi - $y0, + $block); + $output .= $bk; + $block = false; + } + } + $context = $edit->orig; + } else { + if (!is_array($block)) { + $context = array_slice($context, count($context) - $nlead); + $x0 = $xi - count($context); + $y0 = $yi - count($context); + $block = array(); + if ($context) { + $block[] = &new Text_Diff_Op_copy($context); + } + } + $block[] = $edit; + } + + if ($edit->orig) { + $xi += count($edit->orig); + } + if ($edit->final) { + $yi += count($edit->final); + } + } + + if (is_array($block)) { + $bk = $this->_block($x0, $xi - $x0, + $y0, $yi - $y0, + $block); + $output .= $bk; + } + + $final = $output . $this->_endDiff(); + if ($final == '') $final = 'No differences.'; + //$final = preg_replace('#('.preg_quote($this->_ins_suffix).'|'.preg_quote($this->_del_suffix).')(.+?)('.preg_quote($this->_ins_prefix).'|'.preg_quote($this->_ins_suffix).')#', '\\1'.$final.''."\n\n"; + } + + function _block($xbeg, $xlen, $ybeg, $ylen, &$edits) + { + $output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen)); + + foreach ($edits as $edit) { + switch (strtolower(get_class($edit))) { + case 'text_diff_op_copy': + $output .= $this->_context($edit->orig); + break; + + case 'text_diff_op_add': + $output .= $this->_added($edit->final); + break; + + case 'text_diff_op_delete': + $output .= $this->_deleted($edit->orig); + break; + + case 'text_diff_op_change': + $output .= $this->_changed($edit->orig, $edit->final); + break; + } + } + + return $output . $this->_endBlock(); + } + +} diff -r 902822492a68 -r fe660c52c48f includes/email.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/email.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,622 @@ +reset(); + $this->use_smtp = $use_smtp; + $this->reply_to = $this->from = ''; + } + + // Resets all the data (address, template file, etc etc to default + function reset() + { + $this->addresses = array(); + $this->vars = $this->msg = $this->extra_headers = ''; + } + + // Sets an email address to send to + function email_address($address) + { + $this->addresses['to'] = trim($address); + } + + function cc($address) + { + $this->addresses['cc'][] = trim($address); + } + + function bcc($address) + { + $this->addresses['bcc'][] = trim($address); + } + + function replyto($address) + { + $this->reply_to = trim($address); + } + + function from($address) + { + $this->from = trim($address); + } + + // set up subject for mail + function set_subject($subject = '') + { + $this->subject = trim(preg_replace('#[\n\r]+#s', '', $subject)); + } + + // set up extra mail headers + function extra_headers($headers) + { + $this->extra_headers .= trim($headers) . "\n"; + } + + function use_template($template_code) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $this->tpl_msg = $template->makeParserText($template_code); + + return true; + } + + // assign variables + function assign_vars($vars) + { + if ( is_object($this->tpl_msg) ) + { + $this->tpl_msg->assign_vars($vars); + } + else + { + die_friendly(GENERAL_ERROR, 'Can\'t set vars, the template is not set'); + } + } + + // Send the mail out to the recipients set previously in var $this->address + function send() + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $this->msg = $this->tpl_msg->run(); + if ( empty($this->msg) ) + { + die_friendly(GENERAL_ERROR, 'Template for e-mail message returned a blank'); + } + + // We now try and pull a subject from the email body ... if it exists, + // do this here because the subject may contain a variable + $drop_header = ''; + $match = array(); + if (preg_match('#^(Subject:(.*?))$#m', $this->msg, $match)) + { + $this->subject = (trim($match[2]) != '') ? trim($match[2]) : (($this->subject != '') ? $this->subject : 'No Subject'); + $drop_header .= '[\r\n]*?' . preg_quote($match[1], '#'); + } + else + { + $this->subject = (($this->subject != '') ? $this->subject : 'No Subject'); + } + + if (preg_match('#^(Charset:(.*?))$#m', $this->msg, $match)) + { + $this->encoding = (trim($match[2]) != '') ? trim($match[2]) : trim('iso-8859-1'); + $drop_header .= '[\r\n]*?' . preg_quote($match[1], '#'); + } + else + { + $this->encoding = trim('iso-8859-1'); + } + + if ($drop_header != '') + { + $this->msg = trim(preg_replace('#' . $drop_header . '#s', '', $this->msg)); + } + + $to = $this->addresses['to']; + + $cc = (count($this->addresses['cc'])) ? implode(', ', $this->addresses['cc']) : ''; + $bcc = (count($this->addresses['bcc'])) ? implode(', ', $this->addresses['bcc']) : ''; + + // Build header + $this->extra_headers = (($this->reply_to != '') ? "Reply-to: $this->reply_to\n" : '') . + (($this->from != '') ? "From: $this->from\n" : "From: " . getConfig('contact_email') . "\n") . + "Return-Path: " . getConfig('contact_email') . + "\nMessage-ID: <" . md5(uniqid(time())) . "@" . $_SERVER['SERVER_NAME'] . ">\nMIME-Version: 1.0\nContent-type: text/plain; charset=" . $this->encoding . + "\nContent-transfer-encoding: 8bit\nDate: " . date('r', time()) . + "\nX-Priority: 3\nX-MSMail-Priority: Normal\nX-Mailer: PHP\nX-MimeOLE: Produced By Enano CMS\n" . + $this->extra_headers . + (($cc != '') ? "Cc: $cc\n" : '') . + (($bcc != '') ? "Bcc: $bcc\n" : ''); + + //die('
'.print_r($this,true).'
'); + + // Send message ... removed $this->encode() from subject for time being + if ( $this->use_smtp ) + { + $result = smtp_send_email_core($to, $this->subject, $this->msg, $this->extra_headers); + } + else + { + $empty_to_header = ($to == '') ? TRUE : FALSE; + $to = ($to == '') ? ((getConfig('sendmail_fix')=='1') ? ' ' : 'Undisclosed-recipients:;') : $to; + + $result = @mail($to, $this->subject, preg_replace("#(?msg), $this->extra_headers); + + if (!$result && !getConfig('sendmail_fix') && $empty_to_header) + { + $to = ' '; + + setConfig('sendmail_fix', '1'); + + $result = @mail($to, $this->subject, preg_replace("#(?msg), $this->extra_headers); + } + } + + // Did it work? + if (!$result || ( $this->use_smtp && $result != 'success' )) + { + die_friendly(GENERAL_ERROR, 'Failed sending email :: ' . (($this->use_smtp) ? 'SMTP' : 'PHP') . ' :: ' . $result); + } + + return true; + } + + // Encodes the given string for proper display for this encoding ... nabbed + // from php.net and modified. There is an alternative encoding method which + // may produce lesd output but it's questionable as to its worth in this + // scenario IMO + function encode($str) + { + if ($this->encoding == '') + { + return $str; + } + + // define start delimimter, end delimiter and spacer + $end = "?="; + $start = "=?$this->encoding?B?"; + $spacer = "$end\r\n $start"; + + // determine length of encoded text within chunks and ensure length is even + $length = 75 - strlen($start) - strlen($end); + $length = floor($length / 2) * 2; + + // encode the string and split it into chunks with spacers after each chunk + $str = chunk_split(base64_encode($str), $length, $spacer); + + // remove trailing spacer and add start and end delimiters + $str = preg_replace('#' . preg_quote($spacer, '#') . '$#', '', $str); + + return $start . $str . $end; + } + + // + // Attach files via MIME. + // + function attachFile($filename, $mimetype = "application/octet-stream", $szFromAddress, $szFilenameToDisplay) + { + global $lang; + $mime_boundary = "--==================_846811060==_"; + + $this->msg = '--' . $mime_boundary . "\nContent-Type: text/plain;\n\tcharset=".'"' . $lang['ENCODING'] . '"'."\n\n" . $this->msg; + + if ($mime_filename) + { + $filename = $mime_filename; + $encoded = $this->encode_file($filename); + } + + $fd = fopen($filename, "r"); + $contents = fread($fd, filesize($filename)); + + $this->mimeOut = "--" . $mime_boundary . "\n"; + $this->mimeOut .= "Content-Type: " . $mimetype . ";\n\tname=".'"'."$szFilenameToDisplay".'"'."\n"; + $this->mimeOut .= "Content-Transfer-Encoding: quoted-printable\n"; + $this->mimeOut .= "Content-Disposition: attachment;\n\tfilename=".'"'."$szFilenameToDisplay".'"'."\n\n"; + + if ( $mimetype == "message/rfc822" ) + { + $this->mimeOut .= "From: ".$szFromAddress."\n"; + $this->mimeOut .= "To: ".$this->emailAddress."\n"; + $this->mimeOut .= "Date: ".date("D, d M Y H:i:s") . " UT\n"; + $this->mimeOut .= "Reply-To:".$szFromAddress."\n"; + $this->mimeOut .= "Subject: ".$this->mailSubject."\n"; + $this->mimeOut .= "X-Mailer: PHP/".phpversion()."\n"; + $this->mimeOut .= "MIME-Version: 1.0\n"; + } + + $this->mimeOut .= $contents."\n"; + $this->mimeOut .= "--" . $mime_boundary . "--" . "\n"; + + return $out; + // added -- to notify email client attachment is done + } + + function getMimeHeaders($filename, $mime_filename="") + { + $mime_boundary = "--==================_846811060==_"; + + if ($mime_filename) + { + $filename = $mime_filename; + } + + $out = "MIME-Version: 1.0\n"; + $out .= "Content-Type: multipart/mixed;\n\tboundary=".'"'."$mime_boundary".'"'."\n\n"; + $out .= "This message is in MIME format. Since your mail reader does not understand\n"; + $out .= "this format, some or all of this message may not be legible."; + + return $out; + } + + // + // Split string by RFC 2045 semantics (76 chars per line, end with \r\n). + // + function myChunkSplit($str) + { + $stmp = $str; + $len = strlen($stmp); + $out = ""; + + while ($len > 0) + { + if ($len >= 76) + { + $out .= substr($stmp, 0, 76) . "\r\n"; + $stmp = substr($stmp, 76); + $len = $len - 76; + } + else + { + $out .= $stmp . "\r\n"; + $stmp = ""; + $len = 0; + } + } + return $out; + } + + // + // Split the specified file up into a string and return it + // + function encode_file($sourcefile) + { + if (is_readable(@realpath($sourcefile))) + { + $fd = fopen($sourcefile, "r"); + $contents = fread($fd, filesize($sourcefile)); + $encoded = $this->myChunkSplit(base64_encode($contents)); + fclose($fd); + } + + return $encoded; + } + +} // class emailer + + +/** + * This code is copyright (C) 2004 Jim Tucek + * PHP version ported from Javascript by Dan Fuhry + * All rights reserved. + * @link http://www.jracademy.com/~jtucek/email/ + * @license GNU General Public License v2, permission obtained specifically for Enano + */ + +class EmailEncryptor +{ + + var $primes = Array(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199); + + function __construct() + { + $i = 0; + $this->p = 0; + $this->q = 0; + while($this->p * $this->q < 255 || $this->p == $this->q) + { + $this->p = $this->primes[mt_rand(0, sizeof($this->primes)-1)]; + $this->q = $this->primes[mt_rand(0, sizeof($this->primes)-1)]; + } + } + + function EmailEncryptor() + { + $this->__construct(); + } + + function testAll() { + $size = sizeof($this->primes); + + $allCharacters = ""; + for($c = 33; $c <= 126; $c++) + $allCharacters = $allCharacters . $this->fromCharCode($c); + + for($i = 0; $i < $size - 1; $i++) { + for($j = $i + 1; $j < $size; $j++) { + $this->p = $this->primes[$i]; + $this->q = $this->primes[$j]; + if($this->p*$this->q < 255) + break; + $k = $this->makeKey($allCharacters); + die('
'.print_r($k, true).'
'); + $encrypted = $k['X']; + $decrypted = $this->goForth($encrypted,$this->p*$this->q,$k['D']); + if($decrypted != $allCharacters) { + die('Test failed'); + } + } + } + return 'GOOD'; + } + + function charCodeAt($str, $i) + { + return ord(substr($str, $i, 1)); + } + + function fromCharCode($str) + { + return chr($str); + } + + function MakeArray($l) { + $a = Array(); + $i=0; + do { + $a[$i]=null; + $i++; + } while($i < $l); + return $a; + } + + function makeKey($addr,$subj = '',$body = '') { + $value = ""; + + if($this->p * $this->q < 255) + { + return("P*Q must be greater than 255! P*Q = " . $this->p*$this->q); + } + elseif($this->p == $this->q) + { + return("P cannot be equal to Q!"); + } + elseif($addr == "") + { + return("You must enter an address to encrypt!"); + } + else + { + // Make the key + $c = 0; + $z = ($this->p-1)*($this->q-1); + $e = 0; + $n = $this->p*$this->q; + $d = 0; + + do { + $e++; + $d = $this->getKey($this->primes[$e],$z); + } while($d==1); + $e = $this->primes[$e]; + + // Turn the string into an array of numbers < 255 + $m = $addr; + $emailLength = strlen($m); + $justEmail = ""; + $sep = ( strstr('?', $m) ) ? '&' : '?'; + if($subj != "") { + $m = $m . "{$sep}subject=" . $subj; + } + $sep = ( strstr($m, '?') ) ? '&' : '?'; + if($body != "") { + $m = $m . "{$sep}body=" . $body; + } + + $length = strlen($m); + $theString = $this->MakeArray($length); + for($i = 0; $i < $length; $i++) { + $theString[$i] = $this->charCodeAt($m, $i); + } + + // Encrypt each of the numbers + $theCode = $this->MakeArray($length); + $c = ""; + $temp = 0; + for($i = 0; $i < $length; $i++) { + if($i != 0) + $c .= " "; + $temp = $this->myMod($theString[$i],$e,$n); + $theCode[$i] = $temp; + $c .= $temp; + if($i == $emailLength - 1) + $justEmail = $c; + } + } + return Array('X'=>$justEmail, 'N'=>$n, 'D'=>$d, 'E'=>$e, 'C'=>$c, 'M'=>$m); + } + + // Finds x^e % y for large values of (x^e) + function myMod($x,$e,$y) { + if ($e % 2 == 0) { + $answer = 1; + for($i = 1; $i <= e/2; $i++) { + $temp = ($x*$x) % $y; + $answer = ($temp*$answer) % $y; + } + } else { + $answer = $x; + for($i = 1; $i <= $e/2; $i++) { + $temp = ($x*$x) % $y; + $answer = ($temp*$answer) % $y; + } + } + return $answer; + } + + + function getKey($e,$z) { + $A = 1; + $B = 0; + $C = $z; + $F = 0; + $G = 1; + $bar = $e; + // Euclid's Algorithm: + while ($bar != 0) { + $foo = floor($C/$bar); + $K = $A - $foo * $F; + $L = $B - $foo * $G; + $M = $C - $foo * $bar; + $A = $F; + $B = $G; + $C = $bar; + $F = $K; + $G = $L; + $bar = $M; + } + if ($B < 0) + { + return ($B + $z); + } + else + { + return ($B); + } + } + + function goForth($c,$n,$d) { + $c .= " "; + $length = strlen($c); + $number = 0; + $bar = 0; + $answer = ""; + + for($i = 0; $i < $length; $i++) { + $number = 0; + $bar = 0; + while($this->charCodeAt($c, $i) != 32) { + $number = $number * 10; + $number = $number + $this->charCodeAt($c, $i)-48; + $i++; + } + $answer .= $this->fromCharCode($this->decrypt($number,$n,$d)); + } + return $answer; + } + + function decrypt($c,$n,$d) { + // Split exponents up + if ($d % 2== 0) { + $bar = 1; + for($i = 1; $i <= $d/2; $i++) { + $foo = ($c*$c) % $n; + $bar = ($foo*$bar) % $n; + } + } else { + $bar = $c; + for($i = 1; $i <= $d/2; $i++) { + $foo = ($c*$c) % $n; + $bar = ($foo*$bar) % $n; + } + } + return $bar; + } + + function writeOptions() { + $size = sizeof($this->primes); + for($i = 0; $i < $size; $i++) + echo(""); + } + + function jscode() { + return ""; + } + + /** + * Wrapper - spits out ready-to-use HTML + * @param string $address The e-mail address + * @param string $subject The subject of the e-mail. OPTIONAL. + * @param string $body The main content of the e-mail. OPTIONAL and doesn't work in many e-mail clients. + * @param string $text The text to be shown on the e-mail link. Leave as false to make the e-mail address be shown in the link (but still fully encrypted) + */ + + function encryptEmail($address, $subject = '', $body = '', $text = false) + { + $key = $this->makeKey($address, $subject, $body); + if ( $text ) + { + if(preg_match('/^(mailto:)?(?:[\w\d]+\.?)+@(?:(?:[\w\d]\-?)+\.)+\w{2,4}$/', $text)) + { + // This is a mailto link and normal obfuscation should be used + $text = false; + } + } + $text1 = ( $text ) ? '' : ''; + $text2 = ( $text ) ? "$text <".$this->obfuscate_text($this->mask_address($address)).">" : $this->obfuscate_text($this->mask_address($address)); + $email = ''.$text1.''; + return $email; + } + + /** + * Replace @ symbols with " " and dots with " ". + * @param string $email An e-mail address. + * @return string + */ + + function mask_address($email) + { + return str_replace('.', ' ', str_replace('@', ' ', $email)); + } + + /** + * Turn a string of text into hex-encoded HTML entities + * @param string $text the text to encode + * @return string + */ + + function obfuscate_text($text) + { + $a = enano_str_split($text, 1); + $s = ''; + foreach($a as $k => $c) + { + $ch = (string)dechex(ord($a[$k])); + if(strlen($ch) < 2) $ch = '0' . $ch; + $s .= '&#x'.$ch.';'; + } + return $s; + } + +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/functions.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/functions.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1903 @@ +escape($v); + $e=$db->sql_query('DELETE FROM '.table_prefix.'config WHERE config_name=\''.$n.'\';'); + if(!$e) $db->_die('Error during generic setConfig() call row deletion.'); + $e=$db->sql_query('INSERT INTO '.table_prefix.'config(config_name, config_value) VALUES(\''.$n.'\', \''.$v.'\')'); + if(!$e) $db->_die('Error during generic setConfig() call row insertion.'); +} + +function makeUrl($t, $query = false, $escape = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $flags = ''; + $sep = urlSeparator; + if(isset($_GET['printable'])) { $flags .= $sep.'printable'; $sep = '&'; } + if(isset($_GET['theme'])) { $flags .= $sep.'theme='.$session->theme; $sep = '&'; } + if(isset($_GET['style'])) { $flags .= $sep.'style='.$session->style; $sep = '&'; } + $url = $session->append_sid(contentPath.$t.$flags); + if($query) + { + $sep = strstr($url, '?') ? '&' : '?'; + $url = $url . $sep . $query; + } + return ($escape) ? htmlspecialchars($url) : $url; +} + +function makeUrlNS($n, $t, $query = false, $escape = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $flags = ''; + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) $sep = urlSeparator; + else $sep = (strstr($_SERVER['REQUEST_URI'], '?')) ? '&' : '?'; + if(isset($_GET['printable'])) { $flags .= $sep.'printable'; $sep = '&'; } + if(isset($_GET['theme'])) { $flags .= $sep.'theme='.$session->theme; $sep = '&'; } + if(isset($_GET['style'])) { $flags .= $sep.'style='.$session->style; $sep = '&'; } + + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) + { + $url = contentPath.$paths->nslist[$n].$t.$flags; + } + else + { + $url = contentPath.$n.':'.$t.$flags; + } + + if($query) + { + if(strstr($url, '?')) $sep = '&'; + else $sep = '?'; + $url = $url . $sep . $query . $flags; + } + + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) + { + $url = $session->append_sid($url); + } + + return ($escape) ? htmlspecialchars($url) : $url; +} + +function makeUrlComplete($n, $t, $query = false, $escape = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $flags = ''; + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) $sep = urlSeparator; + else $sep = (strstr($_SERVER['REQUEST_URI'], '?')) ? '&' : '?'; + if(isset($_GET['printable'])) { $flags .= $sep.'printable'; $sep = '&'; } + if(isset($_GET['theme'])) { $flags .= $sep.'theme='.$session->theme; $sep = '&'; } + if(isset($_GET['style'])) { $flags .= $sep.'style='.$session->style; $sep = '&'; } + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) $url = $session->append_sid(contentPath.$paths->nslist[$n].$t.$flags); + else $url = contentPath.$n.':'.$t.$flags; + if($query) + { + if(strstr($url, '?')) $sep = '&'; + else $sep = '?'; + $url = $url . $sep . $query . $flags; + } + $baseprot = 'http' . ( isset($_SERVER['HTTPS']) ? 's' : '' ) . '://' . $_SERVER['HTTP_HOST']; + $url = $baseprot . $url; + return ($escape) ? htmlspecialchars($url) : $url; +} + +/** + * Redirect the user to the specified URL. + * @param string $url The URL, either relative or absolute. + * @param string $title The title of the message + * @param string $message A short message to show to the user + * @param string $timeout Timeout, in seconds, to delay the redirect. Defaults to 3. + */ + +function redirect($url, $title = 'Redirecting...', $message = 'Please wait while you are redirected.', $timeout = 3) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( $timeout == 0 ) + { + header('Location: ' . $url); + header('HTTP/1.1 307 Temporary Redirect'); + } + + $template->add_header(''); + $template->add_header(' + '); + + $template->tpl_strings['PAGE_NAME'] = $title; + $template->header(true); + echo '

' . $message . '

If you are not redirected within ' . ( $timeout + 1 ) . ' seconds, please click here.

'; + $template->footer(true); + + $db->close(); + exit(0); + +} + +// Removed wikiFormat() from here, replaced with RenderMan::render + +function isPage($p) { + global $db, $session, $paths, $template, $plugins; // Common objects + if(isset($paths->pages[$p])) return true; + $d = RenderMan::strToPageID($p); + if($d[1] != 'Special' && $d[1] != 'Template' && $d[1] != 'Admin') return false; + $a = explode('/', $p); + if(isset($paths->pages[$a[0]])) return true; + else return false; +} + +function arrayItemUp($arr, $keyname) { + $keylist = array_keys($arr); + $keyflop = array_flip($keylist); + $idx = $keyflop[$keyname]; + $idxm = $idx - 1; + $temp = $arr[$keylist[$idxm]]; + if($arr[$keylist[0]] == $arr[$keyname]) return $arr; + $arr[$keylist[$idxm]] = $arr[$keylist[$idx]]; + $arr[$keylist[$idx]] = $temp; + return $arr; +} + +function arrayItemDown($arr, $keyname) { + $keylist = array_keys($arr); + $keyflop = array_flip($keylist); + $idx = $keyflop[$keyname]; + $idxm = $idx + 1; + $temp = $arr[$keylist[$idxm]]; + $sz = sizeof($arr); $sz--; + if($arr[$keylist[$sz]] == $arr[$keyname]) return $arr; + $arr[$keylist[$idxm]] = $arr[$keylist[$idx]]; + $arr[$keylist[$idx]] = $temp; + return $arr; +} + +function arrayItemTop($arr, $keyname) { + $keylist = array_keys($arr); + $keyflop = array_flip($keylist); + $idx = $keyflop[$keyname]; + while( $orig != $arr[$keylist[0]] ) { + // echo 'Keyname: '.$keylist[$idx] . '
'; flush(); ob_flush(); // Debugger + if($idx < 0) return $arr; + if($keylist[$idx] == '' || $keylist[$idx] < 0 || !$keylist[$idx]) { + /* echo 'Infinite loop caught in arrayItemTop(
';
+      print_r($arr);
+      echo '

, '.$keyname.');

EnanoCMS: Critical error during function call, exiting to prevent excessive server load.'; + exit; */ + return $arr; + } + $arr = arrayItemUp($arr, $keylist[$idx]); + $idx--; + } + return $arr; +} + +function arrayItemBottom($arr, $keyname) { + $keylist = array_keys($arr); + $keyflop = array_flip($keylist); + $idx = $keyflop[$keyname]; + $sz = sizeof($arr); $sz--; + while( $orig != $arr[$keylist[$sz]] ) { + // echo 'Keyname: '.$keylist[$idx] . '
'; flush(); ob_flush(); // Debugger + if($idx > $sz) return $arr; + if($keylist[$idx] == '' || $keylist[$idx] < 0 || !$keylist[$idx]) { + echo 'Infinite loop caught in arrayItemBottom(
';
+      print_r($arr);
+      echo '

, '.$keyname.');

EnanoCMS: Critical error during function call, exiting to prevent excessive server load.'; + exit; + } + $arr = arrayItemDown($arr, $keylist[$idx]); + $idx++; + } + return $arr; +} + +// Convert IP address to hex string +// Input: 127.0.0.1 (string) +// Output: 0x7f000001 (string) +// Updated 12/8/06 to work with PHP4 and not use eval() (blech) +function ip2hex($ip) { + if ( preg_match('/^([0-9a-f:]+)$/', $ip) ) + { + // this is an ipv6 address + return str_replace(':', '', $ip); + } + $nums = explode('.', $ip); + if(sizeof($nums) != 4) return false; + $str = '0x'; + foreach($nums as $n) + { + $str .= (string)dechex($n); + } + return $str; +} + +// Convert DWord to IP address +// Input: 0x7f000001 +// Output: 127.0.0.1 +// Updated 12/8/06 to work with PHP4 and not use eval() (blech) +function hex2ip($in) { + if(substr($in, 0, 2) == '0x') $ip = substr($in, 2, 8); + else $ip = substr($in, 0, 8); + $octets = enano_str_split($ip, 2); + $str = ''; + $newoct = Array(); + foreach($octets as $o) + { + $o = (int)hexdec($o); + $newoct[] = $o; + } + return implode('.', $newoct); +} + +// Function strip_php moved to RenderMan class + +function die_semicritical($t, $p) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $db->close(); + + if ( ob_get_status() ) + ob_end_clean(); + + dc_here('functions: calling die_semicritical'); + + $tpl = new template_nodb(); + $tpl->load_theme('oxygen', 'bleu'); + $tpl->tpl_strings['SITE_NAME'] = getConfig('site_name'); + $tpl->tpl_strings['SITE_DESC'] = getConfig('site_desc'); + $tpl->tpl_strings['COPYRIGHT'] = getConfig('copyright_notice'); + $tpl->tpl_strings['PAGE_NAME'] = $t; + $tpl->header(); + echo $p; + $tpl->footer(); + + exit; +} + +function die_friendly($t, $p) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( ob_get_status() ) + ob_end_clean(); + + dc_here('functions: calling die_friendly'); + $paths->cpage['name'] = $t; + $template->tpl_strings['PAGE_NAME'] = $t; + $template->header(); + echo $p; + $template->footer(); + $db->close(); + + exit; +} + +function grinding_halt($t, $p) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + $db->close(); + + if ( ob_get_status() ) + ob_end_clean(); + + dc_here('functions: calling grinding_halt'); + $tpl = new template_nodb(); + $tpl->load_theme('oxygen', 'bleu'); + $tpl->tpl_strings['SITE_NAME'] = 'Critical error'; + $tpl->tpl_strings['SITE_DESC'] = 'This website is experiencing a serious error and cannot load.'; + $tpl->tpl_strings['COPYRIGHT'] = 'Unable to retrieve copyright information'; + $tpl->tpl_strings['PAGE_NAME'] = $t; + $tpl->header(); + echo $p; + $tpl->footer(); + exit; +} + +function show_category_info() { + global $db, $session, $paths, $template, $plugins; // Common objects + dc_here('functions: showing category info'); + if($template->no_headers && !strpos($_SERVER['REQUEST_URI'], 'ajax.php')) return ''; + if($paths->namespace=='Category') + { + $q = $db->sql_query('SELECT page_id,namespace FROM '.table_prefix.'categories WHERE category_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\'Category\' ORDER BY page_id;'); + if(!$q) $db->_die('The category information could not be selected.'); + $ticker = -1; + echo '

Subcategories

'; + if($db->numrows() < 1) echo '

There are no subcategories in this category.

'; + echo ''; + while($row = $db->fetchrow()) + { + $ticker++;if($ticker==3) $ticker=0; + if($ticker==0) echo ''; + echo ''; + if($ticker==2) echo ''; + } + $db->free_result(); + if($ticker) echo ''; + echo '
'.$paths->pages[$paths->nslist[$row['namespace']].$row['page_id']]['name'].'
'; + + $q = $db->sql_query('SELECT page_id,namespace FROM '.table_prefix.'categories WHERE category_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace!=\'Category\' ORDER BY page_id;'); + if(!$q) $db->_die('The category information could not be selected.'); + $ticker = -1; + echo '

Pages

'; + if($db->numrows() < 1) echo '

There are no pages in this category.

'; + echo ''; + while($row = $db->fetchrow()) + { + $ticker++;if($ticker==3) $ticker=0; + if($ticker==0) echo ''; + echo ''; + if($ticker==2) echo ''; + } + $db->free_result(); + if($ticker) echo ''; + echo '
'.$paths->pages[$paths->nslist[$row['namespace']].$row['page_id']]['name'].'


'; + } + $q = $db->sql_query('SELECT category_id FROM '.table_prefix.'categories WHERE page_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\''.$paths->namespace.'\''); + if(!$q) $db->_die('The error seems to have occurred during selection of category data.'); + if($db->numrows() > 0) { + echo '
Categories: '; + $i=0; + while($r = $db->fetchrow()) + { + if($i>0) echo ', '; + $i++; + echo ''.$paths->pages[$paths->nslist['Category'].$r['category_id']]['name'].''; + } + if( ( $paths->wiki_mode && !$paths->page_protected ) || ( $session->get_permissions('edit_cat') && $session->get_permissions('even_when_protected') ) ) echo ' [ edit categorization ]
'; + } else { + echo '
Categories: '; + echo '(Uncategorized)'; + if( ( $paths->wiki_mode && !$paths->page_protected ) || ( $session->get_permissions('edit_cat') && $session->get_permissions('even_when_protected') ) ) echo ' [ edit categorization ]
'; + else echo '
'; + } + $db->free_result(); +} + +function show_file_info() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + if($paths->namespace != 'File') return null; // Prevent unnecessary work + $selfn = $paths->cpage['urlname_nons']; // substr($paths->page, strlen($paths->nslist['File']), strlen($paths->cpage)); + if(substr($paths->cpage['name'], 0, strlen($paths->nslist['File']))==$paths->nslist['File']) $selfn = substr($paths->cpage['urlname_nons'], strlen($paths->nslist['File']), strlen($paths->cpage['urlname_nons'])); + $q = $db->sql_query('SELECT mimetype,time_id,size FROM '.table_prefix.'files WHERE page_id=\''.$selfn.'\' ORDER BY time_id DESC;'); + if(!$q) $db->_die('The file type could not be fetched.'); + if($db->numrows() < 1) { echo '

Uploaded file

There are no files uploaded with this name yet. Upload a file...


'; return; } + $r = $db->fetchrow(); + $mimetype = $r['mimetype']; + $datestring = date('F d, Y h:i a', (int)$r['time_id']); + echo '

Uploaded file

Type: '.$r['mimetype'].'
Size: '; + $fs = $r['size']; + echo $fs.' bytes'; + $fs = (int)$fs; + if($fs >= 1048576) + { + $fs = round($fs / 1048576, 1); + echo ' ('.$fs.' MB)'; + } elseif($fs >= 1024) { + $fs = round($fs / 1024, 1); + echo ' ('.$fs.' KB)'; + } + echo '
Uploaded: '.$datestring.'

'; + if(substr($mimetype, 0, 6)!='image/' && ( substr($mimetype, 0, 5) != 'text/' || $mimetype == 'text/html' || $mimetype == 'text/javascript' )) + { + echo '
This file type may contain viruses or other code that could harm your computer. You should exercise caution if you download it.
'; + } + if(substr($mimetype, 0, 6)=='image/') + { + echo '

'.$paths->page.'

'; + } + echo '

Download this file'; + if(!$paths->page_protected && ( $paths->wiki_mode || $session->get_permissions('upload_new_version') )) + { + echo ' | Upload new version'; + } + echo '

'; + if($db->numrows() > 1) + { + echo '

File history

'; + while($r = $db->fetchrow()) + { + echo '(this ver) '; + if($session->get_permissions('history_rollback')) + echo ' (revert) '; + $mimetype = $r['mimetype']; + $datestring = date('F d, Y h:i a', (int)$r['time_id']); + echo $datestring.': '.$r['mimetype'].', '; + $fs = $r['size']; + $fs = (int)$fs; + if($fs >= 1048576) + { + $fs = round($fs / 1048576, 1); + echo ' '.$fs.' MB'; + } elseif($fs >= 1024) { + $fs = round($fs / 1024, 1); + echo ' '.$fs.' KB'; + } else { + echo ' '.$fs.' bytes'; + } + echo '
'; + } + echo '

'; + } + $db->free_result(); + echo '

'; +} + +function display_page_headers() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + if($session->get_permissions('vote_reset') && $paths->cpage['delvotes'] > 0) + { + $hr = implode(', ', explode('|', $paths->cpage['delvote_ips'])); + $is = 'is'; + $s = ''; + $s2 = 's'; + if ( $paths->cpage['delvotes'] > 1) + { + $is = 'are'; + $s = 's'; + $s2 = ''; + } + echo '
+ Notice: There '.$is.' '.$paths->cpage['delvotes'].' user'.$s.' that think'.$s2.' this page should be deleted.
+ Users that voted: ' . $hr . '
+ Delete page | Reset votes +
'; + } +} + +function display_page_footers() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + if(isset($_GET['nofooters'])) return; + $code = $plugins->setHook('send_page_footers'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + show_file_info(); + show_category_info(); +} + +function password_prompt($id = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$id) $id = $paths->page; + if(isset($paths->pages[$id]['password']) && strlen($paths->pages[$id]['password']) == 40 && !isset($_REQUEST['pagepass'])) + { + die_friendly('Password required', '

You must supply a password to access this page.

Password:

'); + } elseif(isset($_REQUEST['pagepass'])) { + $p = (preg_match('#^([a-f0-9]*){40}$#', $_REQUEST['pagepass'])) ? $_REQUEST['pagepass'] : sha1($_REQUEST['pagepass']); + if($p != $paths->pages[$id]['password']) die_friendly('Password required', '

The password you entered is incorrect.

Password:

'); + } +} + +function str_hex($string){ + $hex=''; + for ($i=0; $i < strlen($string); $i++){ + $hex .= ' '.dechex(ord($string[$i])); + } + return substr($hex, 1, strlen($hex)); +} + +// Function pulled from phpBB's smtp.php +function smtp_get_response($socket, $response, $line = __LINE__) +{ + $server_response = ''; + while (substr($server_response, 3, 1) != ' ') + { + if (!($server_response = fgets($socket, 256))) + { + die_friendly('SMTP Error', "

Couldn't get mail server response codes

"); + } + } + + if (!(substr($server_response, 0, 3) == $response)) + { + die_friendly('SMTP Error', "

Ran into problems sending mail. Response: $server_response

"); + } +} + +function smtp_send_email($to, $subject, $message, $from) +{ + return smtp_send_email_core($to, $subject, $message, "From: <$from>\n"); +} + +// Replacement or substitute for PHP's mail command +// Ported from phpBB - copyright (C) phpBB group, GPL. +function smtp_send_email_core($mail_to, $subject, $message, $headers = '') +{ + global $board_config; + + // Fix any bare linefeeds in the message to make it RFC821 Compliant. + $message = preg_replace("#(? 1) + { + $headers = join("\n", $headers); + } + else + { + $headers = $headers[0]; + } + } + $headers = chop($headers); + + // Make sure there are no bare linefeeds in the headers + $headers = preg_replace('#(?\r\n"); + smtp_get_response($socket, "250", __LINE__); + + // Specify each user to send to and build to header. + $to_header = ''; + + // Add an additional bit of error checking to the To field. + $mail_to = (trim($mail_to) == '') ? 'Undisclosed-recipients:;' : trim($mail_to); + if (preg_match('#[^ ]+\@[^ ]+#', $mail_to)) + { + enano_fputs($socket, "RCPT TO: <$mail_to>\r\n"); + smtp_get_response($socket, "250", __LINE__); + } + + // Ok now do the CC and BCC fields... + @reset($bcc); + while(list(, $bcc_address) = each($bcc)) + { + // Add an additional bit of error checking to bcc header... + $bcc_address = trim($bcc_address); + if (preg_match('#[^ ]+\@[^ ]+#', $bcc_address)) + { + enano_fputs($socket, "RCPT TO: <$bcc_address>\r\n"); + smtp_get_response($socket, "250", __LINE__); + } + } + + @reset($cc); + while(list(, $cc_address) = each($cc)) + { + // Add an additional bit of error checking to cc header + $cc_address = trim($cc_address); + if (preg_match('#[^ ]+\@[^ ]+#', $cc_address)) + { + enano_fputs($socket, "RCPT TO: <$cc_address>\r\n"); + smtp_get_response($socket, "250", __LINE__); + } + } + + // Ok now we tell the server we are ready to start sending data + enano_fputs($socket, "DATA\r\n"); + + // This is the last response code we look for until the end of the message. + smtp_get_response($socket, "354", __LINE__); + + // Send the Subject Line... + enano_fputs($socket, "Subject: $subject\r\n"); + + // Now the To Header. + enano_fputs($socket, "To: $mail_to\r\n"); + + // Now any custom headers.... + enano_fputs($socket, "$headers\r\n\r\n"); + + // Ok now we are ready for the message... + enano_fputs($socket, "$message\r\n"); + + // Ok the all the ingredients are mixed in let's cook this puppy... + enano_fputs($socket, ".\r\n"); + smtp_get_response($socket, "250", __LINE__); + + // Now tell the server we are done and close the socket... + enano_fputs($socket, "QUIT\r\n"); + fclose($socket); + + return TRUE; +} + +/** + * Tell which version of Enano we're running. + * @param bool $long if true, uses English version names (e.g. alpha, beta, release candidate). If false (default) uses abbreviations (1.0a1, 1.0b3, 1.0RC2, etc.) + * @return string + */ + +function enano_version($long = false, $no_nightly = false) +{ + $r = getConfig('enano_version'); + $rc = ( $long ) ? ' release candidate ' : 'RC'; + $b = ( $long ) ? ' beta ' : 'b'; + $a = ( $long ) ? ' alpha ' : 'a'; + if($v = getConfig('enano_rc_version')) $r .= $rc.$v; + if($v = getConfig('enano_beta_version')) $r .= $b.$v; + if($v = getConfig('enano_alpha_version')) $r .= $a.$v; + if ( defined('ENANO_NIGHTLY') && !$no_nightly ) + { + $nightlytag = ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR; + $nightlylong = ' nightly; build date: ' . ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR; + $r = ( $long ) ? $r . $nightlylong : $r . '-nightly-' . $nightlytag; + } + return $r; +} + +function _dualurlenc($t) { + return rawurlencode(rawurlencode($t)); +} + +function _die($t) { + $_ob = 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\'' . rawurlencode('' . $t . '') . '\')'; + die($_ob); +} + +function jsdie($text) { + global $db, $session, $paths, $template, $plugins; // Common objects + $text = rawurlencode($text . "\n\nSQL Backtrace:\n" . $db->sql_backtrace()); + echo 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\''.$text.'\');'; +} + +// HTML sanitizing function - written by Kallahar +// Original function at: http://quickwired.com/kallahar/smallprojects/php_xss_filter_function.php + +// UNUSED - todo: remove this in gold or put it to use + +function RemoveXSS($val) { + // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed + // this prevents some character re-spacing such as + // note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs + $val = preg_replace('/([\x00-\x08][\x0b-\x0c][\x0e-\x20])/', '', $val); + + // straight replacements, the user should never need these since they're normal characters + // this prevents like + $search = 'abcdefghijklmnopqrstuvwxyz'; + $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $search .= '1234567890!@#$%^&*()'; + $search .= '~`";:?+/={}[]-_|\'\\'; + for ($i = 0; $i < strlen($search); $i++) { + // ;? matches the ;, which is optional + // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars + + // @ @ search for the hex values + $val = preg_replace('/(&#[x|X]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ; + // @ @ 0{0,7} matches '0' zero to seven times + $val = preg_replace('/(�{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ; + } + + // now the only remaining whitespace attacks are \t, \n, and \r + $ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base'); + $ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload'); + $ra = array_merge($ra1, $ra2); + + $found = true; // keep replacing as long as the previous round replaced something + while ($found == true) { + $val_before = $val; + for ($i = 0; $i < sizeof($ra); $i++) { + $pattern = '/'; + for ($j = 0; $j < strlen($ra[$i]); $j++) { + if ($j > 0) { + $pattern .= '('; + $pattern .= '(&#[x|X]0{0,8}([9][a][b]);?)?'; + $pattern .= '|(�{0,8}([9][10][13]);?)?'; + $pattern .= ')?'; + } + $pattern .= $ra[$i][$j]; + } + $pattern .= '/i'; + $replacement = substr($ra[$i], 0, 2).''.substr($ra[$i], 2); // add in <> to nerf the tag + $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags + if ($val_before == $val) { + // no replacements were made, so exit the loop + $found = false; + } + } + } + return $val; +} + +/** + * Capitalizes the first letter of a string + * @param $text string the text to be transformed + * @return string + */ + +function capitalize_first_letter($text) +{ + return strtoupper(substr($text, 0, 1)) . substr($text, 1); +} + +/** + * Checks if a value in a bitfield is on or off + * @param $bitfield int the bit-field value + * @param $value int the value to switch off + * @return bool + */ + +function is_bit($bitfield, $value) +{ + return ( $bitfield & $value ) ? true : false; +} + +/** + * Trims spaces/newlines from the beginning and end of a string + * @param $text the text to process + * @return string + */ + +function trim_spaces($text) +{ + $d = true; + while($d) + { + $c = substr($text, 0, 1); + $a = substr($text, strlen($text)-1, strlen($text)); + if($c == "\n" || $c == "\r" || $c == "\t" || $c == ' ') $text = substr($text, 1, strlen($text)); + elseif($a == "\n" || $a == "\r" || $a == "\t" || $a == ' ') $text = substr($text, 0, strlen($text)-1); + else $d = false; + } + return $text; +} + +/** + * Enano-ese equivalent of str_split() which is only found in PHP5 + * @param $text string the text to split + * @param $inc int size of each block + * @return array + */ + +function enano_str_split($text, $inc = 1) +{ + if($inc < 1) return false; + if($inc >= strlen($text)) return Array($text); + $len = ceil(strlen($text) / $inc); + $ret = Array(); + for($i=0;$i'; + debug_print_backtrace(); + echo ''; + $c = ob_get_contents(); + ob_end_clean(); + if($return) return $c; + else echo $c; + return null; +} + +/** + * Like rawurlencode(), but encodes all characters + * @param string $text the text to encode + * @param optional string $prefix text before each hex character + * @param optional string $suffix text after each hex character + * @return string + */ + +function hexencode($text, $prefix = '%', $suffix = '') +{ + $arr = enano_str_split($text); + $r = ''; + foreach($arr as $a) + { + $nibble = (string)dechex(ord($a)); + if(strlen($nibble) == 1) $nibble = '0' . $nibble; + $r .= $prefix . $nibble . $suffix; + } + return $r; +} + +/** + * Enano-ese equivalent of get_magic_quotes_gpc() + * @return bool + */ + +function enano_get_magic_quotes_gpc() +{ + if(function_exists('get_magic_quotes_gpc')) + { + return ( get_magic_quotes_gpc() == 1 ); + } + else + { + return ( strtolower(@ini_get('magic_quotes_gpc')) == '1' ); + } +} + +/** + * Recursive stripslashes() + * @param array + * @return array + */ + +function stripslashes_recurse($arr) +{ + foreach($arr as $k => $xxxx) + { + $val =& $arr[$k]; + if(is_string($val)) + $val = stripslashes($val); + elseif(is_array($val)) + $val = stripslashes_recurse($val); + } + return $arr; +} + +/** + * If magic_quotes_gpc is on, calls stripslashes() on everything in $_GET/$_POST/$_COOKIE + * @ignore - this doesn't work + * @todo port version from the PHP manual + * @return void + */ +function strip_magic_quotes_gpc() +{ + if(enano_get_magic_quotes_gpc()) + { + $_POST = stripslashes_recurse($_POST); + $_GET = stripslashes_recurse($_GET); + $_COOKIE = stripslashes_recurse($_COOKIE); + } +} + +/** + * A very basic single-character compression algorithm for binary strings/bitfields + * @param string $bits the text to compress + * @return string + */ + +function compress_bitfield($bits) +{ + $crc32 = crc32($bits); + $bits .= '0'; + $start_pos = 0; + $current = substr($bits, 1, 1); + $last = substr($bits, 0, 1); + $chunk_size = 1; + $len = strlen($bits); + $crc = $len; + $crcval = 0; + for ( $i = 1; $i < $len; $i++ ) + { + $current = substr($bits, $i, 1); + $last = substr($bits, $i - 1, 1); + $next = substr($bits, $i + 1, 1); + // Are we on the last character? + if($current == $last && $i+1 < $len) + $chunk_size++; + else + { + if($i+1 == $len && $current == $next) + { + // This character completes a chunk + $chunk_size++; + $i++; + $chunk = substr($bits, $start_pos, $chunk_size); + $chunklen = strlen($chunk); + $newchunk = $last . '[' . $chunklen . ']'; + $newlen = strlen($newchunk); + $bits = substr($bits, 0, $start_pos) . $newchunk . substr($bits, $i, $len); + $chunk_size = 1; + $i = $start_pos + $newlen; + $start_pos = $i; + $len = strlen($bits); + $crcval = $crcval + $chunklen; + } + else + { + // Last character completed a chunk + $chunk = substr($bits, $start_pos, $chunk_size); + $chunklen = strlen($chunk); + $newchunk = $last . '[' . $chunklen . '],'; + $newlen = strlen($newchunk); + $bits = substr($bits, 0, $start_pos) . $newchunk . substr($bits, $i, $len); + $chunk_size = 1; + $i = $start_pos + $newlen; + $start_pos = $i; + $len = strlen($bits); + $crcval = $crcval + $chunklen; + } + } + } + if($crc != $crcval) + { + echo __FUNCTION__.'(): ERROR: length check failed, this is a bug in the algorithm
Debug info: aiming for a CRC val of '.$crc.', got '.$crcval; + return false; + } + $compressed = 'cbf:len='.$crc.';crc='.dechex($crc32).';data='.$bits.'|end'; + return $compressed; +} + +/** + * Uncompresses a bitfield compressed with compress_bitfield() + * @param string $bits the compressed bitfield + * @return string the uncompressed, original (we hope) bitfield OR bool false on error + */ + +function uncompress_bitfield($bits) +{ + if(substr($bits, 0, 4) != 'cbf:') + { + echo __FUNCTION__.'(): ERROR: Invalid stream'; + return false; + } + $len = intval(substr($bits, strpos($bits, 'len=')+4, strpos($bits, ';')-strpos($bits, 'len=')-4)); + $crc = substr($bits, strpos($bits, 'crc=')+4, 8); + $data = substr($bits, strpos($bits, 'data=')+5, strpos($bits, '|end')-strpos($bits, 'data=')-5); + $data = explode(',', $data); + foreach($data as $a => $b) + { + $d =& $data[$a]; + $char = substr($d, 0, 1); + $dlen = intval(substr($d, 2, strlen($d)-1)); + $s = ''; + for($i=0;$i<$dlen;$i++,$s.=$char); + $d = $s; + unset($s, $dlen, $char); + } + $decompressed = implode('', $data); + $decompressed = substr($decompressed, 0, -1); + $dcrc = (string)dechex(crc32($decompressed)); + if($dcrc != $crc) + { + echo __FUNCTION__.'(): ERROR: CRC check failed
debug info:
original crc: '.$crc.'
decomp\'ed crc: '.$dcrc.'
'; + return false; + } + return $decompressed; +} + +/** + * Exports a MySQL table into a SQL string. + * @param string $table The name of the table to export + * @param bool $structure If true, include a CREATE TABLE command + * @param bool $data If true, include the contents of the table + * @param bool $compact If true, omits newlines between parts of SQL statements, use in Enano database exporter + * @return string + */ + +function export_table($table, $structure = true, $data = true, $compact = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $struct_keys = ''; + $divider = (!$compact) ? "\n" : "\n"; + $spacer1 = (!$compact) ? "\n" : " "; + $spacer2 = (!$compact) ? " " : " "; + $rowspacer = (!$compact) ? "\n " : " "; + $index_list = Array(); + $cols = $db->sql_query('SHOW COLUMNS IN '.$table.';'); + if(!$cols) + { + echo 'export_table(): Error getting column list: '.$db->get_error_text().'
'; + return false; + } + $col = Array(); + $sqlcol = Array(); + $collist = Array(); + $pri_keys = Array(); + // Using fetchrow_num() here to compensate for MySQL l10n + while( $row = $db->fetchrow_num() ) + { + $field =& $row[0]; + $type =& $row[1]; + $null =& $row[2]; + $key =& $row[3]; + $def =& $row[4]; + $extra =& $row[5]; + $col[] = Array( + 'name'=>$field, + 'type'=>$type, + 'null'=>$null, + 'key'=>$key, + 'default'=>$def, + 'extra'=>$extra, + ); + $collist[] = $field; + } + + if ( $structure ) + { + $db->sql_query('SET SQL_QUOTE_SHOW_CREATE = 0;'); + $struct = $db->sql_query('SHOW CREATE TABLE '.$table.';'); + if ( !$struct ) + $db->_die(); + $row = $db->fetchrow_num(); + $db->free_result(); + $struct = $row[1]; + $struct = preg_replace("/\n\) ENGINE=(.+)$/", "\n);", $struct); + unset($row); + if ( $compact ) + { + $struct_arr = explode("\n", $struct); + foreach ( $struct_arr as $i => $leg ) + { + if ( $i == 0 ) + continue; + $test = trim($leg); + if ( empty($test) ) + { + unset($struct_arr[$i]); + continue; + } + $struct_arr[$i] = preg_replace('/^([\s]*)/', ' ', $leg); + } + $struct = implode("", $struct_arr); + } + } + + // Structuring complete + if($data) + { + $datq = $db->sql_query('SELECT * FROM '.$table.';'); + if(!$datq) + { + echo 'export_table(): Error getting column list: '.$db->get_error_text().'
'; + return false; + } + if($db->numrows() < 1) + { + if($structure) return $struct; + else return ''; + } + $rowdata = Array(); + $dataqs = Array(); + $insert_strings = Array(); + $z = false; + while($row = $db->fetchrow_num()) + { + $z = false; + foreach($row as $i => $cell) + { + $str = mysql_encode_column($cell, $col[$i]['type']); + $rowdata[] = $str; + } + $dataqs2 = implode(",$rowspacer", $dataqs) . ",$rowspacer" . '( ' . implode(', ', $rowdata) . ' )'; + $ins = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . $dataqs2 . ";"; + if ( strlen( $ins ) > MYSQL_MAX_PACKET_SIZE ) + { + // We've exceeded the maximum allowed packet size for MySQL - separate this into a different query + $insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";; + $dataqs = Array('( ' . implode(', ', $rowdata) . ' )'); + $z = true; + } + else + { + $dataqs[] = '( ' . implode(', ', $rowdata) . ' )'; + } + $rowdata = Array(); + } + if ( !$z ) + { + $insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";; + $dataqs = Array(); + } + $datstring = implode($divider, $insert_strings); + } + if($structure && !$data) return $struct; + elseif(!$structure && $data) return $datstring; + elseif($structure && $data) return $struct . $divider . $datstring; + elseif(!$structure && !$data) return ''; +} + +/** + * Encodes a string value for use in an INSERT statement for given column type $type. + * @access private + */ + +function mysql_encode_column($input, $type) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + // Decide whether to quote the string or not + if(substr($type, 0, 7) == 'varchar' || $type == 'datetime' || $type == 'text' || $type == 'tinytext' || $type == 'smalltext' || $type == 'longtext' || substr($type, 0, 4) == 'char') + { + $str = "'" . $db->escape($input) . "'"; + } + elseif(in_array($type, Array('blob', 'longblob', 'mediumblob', 'smallblob')) || substr($type, 0, 6) == 'binary' || substr($type, 0, 9) == 'varbinary') + { + $str = '0x' . hexencode($input, '', ''); + } + elseif(is_null($input)) + { + $str = 'NULL'; + } + else + { + $str = (string)$input; + } + return $str; +} + +/** + * Creates an associative array defining which file extensions are allowed and which ones aren't + * @return array keyname will be a file extension, value will be true or false + */ + +function fetch_allowed_extensions() +{ + global $mime_types; + $bits = getConfig('allowed_mime_types'); + if(!$bits) return Array(false); + $bits = uncompress_bitfield($bits); + if(!$bits) return Array(false); + $bits = enano_str_split($bits, 1); + $ret = Array(); + $mt = array_keys($mime_types); + foreach($bits as $i => $b) + { + $ret[$mt[$i]] = ( $b == '1' ) ? true : false; + } + return $ret; +} + +/** + * Generates a random key suitable for encryption + * @param int $len the length of the key + * @return string a BINARY key + */ + +function randkey($len = 32) +{ + $key = ''; + for($i=0;$i<$len;$i++) + { + $key .= chr(mt_rand(0, 255)); + } + return $key; +} + +/** + * Decodes a hex string. + * @param string $hex The hex code to decode + * @return string + */ + +function hexdecode($hex) +{ + $hex = enano_str_split($hex, 2); + $bin_key = ''; + foreach($hex as $nibble) + { + $byte = chr(hexdec($nibble)); + $bin_key .= $byte; + } + return $bin_key; +} + +/** + * Enano's own (almost) bulletproof HTML sanitizer. + * @param string $html The input HTML + * @return string cleaned HTML + */ + +function sanitize_html($html, $filter_php = true) +{ + + $html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>(.*?)#is', '<\\1\\2\\3javascript:\\59>\\60</\\1>', $html); + $html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>#is', '<\\1\\2\\3javascript:\\59>', $html); + + if($filter_php) + $html = str_replace( + Array('', '%>'), + Array('<?php', '<?', '<%', '?>', '%>'), + $html); + + $tag_whitelist = array_keys ( setupAttributeWhitelist() ); + if ( !$filter_php ) + $tag_whitelist[] = '?php'; + $len = strlen($html); + $in_quote = false; + $quote_char = ''; + $tag_start = 0; + $tag_name = ''; + $in_tag = false; + $trk_name = false; + for ( $i = 0; $i < $len; $i++ ) + { + $chr = $html{$i}; + $prev = ( $i == 0 ) ? '' : $html{ $i - 1 }; + $next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 }; + if ( $in_quote && $in_tag ) + { + if ( $quote_char == $chr && $prev != '\\' ) + $in_quote = false; + } + elseif ( ( $chr == '"' || $chr == "'" ) && $prev != '\\' && $in_tag ) + { + $in_quote = true; + $quote_char = $chr; + } + if ( $chr == '<' && !$in_tag && $next != '/' ) + { + // start of a tag + $tag_start = $i; + $in_tag = true; + $trk_name = true; + } + elseif ( !$in_quote && $in_tag && $chr == '>' ) + { + $full_tag = substr($html, $tag_start, ( $i - $tag_start ) + 1 ); + $l = strlen($tag_name) + 2; + $attribs_only = trim( substr($full_tag, $l, ( strlen($full_tag) - $l - 1 ) ) ); + + // Debugging message + // echo htmlspecialchars($full_tag) . '
'; + + if ( !in_array($tag_name, $tag_whitelist) ) + { + // Illegal tag + //echo $tag_name . ' '; + + $s = ( empty($attribs_only) ) ? '' : ' '; + + $sanitized = '<' . $tag_name . $s . $attribs_only . '>'; + + $html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1); + $html = str_replace('', '</' . $tag_name . '>', $html); + $new_i = $tag_start + strlen($sanitized); + + $len = strlen($html); + $i = $new_i; + + $in_tag = false; + $tag_name = ''; + continue; + } + else + { + if ( $tag_name == '?php' && !$filter_php ) + continue; + $f = fixTagAttributes( $attribs_only, $tag_name ); + $s = ( empty($f) ) ? '' : ' '; + + $sanitized = '<' . $tag_name . $f . '>'; + $new_i = $tag_start + strlen($sanitized); + + $html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1); + $len = strlen($html); + $i = $new_i; + + $in_tag = false; + $tag_name = ''; + continue; + } + } + elseif ( $in_tag && $trk_name ) + { + $is_alphabetical = ( strtolower($chr) != strtoupper($chr) || in_array($chr, array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) || $chr == '?' ); + if ( $is_alphabetical ) + $tag_name .= $chr; + else + { + $trk_name = false; + } + } + + } + + return $html; + +} + +function htmlalternatives($string) +{ + $ret = ''; + for ( $i = 0; $i < strlen($string); $i++ ) + { + $chr = $string{$i}; + $ch1 = ord($chr); + $ch2 = dechex($ch1); + $byte = '(&\\#([0]*){0,7}' . $ch1 . ';|\\\\([0]*){0,7}' . $ch1 . ';|\\\\([0]*){0,7}' . $ch2 . ';|&\\#x([0]*){0,7}' . $ch2 . ';|%([0]*){0,7}' . $ch2 . '|' . preg_quote($chr) . ')'; + $ret .= $byte; + $ret .= '([\s]){0,2}'; + } + return $ret; +} + +/** + * Paginates (breaks into multiple pages) a MySQL result resource, which is treated as unbuffered. + * @param resource The MySQL result resource. This should preferably be an unbuffered query. + * @param string A template, with variables being named after the column name + * @param int The number of total results. This should be determined by a second query. + * @param string sprintf-style formatting string for URLs for result pages. First parameter will be start offset. + * @param int Optional. Start offset in individual results. Defaults to 0. + * @param int Optional. The number of results per page. Defualts to 10. + * @param int Optional. An associative array of functions to call, with key names being column names, and values being function names. Values can also be an array with key 0 being either an object or a string(class name) and key 1 being a [static] method. + * @param string Optional. The text to be sent before the result list, only if there are any results. Possibly the start of a table. + * @param string Optional. The text to be sent after the result list, only if there are any results. Possibly the end of a table. + * @return string + */ + +function paginate($q, $tpl_text, $num_results, $result_url, $start = 0, $perpage = 10, $callers = Array(), $header = '', $footer = '') +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $parser = $template->makeParserText($tpl_text); + $num_pages = ceil ( $num_results / $perpage ); + $out = ''; + $i = 0; + $this_page = ceil ( $start / $perpage ); + + // Build paginator + $begin = '
+ + '; + $block = ''; + $end = '
Page:{LINK}
'; + $blk = $template->makeParserText($block); + $inner = ''; + $cls = 'row2'; + if ( $num_pages < 5 ) + { + for ( $i = 0; $i < $num_pages; $i++ ) + { + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($i * $perpage); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "$j" : "$j"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + } + else + { + if ( $this_page + 5 > $num_pages ) + { + $list = Array(); + $tp = $this_page; + if ( $this_page + 0 == $num_pages ) $tp = $tp - 3; + if ( $this_page + 1 == $num_pages ) $tp = $tp - 2; + if ( $this_page + 2 == $num_pages ) $tp = $tp - 1; + for ( $i = $tp - 1; $i <= $tp + 1; $i++ ) + { + $list[] = $i; + } + } + else + { + $list = Array(); + $current = $this_page; + $lower = ( $current < 3 ) ? 1 : $current - 1; + for ( $i = 0; $i < 3; $i++ ) + { + $list[] = $lower + $i; + } + } + $url = sprintf($result_url, '0'); + $link = ( 0 == $start ) ? "First" : "« First"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + + // if ( !in_array(1, $list) ) + // { + // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); + // $inner .= $blk->run(); + // } + + foreach ( $list as $i ) + { + if ( $i == $num_pages ) + break; + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($i * $perpage); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "$j" : "$j"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + $total = $num_pages * $perpage - $perpage; + + if ( $this_page < $num_pages ) + { + // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); + // $inner .= $blk->run(); + + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($total); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "Last" : "Last »"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + } + + $inner .= '↓'; + + $paginator = "\n$begin$inner$end\n"; + $out .= $paginator; + + $cls = 'row2'; + + if ( $row = $db->fetchrow($q) ) + { + $i = 0; + $out .= $header; + do { + $i++; + if ( $i <= $start ) + { + continue; + } + if ( ( $i - $start ) > $perpage ) + { + break; + } + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + foreach ( $row as $j => $val ) + { + if ( isset($callers[$j]) ) + { + $tmp = ( is_callable($callers[$j]) ) ? @call_user_func($callers[$j], $val, $row) : $v; + + if ( $tmp ) + { + $row[$j] = $tmp; + } + } + } + $parser->assign_vars($row); + $parser->assign_vars(array('_css_class' => $cls)); + $out .= $parser->run(); + } while ( $row = $db->fetchrow($q) ); + $out .= $footer; + } + + $out .= $paginator; + + return $out; +} + +/** + * This is the same as paginate(), but it processes an array instead of a MySQL result resource. + * @param array The results. Each value is simply echoed. + * @param int The number of total results. This should be determined by a second query. + * @param string sprintf-style formatting string for URLs for result pages. First parameter will be start offset. + * @param int Optional. Start offset in individual results. Defaults to 0. + * @param int Optional. The number of results per page. Defualts to 10. + * @param string Optional. The text to be sent before the result list, only if there are any results. Possibly the start of a table. + * @param string Optional. The text to be sent after the result list, only if there are any results. Possibly the end of a table. + * @return string + */ + +function paginate_array($q, $num_results, $result_url, $start = 0, $perpage = 10, $header = '', $footer = '') +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $parser = $template->makeParserText($tpl_text); + $num_pages = ceil ( $num_results / $perpage ); + $out = ''; + $i = 0; + $this_page = ceil ( $start / $perpage ); + + // Build paginator + $begin = '
+ + '; + $block = ''; + $end = '
Page:{LINK}
'; + $blk = $template->makeParserText($block); + $inner = ''; + $cls = 'row2'; + if ( $start > 0 ) + { + $url = sprintf($result_url, abs($start - $perpage)); + $link = "« Prev"; + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + if ( $num_pages < 5 ) + { + for ( $i = 0; $i < $num_pages; $i++ ) + { + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($i * $perpage); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "$j" : "$j"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + } + else + { + if ( $this_page + 5 > $num_pages ) + { + $list = Array(); + $tp = $this_page; + if ( $this_page + 0 == $num_pages ) $tp = $tp - 3; + if ( $this_page + 1 == $num_pages ) $tp = $tp - 2; + if ( $this_page + 2 == $num_pages ) $tp = $tp - 1; + for ( $i = $tp - 1; $i <= $tp + 1; $i++ ) + { + $list[] = $i; + } + } + else + { + $list = Array(); + $current = $this_page; + $lower = ( $current < 3 ) ? 1 : $current - 1; + for ( $i = 0; $i < 3; $i++ ) + { + $list[] = $lower + $i; + } + } + $url = sprintf($result_url, '0'); + $link = ( 0 == $start ) ? "First" : "« First"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + + // if ( !in_array(1, $list) ) + // { + // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); + // $inner .= $blk->run(); + // } + + foreach ( $list as $i ) + { + if ( $i == $num_pages ) + break; + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($i * $perpage); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "$j" : "$j"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + $total = $num_pages * $perpage - $perpage; + + if ( $this_page < $num_pages ) + { + // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); + // $inner .= $blk->run(); + + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($total); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "Last" : "Last »"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + } + + if ( $start < $total ) + { + $url = sprintf($result_url, abs($start + $perpage)); + $link = "Next »"; + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + $inner .= '↓'; + + $paginator = "\n$begin$inner$end\n"; + if ( $total > 1 ) + $out .= $paginator; + + $cls = 'row2'; + + if ( sizeof($q) > 0 ) + { + $i = 0; + $out .= $header; + foreach ( $q as $val ) { + $i++; + if ( $i <= $start ) + { + continue; + } + if ( ( $i - $start ) > $perpage ) + { + break; + } + $out .= $val; + } + $out .= $footer; + } + + if ( $total > 1 ) + $out .= $paginator; + + return $out; +} + +/** + * Enano version of fputs for debugging + */ + +function enano_fputs($socket, $data) +{ + // echo '
' . htmlspecialchars($data) . '
'; + // flush(); + // ob_flush(); + // ob_end_flush(); + return fputs($socket, $data); +} + +/** + * Sanitizes a page URL string so that it can safely be stored in the database. + * @param string Page ID to sanitize + * @return string Cleaned text + */ + +function sanitize_page_id($page_id) +{ + + // First, replace spaces with underscores + $page_id = str_replace(' ', '_', $page_id); + + preg_match_all('/\.[A-Fa-f0-9][A-Fa-f0-9]/', $page_id, $matches); + + foreach ( $matches[0] as $id => $char ) + { + $char = substr($char, 1); + $char = strtolower($char); + $char = intval(hexdec($char)); + $char = chr($char); + $page_id = str_replace($matches[0][$id], $char, $page_id); + } + + $pid_clean = preg_replace('/[\w\/:;\(\)@\[\]_-]/', 'X', $page_id); + $pid_dirty = enano_str_split($pid_clean, 1); + + foreach ( $pid_dirty as $id => $char ) + { + if ( $char == 'X' ) + continue; + $cid = ord($char); + $cid = dechex($cid); + $cid = strval($cid); + if ( strlen($cid) < 2 ) + { + $cid = strtoupper("0$cid"); + } + $pid_dirty[$id] = ".$cid"; + } + + $pid_chars = enano_str_split($page_id, 1); + $page_id_cleaned = ''; + + foreach ( $pid_chars as $id => $char ) + { + if ( $pid_dirty[$id] == 'X' ) + $page_id_cleaned .= $char; + else + $page_id_cleaned .= $pid_dirty[$id]; + } + + global $mime_types; + + $exts = array_keys($mime_types); + $exts = '(' . implode('|', $exts) . ')'; + + $page_id_cleaned = preg_replace('/\.2e' . $exts . '$/', '.\\1', $page_id_cleaned); + + return $page_id_cleaned; +} + +/** + * Inserts commas into a number to make it more human-readable. Floating point-safe. + * @param int The number to process + * @return string Input number with commas added + */ + +function commatize($num) +{ + $num = (string)$num; + if ( strpos($num, '.') ) + { + $whole = explode('.', $num); + $num = $whole[0]; + $dec = $whole[1]; + } + else + { + $whole = $num; + } + $offset = ( strlen($num) ) % 3; + $len = strlen($num); + $offset = ( $offset == 0 ) + ? 3 + : $offset; + for ( $i = $offset; $i < $len; $i=$i+3 ) + { + $num = substr($num, 0, $i) . ',' . substr($num, $i, $len); + $len = strlen($num); + $i++; + } + if ( isset($dec) ) + { + return $num . '.' . $dec; + } + else + { + return $num; + } +} + +//die('
Original:  01010101010100101010100101010101011010'."\nProcessed: ".uncompress_bitfield(compress_bitfield('01010101010100101010100101010101011010')).'
'); + +?> diff -r 902822492a68 -r fe660c52c48f includes/functions.php~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/functions.php~ Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1863 @@ +escape($v); + $e=$db->sql_query('DELETE FROM '.table_prefix.'config WHERE config_name=\''.$n.'\';'); + if(!$e) $db->_die('Error during generic setConfig() call row deletion.'); + $e=$db->sql_query('INSERT INTO '.table_prefix.'config(config_name, config_value) VALUES(\''.$n.'\', \''.$v.'\')'); + if(!$e) $db->_die('Error during generic setConfig() call row insertion.'); +} + +function makeUrl($t, $query = false, $escape = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $flags = ''; + $sep = urlSeparator; + if(isset($_GET['printable'])) { $flags .= $sep.'printable'; $sep = '&'; } + if(isset($_GET['theme'])) { $flags .= $sep.'theme='.$session->theme; $sep = '&'; } + if(isset($_GET['style'])) { $flags .= $sep.'style='.$session->style; $sep = '&'; } + $url = $session->append_sid(contentPath.$t.$flags); + if($query) + { + $sep = strstr($url, '?') ? '&' : '?'; + $url = $url . $sep . $query; + } + return ($escape) ? htmlspecialchars($url) : $url; +} + +function makeUrlNS($n, $t, $query = false, $escape = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $flags = ''; + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) $sep = urlSeparator; + else $sep = (strstr($_SERVER['REQUEST_URI'], '?')) ? '&' : '?'; + if(isset($_GET['printable'])) { $flags .= $sep.'printable'; $sep = '&'; } + if(isset($_GET['theme'])) { $flags .= $sep.'theme='.$session->theme; $sep = '&'; } + if(isset($_GET['style'])) { $flags .= $sep.'style='.$session->style; $sep = '&'; } + + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) + { + $url = contentPath.$paths->nslist[$n].$t.$flags; + } + else + { + $url = contentPath.$n.':'.$t.$flags; + } + + if($query) + { + if(strstr($url, '?')) $sep = '&'; + else $sep = '?'; + $url = $url . $sep . $query . $flags; + } + + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) + { + $url = $session->append_sid($url); + } + + return ($escape) ? htmlspecialchars($url) : $url; +} + +function makeUrlComplete($n, $t, $query = false, $escape = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $flags = ''; + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) $sep = urlSeparator; + else $sep = (strstr($_SERVER['REQUEST_URI'], '?')) ? '&' : '?'; + if(isset($_GET['printable'])) { $flags .= $sep.'printable'; $sep = '&'; } + if(isset($_GET['theme'])) { $flags .= $sep.'theme='.$session->theme; $sep = '&'; } + if(isset($_GET['style'])) { $flags .= $sep.'style='.$session->style; $sep = '&'; } + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) $url = $session->append_sid(contentPath.$paths->nslist[$n].$t.$flags); + else $url = contentPath.$n.':'.$t.$flags; + if($query) + { + if(strstr($url, '?')) $sep = '&'; + else $sep = '?'; + $url = $url . $sep . $query . $flags; + } + $baseprot = 'http' . ( isset($_SERVER['HTTPS']) ? 's' : '' ) . '://' . $_SERVER['HTTP_HOST']; + $url = $baseprot . $url; + return ($escape) ? htmlspecialchars($url) : $url; +} + +/** + * Redirect the user to the specified URL. + * @param string $url The URL, either relative or absolute. + * @param string $title The title of the message + * @param string $message A short message to show to the user + * @param string $timeout Timeout, in seconds, to delay the redirect. Defaults to 3. + */ + +function redirect($url, $title = 'Redirecting...', $message = 'Please wait while you are redirected.', $timeout = 3) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( $timeout == 0 ) + { + header('Location: ' . $url); + header('HTTP/1.1 307 Temporary Redirect'); + } + + $template->add_header(''); + $template->add_header(' + '); + + $template->tpl_strings['PAGE_NAME'] = $title; + $template->header(true); + echo '

' . $message . '

If you are not redirected within ' . ( $timeout + 1 ) . ' seconds, please click here.

'; + $template->footer(true); + + $db->close(); + exit(0); + +} + +// Removed wikiFormat() from here, replaced with RenderMan::render + +function isPage($p) { + global $db, $session, $paths, $template, $plugins; // Common objects + if(isset($paths->pages[$p])) return true; + $d = RenderMan::strToPageID($p); + if($d[1] != 'Special' && $d[1] != 'Template' && $d[1] != 'Admin') return false; + $a = explode('/', $p); + if(isset($paths->pages[$a[0]])) return true; + else return false; +} + +function arrayItemUp($arr, $keyname) { + $keylist = array_keys($arr); + $keyflop = array_flip($keylist); + $idx = $keyflop[$keyname]; + $idxm = $idx - 1; + $temp = $arr[$keylist[$idxm]]; + if($arr[$keylist[0]] == $arr[$keyname]) return $arr; + $arr[$keylist[$idxm]] = $arr[$keylist[$idx]]; + $arr[$keylist[$idx]] = $temp; + return $arr; +} + +function arrayItemDown($arr, $keyname) { + $keylist = array_keys($arr); + $keyflop = array_flip($keylist); + $idx = $keyflop[$keyname]; + $idxm = $idx + 1; + $temp = $arr[$keylist[$idxm]]; + $sz = sizeof($arr); $sz--; + if($arr[$keylist[$sz]] == $arr[$keyname]) return $arr; + $arr[$keylist[$idxm]] = $arr[$keylist[$idx]]; + $arr[$keylist[$idx]] = $temp; + return $arr; +} + +function arrayItemTop($arr, $keyname) { + $keylist = array_keys($arr); + $keyflop = array_flip($keylist); + $idx = $keyflop[$keyname]; + while( $orig != $arr[$keylist[0]] ) { + // echo 'Keyname: '.$keylist[$idx] . '
'; flush(); ob_flush(); // Debugger + if($idx < 0) return $arr; + if($keylist[$idx] == '' || $keylist[$idx] < 0 || !$keylist[$idx]) { + /* echo 'Infinite loop caught in arrayItemTop(
';
+      print_r($arr);
+      echo '

, '.$keyname.');

EnanoCMS: Critical error during function call, exiting to prevent excessive server load.'; + exit; */ + return $arr; + } + $arr = arrayItemUp($arr, $keylist[$idx]); + $idx--; + } + return $arr; +} + +function arrayItemBottom($arr, $keyname) { + $keylist = array_keys($arr); + $keyflop = array_flip($keylist); + $idx = $keyflop[$keyname]; + $sz = sizeof($arr); $sz--; + while( $orig != $arr[$keylist[$sz]] ) { + // echo 'Keyname: '.$keylist[$idx] . '
'; flush(); ob_flush(); // Debugger + if($idx > $sz) return $arr; + if($keylist[$idx] == '' || $keylist[$idx] < 0 || !$keylist[$idx]) { + echo 'Infinite loop caught in arrayItemBottom(
';
+      print_r($arr);
+      echo '

, '.$keyname.');

EnanoCMS: Critical error during function call, exiting to prevent excessive server load.'; + exit; + } + $arr = arrayItemDown($arr, $keylist[$idx]); + $idx++; + } + return $arr; +} + +// Convert IP address to hex string +// Input: 127.0.0.1 (string) +// Output: 0x7f000001 (string) +// Updated 12/8/06 to work with PHP4 and not use eval() (blech) +function ip2hex($ip) { + if ( preg_match('/^([0-9a-f:]+)$/', $ip) ) + { + // this is an ipv6 address + return str_replace(':', '', $ip); + } + $nums = explode('.', $ip); + if(sizeof($nums) != 4) return false; + $str = '0x'; + foreach($nums as $n) + { + $str .= (string)dechex($n); + } + return $str; +} + +// Convert DWord to IP address +// Input: 0x7f000001 +// Output: 127.0.0.1 +// Updated 12/8/06 to work with PHP4 and not use eval() (blech) +function hex2ip($in) { + if(substr($in, 0, 2) == '0x') $ip = substr($in, 2, 8); + else $ip = substr($in, 0, 8); + $octets = enano_str_split($ip, 2); + $str = ''; + $newoct = Array(); + foreach($octets as $o) + { + $o = (int)hexdec($o); + $newoct[] = $o; + } + return implode('.', $newoct); +} + +// Function strip_php moved to RenderMan class + +function die_semicritical($t, $p) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $db->close(); + + if ( ob_get_status() ) + ob_end_clean(); + + dc_here('functions: calling die_semicritical'); + + $tpl = new template_nodb(); + $tpl->load_theme('oxygen', 'bleu'); + $tpl->tpl_strings['SITE_NAME'] = getConfig('site_name'); + $tpl->tpl_strings['SITE_DESC'] = getConfig('site_desc'); + $tpl->tpl_strings['COPYRIGHT'] = getConfig('copyright_notice'); + $tpl->tpl_strings['PAGE_NAME'] = $t; + $tpl->header(); + echo $p; + $tpl->footer(); + + exit; +} + +function die_friendly($t, $p) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( ob_get_status() ) + ob_end_clean(); + + dc_here('functions: calling die_friendly'); + $paths->cpage['name'] = $t; + $template->tpl_strings['PAGE_NAME'] = $t; + $template->header(); + echo $p; + $template->footer(); + $db->close(); + + exit; +} + +function grinding_halt($t, $p) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + $db->close(); + + if ( ob_get_status() ) + ob_end_clean(); + + dc_here('functions: calling grinding_halt'); + $tpl = new template_nodb(); + $tpl->load_theme('oxygen', 'bleu'); + $tpl->tpl_strings['SITE_NAME'] = 'Critical error'; + $tpl->tpl_strings['SITE_DESC'] = 'This website is experiencing a serious error and cannot load.'; + $tpl->tpl_strings['COPYRIGHT'] = 'Unable to retrieve copyright information'; + $tpl->tpl_strings['PAGE_NAME'] = $t; + $tpl->header(); + echo $p; + $tpl->footer(); + exit; +} + +function show_category_info() { + global $db, $session, $paths, $template, $plugins; // Common objects + dc_here('functions: showing category info'); + if($template->no_headers && !strpos($_SERVER['REQUEST_URI'], 'ajax.php')) return ''; + if($paths->namespace=='Category') + { + $q = $db->sql_query('SELECT page_id,namespace FROM '.table_prefix.'categories WHERE category_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\'Category\' ORDER BY page_id;'); + if(!$q) $db->_die('The category information could not be selected.'); + $ticker = -1; + echo '

Subcategories

'; + if($db->numrows() < 1) echo '

There are no subcategories in this category.

'; + echo ''; + while($row = $db->fetchrow()) + { + $ticker++;if($ticker==3) $ticker=0; + if($ticker==0) echo ''; + echo ''; + if($ticker==2) echo ''; + } + $db->free_result(); + if($ticker) echo ''; + echo '
'.$paths->pages[$paths->nslist[$row['namespace']].$row['page_id']]['name'].'
'; + + $q = $db->sql_query('SELECT page_id,namespace FROM '.table_prefix.'categories WHERE category_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace!=\'Category\' ORDER BY page_id;'); + if(!$q) $db->_die('The category information could not be selected.'); + $ticker = -1; + echo '

Pages

'; + if($db->numrows() < 1) echo '

There are no pages in this category.

'; + echo ''; + while($row = $db->fetchrow()) + { + $ticker++;if($ticker==3) $ticker=0; + if($ticker==0) echo ''; + echo ''; + if($ticker==2) echo ''; + } + $db->free_result(); + if($ticker) echo ''; + echo '
'.$paths->pages[$paths->nslist[$row['namespace']].$row['page_id']]['name'].'


'; + } + $q = $db->sql_query('SELECT category_id FROM '.table_prefix.'categories WHERE page_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\''.$paths->namespace.'\''); + if(!$q) $db->_die('The error seems to have occurred during selection of category data.'); + if($db->numrows() > 0) { + echo '
Categories: '; + $i=0; + while($r = $db->fetchrow()) + { + if($i>0) echo ', '; + $i++; + echo ''.$paths->pages[$paths->nslist['Category'].$r['category_id']]['name'].''; + } + if( ( $paths->wiki_mode && !$paths->page_protected ) || ( $session->get_permissions('edit_cat') && $session->get_permissions('even_when_protected') ) ) echo ' [ edit categorization ]
'; + } else { + echo '
Categories: '; + echo '(Uncategorized)'; + if( ( $paths->wiki_mode && !$paths->page_protected ) || ( $session->get_permissions('edit_cat') && $session->get_permissions('even_when_protected') ) ) echo ' [ edit categorization ]
'; + else echo '
'; + } + $db->free_result(); +} + +function show_file_info() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + if($paths->namespace != 'File') return null; // Prevent unnecessary work + $selfn = $paths->cpage['urlname_nons']; // substr($paths->page, strlen($paths->nslist['File']), strlen($paths->cpage)); + if(substr($paths->cpage['name'], 0, strlen($paths->nslist['File']))==$paths->nslist['File']) $selfn = substr($paths->cpage['urlname_nons'], strlen($paths->nslist['File']), strlen($paths->cpage['urlname_nons'])); + $q = $db->sql_query('SELECT mimetype,time_id,size FROM '.table_prefix.'files WHERE page_id=\''.$selfn.'\' ORDER BY time_id DESC;'); + if(!$q) $db->_die('The file type could not be fetched.'); + if($db->numrows() < 1) { echo '

Uploaded file

There are no files uploaded with this name yet. Upload a file...


'; return; } + $r = $db->fetchrow(); + $mimetype = $r['mimetype']; + $datestring = date('F d, Y h:i a', (int)$r['time_id']); + echo '

Uploaded file

Type: '.$r['mimetype'].'
Size: '; + $fs = $r['size']; + echo $fs.' bytes'; + $fs = (int)$fs; + if($fs >= 1048576) + { + $fs = round($fs / 1048576, 1); + echo ' ('.$fs.' MB)'; + } elseif($fs >= 1024) { + $fs = round($fs / 1024, 1); + echo ' ('.$fs.' KB)'; + } + echo '
Uploaded: '.$datestring.'

'; + if(substr($mimetype, 0, 6)!='image/' && ( substr($mimetype, 0, 5) != 'text/' || $mimetype == 'text/html' || $mimetype == 'text/javascript' )) + { + echo '
This file type may contain viruses or other code that could harm your computer. You should exercise caution if you download it.
'; + } + if(substr($mimetype, 0, 6)=='image/') + { + echo '

'.$paths->page.'

'; + } + echo '

Download this file'; + if(!$paths->page_protected && ( $paths->wiki_mode || $session->get_permissions('upload_new_version') )) + { + echo ' | Upload new version'; + } + echo '

'; + if($db->numrows() > 1) + { + echo '

File history

'; + while($r = $db->fetchrow()) + { + echo '(this ver) '; + if($session->get_permissions('history_rollback')) + echo ' (revert) '; + $mimetype = $r['mimetype']; + $datestring = date('F d, Y h:i a', (int)$r['time_id']); + echo $datestring.': '.$r['mimetype'].', '; + $fs = $r['size']; + $fs = (int)$fs; + if($fs >= 1048576) + { + $fs = round($fs / 1048576, 1); + echo ' '.$fs.' MB'; + } elseif($fs >= 1024) { + $fs = round($fs / 1024, 1); + echo ' '.$fs.' KB'; + } else { + echo ' '.$fs.' bytes'; + } + echo '
'; + } + echo '

'; + } + $db->free_result(); + echo '

'; +} + +function display_page_headers() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + if($session->get_permissions('vote_reset') && $paths->cpage['delvotes'] > 0) + { + $hr = implode(', ', explode('|', $paths->cpage['delvote_ips'])); + $is = 'is'; + $s = ''; + $s2 = 's'; + if ( $paths->cpage['delvotes'] > 1) + { + $is = 'are'; + $s = 's'; + $s2 = ''; + } + echo '
+ Notice: There '.$is.' '.$paths->cpage['delvotes'].' user'.$s.' that think'.$s2.' this page should be deleted.
+ Users that voted: ' . $hr . '
+ Delete page | Reset votes +
'; + } +} + +function display_page_footers() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + if(isset($_GET['nofooters'])) return; + $code = $plugins->setHook('send_page_footers'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + show_file_info(); + show_category_info(); +} + +function password_prompt($id = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$id) $id = $paths->page; + if(isset($paths->pages[$id]['password']) && strlen($paths->pages[$id]['password']) == 40 && !isset($_REQUEST['pagepass'])) + { + die_friendly('Password required', '

You must supply a password to access this page.

Password:

'); + } elseif(isset($_REQUEST['pagepass'])) { + $p = (preg_match('#^([a-f0-9]*){40}$#', $_REQUEST['pagepass'])) ? $_REQUEST['pagepass'] : sha1($_REQUEST['pagepass']); + if($p != $paths->pages[$id]['password']) die_friendly('Password required', '

The password you entered is incorrect.

Password:

'); + } +} + +function str_hex($string){ + $hex=''; + for ($i=0; $i < strlen($string); $i++){ + $hex .= ' '.dechex(ord($string[$i])); + } + return substr($hex, 1, strlen($hex)); +} + +// Function pulled from phpBB's smtp.php +function smtp_get_response($socket, $response, $line = __LINE__) +{ + $server_response = ''; + while (substr($server_response, 3, 1) != ' ') + { + if (!($server_response = fgets($socket, 256))) + { + die_friendly('SMTP Error', "

Couldn't get mail server response codes

"); + } + } + + if (!(substr($server_response, 0, 3) == $response)) + { + die_friendly('SMTP Error', "

Ran into problems sending mail. Response: $server_response

"); + } +} + +function smtp_send_email($to, $subject, $message, $from) +{ + return smtp_send_email_core($to, $subject, $message, "From: <$from>\n"); +} + +// Replacement or substitute for PHP's mail command +// Ported from phpBB - copyright (C) phpBB group, GPL. +function smtp_send_email_core($mail_to, $subject, $message, $headers = '') +{ + global $board_config; + + // Fix any bare linefeeds in the message to make it RFC821 Compliant. + $message = preg_replace("#(? 1) + { + $headers = join("\n", $headers); + } + else + { + $headers = $headers[0]; + } + } + $headers = chop($headers); + + // Make sure there are no bare linefeeds in the headers + $headers = preg_replace('#(?\r\n"); + smtp_get_response($socket, "250", __LINE__); + + // Specify each user to send to and build to header. + $to_header = ''; + + // Add an additional bit of error checking to the To field. + $mail_to = (trim($mail_to) == '') ? 'Undisclosed-recipients:;' : trim($mail_to); + if (preg_match('#[^ ]+\@[^ ]+#', $mail_to)) + { + enano_fputs($socket, "RCPT TO: <$mail_to>\r\n"); + smtp_get_response($socket, "250", __LINE__); + } + + // Ok now do the CC and BCC fields... + @reset($bcc); + while(list(, $bcc_address) = each($bcc)) + { + // Add an additional bit of error checking to bcc header... + $bcc_address = trim($bcc_address); + if (preg_match('#[^ ]+\@[^ ]+#', $bcc_address)) + { + enano_fputs($socket, "RCPT TO: <$bcc_address>\r\n"); + smtp_get_response($socket, "250", __LINE__); + } + } + + @reset($cc); + while(list(, $cc_address) = each($cc)) + { + // Add an additional bit of error checking to cc header + $cc_address = trim($cc_address); + if (preg_match('#[^ ]+\@[^ ]+#', $cc_address)) + { + enano_fputs($socket, "RCPT TO: <$cc_address>\r\n"); + smtp_get_response($socket, "250", __LINE__); + } + } + + // Ok now we tell the server we are ready to start sending data + enano_fputs($socket, "DATA\r\n"); + + // This is the last response code we look for until the end of the message. + smtp_get_response($socket, "354", __LINE__); + + // Send the Subject Line... + enano_fputs($socket, "Subject: $subject\r\n"); + + // Now the To Header. + enano_fputs($socket, "To: $mail_to\r\n"); + + // Now any custom headers.... + enano_fputs($socket, "$headers\r\n\r\n"); + + // Ok now we are ready for the message... + enano_fputs($socket, "$message\r\n"); + + // Ok the all the ingredients are mixed in let's cook this puppy... + enano_fputs($socket, ".\r\n"); + smtp_get_response($socket, "250", __LINE__); + + // Now tell the server we are done and close the socket... + enano_fputs($socket, "QUIT\r\n"); + fclose($socket); + + return TRUE; +} + +/** + * Tell which version of Enano we're running. + * @param bool $long if true, uses English version names (e.g. alpha, beta, release candidate). If false (default) uses abbreviations (1.0a1, 1.0b3, 1.0RC2, etc.) + * @return string + */ + +function enano_version($long = false, $no_nightly = false) +{ + $r = getConfig('enano_version'); + $rc = ( $long ) ? ' release candidate ' : 'RC'; + $b = ( $long ) ? ' beta ' : 'b'; + $a = ( $long ) ? ' alpha ' : 'a'; + if($v = getConfig('enano_rc_version')) $r .= $rc.$v; + if($v = getConfig('enano_beta_version')) $r .= $b.$v; + if($v = getConfig('enano_alpha_version')) $r .= $a.$v; + if ( defined('ENANO_NIGHTLY') && !$no_nightly ) + { + $nightlytag = ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR; + $nightlylong = ' nightly; build date: ' . ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR; + $r = ( $long ) ? $r . $nightlylong : $r . '-nightly-' . $nightlytag; + } + return $r; +} + +function _dualurlenc($t) { + return rawurlencode(rawurlencode($t)); +} + +function _die($t) { + $_ob = 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\'' . rawurlencode('' . $t . '') . '\')'; + die($_ob); +} + +function jsdie($text) { + global $db, $session, $paths, $template, $plugins; // Common objects + $text = rawurlencode($text . "\n\nSQL Backtrace:\n" . $db->sql_backtrace()); + echo 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\''.$text.'\');'; +} + +// HTML sanitizing function - written by Kallahar +// Original function at: http://quickwired.com/kallahar/smallprojects/php_xss_filter_function.php + +// UNUSED - todo: remove this in gold or put it to use + +function RemoveXSS($val) { + // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed + // this prevents some character re-spacing such as + // note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs + $val = preg_replace('/([\x00-\x08][\x0b-\x0c][\x0e-\x20])/', '', $val); + + // straight replacements, the user should never need these since they're normal characters + // this prevents like + $search = 'abcdefghijklmnopqrstuvwxyz'; + $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $search .= '1234567890!@#$%^&*()'; + $search .= '~`";:?+/={}[]-_|\'\\'; + for ($i = 0; $i < strlen($search); $i++) { + // ;? matches the ;, which is optional + // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars + + // @ @ search for the hex values + $val = preg_replace('/(&#[x|X]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ; + // @ @ 0{0,7} matches '0' zero to seven times + $val = preg_replace('/(�{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ; + } + + // now the only remaining whitespace attacks are \t, \n, and \r + $ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base'); + $ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload'); + $ra = array_merge($ra1, $ra2); + + $found = true; // keep replacing as long as the previous round replaced something + while ($found == true) { + $val_before = $val; + for ($i = 0; $i < sizeof($ra); $i++) { + $pattern = '/'; + for ($j = 0; $j < strlen($ra[$i]); $j++) { + if ($j > 0) { + $pattern .= '('; + $pattern .= '(&#[x|X]0{0,8}([9][a][b]);?)?'; + $pattern .= '|(�{0,8}([9][10][13]);?)?'; + $pattern .= ')?'; + } + $pattern .= $ra[$i][$j]; + } + $pattern .= '/i'; + $replacement = substr($ra[$i], 0, 2).''.substr($ra[$i], 2); // add in <> to nerf the tag + $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags + if ($val_before == $val) { + // no replacements were made, so exit the loop + $found = false; + } + } + } + return $val; +} + +/** + * Capitalizes the first letter of a string + * @param $text string the text to be transformed + * @return string + */ + +function capitalize_first_letter($text) +{ + return strtoupper(substr($text, 0, 1)) . substr($text, 1); +} + +/** + * Checks if a value in a bitfield is on or off + * @param $bitfield int the bit-field value + * @param $value int the value to switch off + * @return bool + */ + +function is_bit($bitfield, $value) +{ + return ( $bitfield & $value ) ? true : false; +} + +/** + * Trims spaces/newlines from the beginning and end of a string + * @param $text the text to process + * @return string + */ + +function trim_spaces($text) +{ + $d = true; + while($d) + { + $c = substr($text, 0, 1); + $a = substr($text, strlen($text)-1, strlen($text)); + if($c == "\n" || $c == "\r" || $c == "\t" || $c == ' ') $text = substr($text, 1, strlen($text)); + elseif($a == "\n" || $a == "\r" || $a == "\t" || $a == ' ') $text = substr($text, 0, strlen($text)-1); + else $d = false; + } + return $text; +} + +/** + * Enano-ese equivalent of str_split() which is only found in PHP5 + * @param $text string the text to split + * @param $inc int size of each block + * @return array + */ + +function enano_str_split($text, $inc = 1) +{ + if($inc < 1) return false; + if($inc >= strlen($text)) return Array($text); + $len = ceil(strlen($text) / $inc); + $ret = Array(); + for($i=0;$i'; + debug_print_backtrace(); + echo ''; + $c = ob_get_contents(); + ob_end_clean(); + if($return) return $c; + else echo $c; + return null; +} + +/** + * Like rawurlencode(), but encodes all characters + * @param string $text the text to encode + * @param optional string $prefix text before each hex character + * @param optional string $suffix text after each hex character + * @return string + */ + +function hexencode($text, $prefix = '%', $suffix = '') +{ + $arr = enano_str_split($text); + $r = ''; + foreach($arr as $a) + { + $nibble = (string)dechex(ord($a)); + if(strlen($nibble) == 1) $nibble = '0' . $nibble; + $r .= $prefix . $nibble . $suffix; + } + return $r; +} + +/** + * Enano-ese equivalent of get_magic_quotes_gpc() + * @return bool + */ + +function enano_get_magic_quotes_gpc() +{ + if(function_exists('get_magic_quotes_gpc')) + { + return ( get_magic_quotes_gpc() == 1 ); + } + else + { + return ( strtolower(@ini_get('magic_quotes_gpc')) == '1' ); + } +} + +/** + * Recursive stripslashes() + * @param array + * @return array + */ + +function stripslashes_recurse($arr) +{ + foreach($arr as $k => $xxxx) + { + $val =& $arr[$k]; + if(is_string($val)) + $val = stripslashes($val); + elseif(is_array($val)) + $val = stripslashes_recurse($val); + } + return $arr; +} + +/** + * If magic_quotes_gpc is on, calls stripslashes() on everything in $_GET/$_POST/$_COOKIE + * @ignore - this doesn't work + * @todo port version from the PHP manual + * @return void + */ +function strip_magic_quotes_gpc() +{ + if(enano_get_magic_quotes_gpc()) + { + $_POST = stripslashes_recurse($_POST); + $_GET = stripslashes_recurse($_GET); + $_COOKIE = stripslashes_recurse($_COOKIE); + } +} + +/** + * A very basic single-character compression algorithm for binary strings/bitfields + * @param string $bits the text to compress + * @return string + */ + +function compress_bitfield($bits) +{ + $crc32 = crc32($bits); + $bits .= '0'; + $start_pos = 0; + $current = substr($bits, 1, 1); + $last = substr($bits, 0, 1); + $chunk_size = 1; + $len = strlen($bits); + $crc = $len; + $crcval = 0; + for ( $i = 1; $i < $len; $i++ ) + { + $current = substr($bits, $i, 1); + $last = substr($bits, $i - 1, 1); + $next = substr($bits, $i + 1, 1); + // Are we on the last character? + if($current == $last && $i+1 < $len) + $chunk_size++; + else + { + if($i+1 == $len && $current == $next) + { + // This character completes a chunk + $chunk_size++; + $i++; + $chunk = substr($bits, $start_pos, $chunk_size); + $chunklen = strlen($chunk); + $newchunk = $last . '[' . $chunklen . ']'; + $newlen = strlen($newchunk); + $bits = substr($bits, 0, $start_pos) . $newchunk . substr($bits, $i, $len); + $chunk_size = 1; + $i = $start_pos + $newlen; + $start_pos = $i; + $len = strlen($bits); + $crcval = $crcval + $chunklen; + } + else + { + // Last character completed a chunk + $chunk = substr($bits, $start_pos, $chunk_size); + $chunklen = strlen($chunk); + $newchunk = $last . '[' . $chunklen . '],'; + $newlen = strlen($newchunk); + $bits = substr($bits, 0, $start_pos) . $newchunk . substr($bits, $i, $len); + $chunk_size = 1; + $i = $start_pos + $newlen; + $start_pos = $i; + $len = strlen($bits); + $crcval = $crcval + $chunklen; + } + } + } + if($crc != $crcval) + { + echo __FUNCTION__.'(): ERROR: length check failed, this is a bug in the algorithm
Debug info: aiming for a CRC val of '.$crc.', got '.$crcval; + return false; + } + $compressed = 'cbf:len='.$crc.';crc='.dechex($crc32).';data='.$bits.'|end'; + return $compressed; +} + +/** + * Uncompresses a bitfield compressed with compress_bitfield() + * @param string $bits the compressed bitfield + * @return string the uncompressed, original (we hope) bitfield OR bool false on error + */ + +function uncompress_bitfield($bits) +{ + if(substr($bits, 0, 4) != 'cbf:') + { + echo __FUNCTION__.'(): ERROR: Invalid stream'; + return false; + } + $len = intval(substr($bits, strpos($bits, 'len=')+4, strpos($bits, ';')-strpos($bits, 'len=')-4)); + $crc = substr($bits, strpos($bits, 'crc=')+4, 8); + $data = substr($bits, strpos($bits, 'data=')+5, strpos($bits, '|end')-strpos($bits, 'data=')-5); + $data = explode(',', $data); + foreach($data as $a => $b) + { + $d =& $data[$a]; + $char = substr($d, 0, 1); + $dlen = intval(substr($d, 2, strlen($d)-1)); + $s = ''; + for($i=0;$i<$dlen;$i++,$s.=$char); + $d = $s; + unset($s, $dlen, $char); + } + $decompressed = implode('', $data); + $decompressed = substr($decompressed, 0, -1); + $dcrc = (string)dechex(crc32($decompressed)); + if($dcrc != $crc) + { + echo __FUNCTION__.'(): ERROR: CRC check failed
debug info:
original crc: '.$crc.'
decomp\'ed crc: '.$dcrc.'
'; + return false; + } + return $decompressed; +} + +/** + * Exports a MySQL table into a SQL string. + * @param string $table The name of the table to export + * @param bool $structure If true, include a CREATE TABLE command + * @param bool $data If true, include the contents of the table + * @param bool $compact If true, omits newlines between parts of SQL statements, use in Enano database exporter + * @return string + */ + +function export_table($table, $structure = true, $data = true, $compact = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $struct_keys = ''; + $divider = (!$compact) ? "\n" : "\n"; + $spacer1 = (!$compact) ? "\n" : " "; + $spacer2 = (!$compact) ? " " : " "; + $rowspacer = (!$compact) ? "\n " : " "; + $index_list = Array(); + $cols = $db->sql_query('SHOW COLUMNS IN '.$table.';'); + if(!$cols) + { + echo 'export_table(): Error getting column list: '.$db->get_error_text().'
'; + return false; + } + $col = Array(); + $sqlcol = Array(); + $collist = Array(); + $pri_keys = Array(); + // Using fetchrow_num() here to compensate for MySQL l10n + while( $row = $db->fetchrow_num() ) + { + $field =& $row[0]; + $type =& $row[1]; + $null =& $row[2]; + $key =& $row[3]; + $def =& $row[4]; + $extra =& $row[5]; + $col[] = Array( + 'name'=>$field, + 'type'=>$type, + 'null'=>$null, + 'key'=>$key, + 'default'=>$def, + 'extra'=>$extra, + ); + $collist[] = $field; + } + + if ( $structure ) + { + $db->sql_query('SET SQL_QUOTE_SHOW_CREATE = 0;'); + $struct = $db->sql_query('SHOW CREATE TABLE '.$table.';'); + if ( !$struct ) + $db->_die(); + $row = $db->fetchrow_num(); + $db->free_result(); + $struct = $row[1]; + $struct = preg_replace("/\n\) ENGINE=(.+)$/", "\n);", $struct); + unset($row); + if ( $compact ) + { + $struct_arr = explode("\n", $struct); + foreach ( $struct_arr as $i => $leg ) + { + if ( $i == 0 ) + continue; + $test = trim($leg); + if ( empty($test) ) + { + unset($struct_arr[$i]); + continue; + } + $struct_arr[$i] = preg_replace('/^([\s]*)/', ' ', $leg); + } + $struct = implode("", $struct_arr); + } + } + + // Structuring complete + if($data) + { + $datq = $db->sql_query('SELECT * FROM '.$table.';'); + if(!$datq) + { + echo 'export_table(): Error getting column list: '.$db->get_error_text().'
'; + return false; + } + if($db->numrows() < 1) + { + if($structure) return $struct; + else return ''; + } + $rowdata = Array(); + $dataqs = Array(); + $insert_strings = Array(); + $z = false; + while($row = $db->fetchrow_num()) + { + $z = false; + foreach($row as $i => $cell) + { + $str = mysql_encode_column($cell, $col[$i]['type']); + $rowdata[] = $str; + } + $dataqs2 = implode(",$rowspacer", $dataqs) . ",$rowspacer" . '( ' . implode(', ', $rowdata) . ' )'; + $ins = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . $dataqs2 . ";"; + if ( strlen( $ins ) > MYSQL_MAX_PACKET_SIZE ) + { + // We've exceeded the maximum allowed packet size for MySQL - separate this into a different query + $insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";; + $dataqs = Array('( ' . implode(', ', $rowdata) . ' )'); + $z = true; + } + else + { + $dataqs[] = '( ' . implode(', ', $rowdata) . ' )'; + } + $rowdata = Array(); + } + if ( !$z ) + { + $insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";; + $dataqs = Array(); + } + $datstring = implode($divider, $insert_strings); + } + if($structure && !$data) return $struct; + elseif(!$structure && $data) return $datstring; + elseif($structure && $data) return $struct . $divider . $datstring; + elseif(!$structure && !$data) return ''; +} + +/** + * Encodes a string value for use in an INSERT statement for given column type $type. + * @access private + */ + +function mysql_encode_column($input, $type) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + // Decide whether to quote the string or not + if(substr($type, 0, 7) == 'varchar' || $type == 'datetime' || $type == 'text' || $type == 'tinytext' || $type == 'smalltext' || $type == 'longtext' || substr($type, 0, 4) == 'char') + { + $str = "'" . $db->escape($input) . "'"; + } + elseif(in_array($type, Array('blob', 'longblob', 'mediumblob', 'smallblob')) || substr($type, 0, 6) == 'binary' || substr($type, 0, 9) == 'varbinary') + { + $str = '0x' . hexencode($input, '', ''); + } + elseif(is_null($input)) + { + $str = 'NULL'; + } + else + { + $str = (string)$input; + } + return $str; +} + +/** + * Creates an associative array defining which file extensions are allowed and which ones aren't + * @return array keyname will be a file extension, value will be true or false + */ + +function fetch_allowed_extensions() +{ + global $mime_types; + $bits = getConfig('allowed_mime_types'); + if(!$bits) return Array(false); + $bits = uncompress_bitfield($bits); + if(!$bits) return Array(false); + $bits = enano_str_split($bits, 1); + $ret = Array(); + $mt = array_keys($mime_types); + foreach($bits as $i => $b) + { + $ret[$mt[$i]] = ( $b == '1' ) ? true : false; + } + return $ret; +} + +/** + * Generates a random key suitable for encryption + * @param int $len the length of the key + * @return string a BINARY key + */ + +function randkey($len = 32) +{ + $key = ''; + for($i=0;$i<$len;$i++) + { + $key .= chr(mt_rand(0, 255)); + } + return $key; +} + +/** + * Decodes a hex string. + * @param string $hex The hex code to decode + * @return string + */ + +function hexdecode($hex) +{ + $hex = enano_str_split($hex, 2); + $bin_key = ''; + foreach($hex as $nibble) + { + $byte = chr(hexdec($nibble)); + $bin_key .= $byte; + } + return $bin_key; +} + +/** + * Enano's own (almost) bulletproof HTML sanitizer. + * @param string $html The input HTML + * @return string cleaned HTML + */ + +function sanitize_html($html, $filter_php = true) +{ + + $html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>(.*?)#is', '<\\1\\2\\3javascript:\\59>\\60</\\1>', $html); + $html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>#is', '<\\1\\2\\3javascript:\\59>', $html); + + if($filter_php) + $html = str_replace( + Array('', '%>'), + Array('<?php', '<?', '<%', '?>', '%>'), + $html); + + $tag_whitelist = array_keys ( setupAttributeWhitelist() ); + if ( !$filter_php ) + $tag_whitelist[] = '?php'; + $len = strlen($html); + $in_quote = false; + $quote_char = ''; + $tag_start = 0; + $tag_name = ''; + $in_tag = false; + $trk_name = false; + for ( $i = 0; $i < $len; $i++ ) + { + $chr = $html{$i}; + $prev = ( $i == 0 ) ? '' : $html{ $i - 1 }; + $next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 }; + if ( $in_quote && $in_tag ) + { + if ( $quote_char == $chr && $prev != '\\' ) + $in_quote = false; + } + elseif ( ( $chr == '"' || $chr == "'" ) && $prev != '\\' && $in_tag ) + { + $in_quote = true; + $quote_char = $chr; + } + if ( $chr == '<' && !$in_tag && $next != '/' ) + { + // start of a tag + $tag_start = $i; + $in_tag = true; + $trk_name = true; + } + elseif ( !$in_quote && $in_tag && $chr == '>' ) + { + $full_tag = substr($html, $tag_start, ( $i - $tag_start ) + 1 ); + $l = strlen($tag_name) + 2; + $attribs_only = trim( substr($full_tag, $l, ( strlen($full_tag) - $l - 1 ) ) ); + + // Debugging message + // echo htmlspecialchars($full_tag) . '
'; + + if ( !in_array($tag_name, $tag_whitelist) ) + { + // Illegal tag + //echo $tag_name . ' '; + + $s = ( empty($attribs_only) ) ? '' : ' '; + + $sanitized = '<' . $tag_name . $s . $attribs_only . '>'; + + $html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1); + $html = str_replace('', '</' . $tag_name . '>', $html); + $new_i = $tag_start + strlen($sanitized); + + $len = strlen($html); + $i = $new_i; + + $in_tag = false; + $tag_name = ''; + continue; + } + else + { + if ( $tag_name == '?php' && !$filter_php ) + continue; + $f = fixTagAttributes( $attribs_only, $tag_name ); + $s = ( empty($f) ) ? '' : ' '; + + $sanitized = '<' . $tag_name . $f . '>'; + $new_i = $tag_start + strlen($sanitized); + + $html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1); + $len = strlen($html); + $i = $new_i; + + $in_tag = false; + $tag_name = ''; + continue; + } + } + elseif ( $in_tag && $trk_name ) + { + $is_alphabetical = ( strtolower($chr) != strtoupper($chr) || in_array($chr, array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) || $chr == '?' ); + if ( $is_alphabetical ) + $tag_name .= $chr; + else + { + $trk_name = false; + } + } + + } + + return $html; + +} + +function htmlalternatives($string) +{ + $ret = ''; + for ( $i = 0; $i < strlen($string); $i++ ) + { + $chr = $string{$i}; + $ch1 = ord($chr); + $ch2 = dechex($ch1); + $byte = '(&\\#([0]*){0,7}' . $ch1 . ';|\\\\([0]*){0,7}' . $ch1 . ';|\\\\([0]*){0,7}' . $ch2 . ';|&\\#x([0]*){0,7}' . $ch2 . ';|%([0]*){0,7}' . $ch2 . '|' . preg_quote($chr) . ')'; + $ret .= $byte; + $ret .= '([\s]){0,2}'; + } + return $ret; +} + +/** + * Paginates (breaks into multiple pages) a MySQL result resource, which is treated as unbuffered. + * @param resource The MySQL result resource. This should preferably be an unbuffered query. + * @param string A template, with variables being named after the column name + * @param int The number of total results. This should be determined by a second query. + * @param string sprintf-style formatting string for URLs for result pages. First parameter will be start offset. + * @param int Optional. Start offset in individual results. Defaults to 0. + * @param int Optional. The number of results per page. Defualts to 10. + * @param int Optional. An associative array of functions to call, with key names being column names, and values being function names. Values can also be an array with key 0 being either an object or a string(class name) and key 1 being a [static] method. + * @param string Optional. The text to be sent before the result list, only if there are any results. Possibly the start of a table. + * @param string Optional. The text to be sent after the result list, only if there are any results. Possibly the end of a table. + * @return string + */ + +function paginate($q, $tpl_text, $num_results, $result_url, $start = 0, $perpage = 10, $callers = Array(), $header = '', $footer = '') +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $parser = $template->makeParserText($tpl_text); + $num_pages = ceil ( $num_results / $perpage ); + $out = ''; + $i = 0; + $this_page = ceil ( $start / $perpage ); + + // Build paginator + $begin = '
+ + '; + $block = ''; + $end = '
Page:{LINK}
'; + $blk = $template->makeParserText($block); + $inner = ''; + $cls = 'row2'; + if ( $num_pages < 5 ) + { + for ( $i = 0; $i < $num_pages; $i++ ) + { + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($i * $perpage); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "$j" : "$j"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + } + else + { + if ( $this_page + 5 > $num_pages ) + { + $list = Array(); + $tp = $this_page; + if ( $this_page + 0 == $num_pages ) $tp = $tp - 3; + if ( $this_page + 1 == $num_pages ) $tp = $tp - 2; + if ( $this_page + 2 == $num_pages ) $tp = $tp - 1; + for ( $i = $tp - 1; $i <= $tp + 1; $i++ ) + { + $list[] = $i; + } + } + else + { + $list = Array(); + $current = $this_page; + $lower = ( $current < 3 ) ? 1 : $current - 1; + for ( $i = 0; $i < 3; $i++ ) + { + $list[] = $lower + $i; + } + } + $url = sprintf($result_url, '0'); + $link = ( 0 == $start ) ? "First" : "« First"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + + // if ( !in_array(1, $list) ) + // { + // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); + // $inner .= $blk->run(); + // } + + foreach ( $list as $i ) + { + if ( $i == $num_pages ) + break; + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($i * $perpage); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "$j" : "$j"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + $total = $num_pages * $perpage - $perpage; + + if ( $this_page < $num_pages ) + { + // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); + // $inner .= $blk->run(); + + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($total); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "Last" : "Last »"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + } + + $inner .= '↓'; + + $paginator = "\n$begin$inner$end\n"; + $out .= $paginator; + + $cls = 'row2'; + + if ( $row = $db->fetchrow($q) ) + { + $i = 0; + $out .= $header; + do { + $i++; + if ( $i <= $start ) + { + continue; + } + if ( ( $i - $start ) > $perpage ) + { + break; + } + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + foreach ( $row as $j => $val ) + { + if ( isset($callers[$j]) ) + { + $tmp = ( is_callable($callers[$j]) ) ? @call_user_func($callers[$j], $val, $row) : $v; + + if ( $tmp ) + { + $row[$j] = $tmp; + } + } + } + $parser->assign_vars($row); + $parser->assign_vars(array('_css_class' => $cls)); + $out .= $parser->run(); + } while ( $row = $db->fetchrow($q) ); + $out .= $footer; + } + + $out .= $paginator; + + return $out; +} + +/** + * This is the same as paginate(), but it processes an array instead of a MySQL result resource. + * @param array The results. Each value is simply echoed. + * @param int The number of total results. This should be determined by a second query. + * @param string sprintf-style formatting string for URLs for result pages. First parameter will be start offset. + * @param int Optional. Start offset in individual results. Defaults to 0. + * @param int Optional. The number of results per page. Defualts to 10. + * @param string Optional. The text to be sent before the result list, only if there are any results. Possibly the start of a table. + * @param string Optional. The text to be sent after the result list, only if there are any results. Possibly the end of a table. + * @return string + */ + +function paginate_array($q, $num_results, $result_url, $start = 0, $perpage = 10, $header = '', $footer = '') +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $parser = $template->makeParserText($tpl_text); + $num_pages = ceil ( $num_results / $perpage ); + $out = ''; + $i = 0; + $this_page = ceil ( $start / $perpage ); + + // Build paginator + $begin = '
+ + '; + $block = ''; + $end = '
Page:{LINK}
'; + $blk = $template->makeParserText($block); + $inner = ''; + $cls = 'row2'; + if ( $start > 0 ) + { + $url = sprintf($result_url, abs($start - $perpage)); + $link = "« Prev"; + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + if ( $num_pages < 5 ) + { + for ( $i = 0; $i < $num_pages; $i++ ) + { + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($i * $perpage); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "$j" : "$j"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + } + else + { + if ( $this_page + 5 > $num_pages ) + { + $list = Array(); + $tp = $this_page; + if ( $this_page + 0 == $num_pages ) $tp = $tp - 3; + if ( $this_page + 1 == $num_pages ) $tp = $tp - 2; + if ( $this_page + 2 == $num_pages ) $tp = $tp - 1; + for ( $i = $tp - 1; $i <= $tp + 1; $i++ ) + { + $list[] = $i; + } + } + else + { + $list = Array(); + $current = $this_page; + $lower = ( $current < 3 ) ? 1 : $current - 1; + for ( $i = 0; $i < 3; $i++ ) + { + $list[] = $lower + $i; + } + } + $url = sprintf($result_url, '0'); + $link = ( 0 == $start ) ? "First" : "« First"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + + // if ( !in_array(1, $list) ) + // { + // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); + // $inner .= $blk->run(); + // } + + foreach ( $list as $i ) + { + if ( $i == $num_pages ) + break; + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($i * $perpage); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "$j" : "$j"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + $total = $num_pages * $perpage - $perpage; + + if ( $this_page < $num_pages ) + { + // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); + // $inner .= $blk->run(); + + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($total); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "Last" : "Last »"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + } + + if ( $start < $total ) + { + $url = sprintf($result_url, abs($start + $perpage)); + $link = "Next »"; + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + $inner .= '↓'; + + $paginator = "\n$begin$inner$end\n"; + if ( $total > 1 ) + $out .= $paginator; + + $cls = 'row2'; + + if ( sizeof($q) > 0 ) + { + $i = 0; + $out .= $header; + foreach ( $q as $val ) { + $i++; + if ( $i <= $start ) + { + continue; + } + if ( ( $i - $start ) > $perpage ) + { + break; + } + $out .= $val; + } + $out .= $footer; + } + + if ( $total > 1 ) + $out .= $paginator; + + return $out; +} + +/** + * Enano version of fputs for debugging + */ + +function enano_fputs($socket, $data) +{ + // echo '
' . htmlspecialchars($data) . '
'; + // flush(); + // ob_flush(); + // ob_end_flush(); + return fputs($socket, $data); +} + +/** + * Sanitizes a page URL string so that it can safely be stored in the database. + * @param string Page ID to sanitize + * @return string Cleaned text + */ + +function sanitize_page_id($page_id) +{ + + // First, replace spaces with underscores + $page_id = str_replace(' ', '_', $page_id); + + preg_match_all('/\.[A-Fa-f0-9][A-Fa-f0-9]/', $page_id, $matches); + + foreach ( $matches[0] as $id => $char ) + { + $char = substr($char, 1); + $char = strtolower($char); + $char = intval(hexdec($char)); + $char = chr($char); + $page_id = str_replace($matches[0][$id], $char, $page_id); + } + + $pid_clean = preg_replace('/[\w\/:;\(\)@\[\]_-]/', 'X', $page_id); + $pid_dirty = enano_str_split($pid_clean, 1); + + foreach ( $pid_dirty as $id => $char ) + { + if ( $char == 'X' ) + continue; + $cid = ord($char); + $cid = dechex($cid); + $cid = strval($cid); + if ( strlen($cid) < 2 ) + { + $cid = strtoupper("0$cid"); + } + $pid_dirty[$id] = ".$cid"; + } + + $pid_chars = enano_str_split($page_id, 1); + $page_id_cleaned = ''; + + foreach ( $pid_chars as $id => $char ) + { + if ( $pid_dirty[$id] == 'X' ) + $page_id_cleaned .= $char; + else + $page_id_cleaned .= $pid_dirty[$id]; + } + + global $mime_types; + + $exts = array_keys($mime_types); + $exts = '(' . implode('|', $exts) . ')'; + + $page_id_cleaned = preg_replace('/\.2e' . $exts . '$/', '.\\1', $page_id_cleaned); + + return $page_id_cleaned; +} + +//die('
Original:  01010101010100101010100101010101011010'."\nProcessed: ".uncompress_bitfield(compress_bitfield('01010101010100101010100101010101011010')).'
'); + +?> diff -r 902822492a68 -r fe660c52c48f includes/graphs.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/graphs.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,937 @@ + 7, 'b' => 3, 'c' => 6, 'd' => 0, 'e' => 2); + /** + * GraphMaker::bar_padding + * Padding of bars + */ + var $bar_padding = 5; + /** + * GraphMaker::bar_bordercolor + * Border color of bars + */ + var $bar_bordercolor = array(39, 78, 120); + /** + * GraphMaker::bar_bgcolor + * Background color of bars + */ + var $bar_bgcolor = array(69, 129, 194); + //--------------------------------------------- + /** + * GraphMaker::graph_areaheight + * Height of graphic area + */ + var $graph_areaheight = 100; + /** + * GraphMaker::graph_padding + * Paddings of graph + */ + var $graph_padding = array('left' => 50, 'top' => 20, 'right' => 20, 'bottom' => 20); + /** + * GraphMaker::graph_title + * Title text of graph + */ + var $graph_title = ""; + /** + * GraphMaker::graph_bgcolor + * Background color of graph + */ + var $graph_bgcolor = array(255, 255, 255); + /** + * GraphMaker::graph_bgtransparent + * Boolean for background transparency + */ + var $graph_bgtransparent = 0; + /** + * GraphMaker::graph_transparencylevel + * Transparency level (0=opaque, 127=transparent) + */ + var $graph_transparencylevel = 0; + /** + * GraphMaker::graph_borderwidth + * Width of graph border + */ + var $graph_borderwidth = 1; + /** + * GraphMaker::graph_bordercolor + * Border color of graph + */ + var $graph_bordercolor = array(218, 218, 239); + /** + * GraphMaker::graph_titlecolor + * Color of title text of graph + */ + var $graph_titlecolor = array(99, 88, 78); + //--------------------------------------------- + /** + * GraphMaker::axis_step + * Scale step of axis + */ + var $axis_step = 2; + /** + * GraphMaker::axis_bordercolor + * Border color of axis + */ + var $axis_bordercolor = array(99, 88, 78); + /** + * GraphMaker::axis_bgcolor + * Background color of axis + */ + var $axis_bgcolor = array(152, 137, 124); + + /**************************************************************** + GRAPH + ****************************************************************/ + + /** + * GraphMaker::SetGraphAreaHeight() + * Sets graph height (not counting top and bottom margins) + **/ + function SetGraphAreaHeight($height) { + if ($height > 0) $this->graph_areaheight = $height; + } + + /** + * GraphMaker::SetGraphPadding() + * Sets graph padding (margins) + **/ + function SetGraphPadding($left, $top, $right, $bottom) { + $this->graph_padding = array('left' => (int) $left, + 'top' => (int) $top, + 'right' => (int) $right, + 'bottom' => (int) $bottom); + } + + /** + * GraphMaker::SetGraphTitle() + * Set title text + **/ + function SetGraphTitle($title) { + $this->graph_title = $title; + } + + /** + * GraphMaker::SetGraphBorderColor() + * Sets border color for graph + **/ + function SetGraphBorderColor($red, $green, $blue) { + $this->graph_bordercolor = array($red, $green, $blue); + } + + /** + * GraphMaker::SetGraphBorderWidth() + * Set width of border. 0 disables border + **/ + function SetGraphBorderWidth($width = 0) { + $this->graph_borderwidth = $width; + } + + /** + * GraphMaker::SetGraphBackgroundColor() + * Sets background color for graph + **/ + function SetGraphBackgroundColor($red, $green, $blue) { + $this->graph_bgcolor = array($red, $green, $blue); + } + + /** + * GraphMaker::SetGraphBackgroundTransparent() + * Sets background color for graph (and set it transparent) + **/ + function SetGraphBackgroundTransparent($red, $green, $blue, $addtransparency = 1) { + $this->graph_bgcolor = array($red, $green, $blue); + $this->graph_bgtransparent = ($addtransparency ? 1 : 0); + } + + /** + * GraphMaker::SetGraphTitleColor() + * Sets title color for graph + **/ + function SetGraphTitleColor($red, $green, $blue) { + $this->graph_titlecolor = array($red, $green, $blue); + } + + /** + * GraphMaker::SetGraphTransparency() + * Sets transparency for graph + **/ + function SetGraphTransparency($percent) { + if ($percent < 0) $percent = 0; + elseif ($percent > 100) $percent = 127; + else $percent = $percent * 1.27; + $this->graph_transparencylevel = $percent; + } + + /**************************************************************** + BAR + ****************************************************************/ + + /** + * GraphMaker::SetBarBorderColor() + * Sets border color for bars + **/ + function SetBarBorderColor($red, $green, $blue) { + $this->bar_bordercolor = array($red, $green, $blue); + } + + /** + * GraphMaker::SetBarBackgroundColor() + * Sets background color for bars + **/ + function SetBarBackgroundColor($red, $green, $blue) { + $this->bar_bgcolor = array($red, $green, $blue); + } + + /** + * GraphMaker::SetBarData() + * Sets data of graph (parameter should be an array with key + * being the name of the bar and the value the value of the bar. + **/ + function SetBarData($data) { + if (is_array($data)) $this->bar_data = $data; + } + + /** + * GraphMaker::SetBarDimensions() + * Sets with and height of each bar + **/ + function SetBarDimensions($width, $height) { + if ($width > 0) $this->bar_width = $width; + if ($height > 0) $this->bar_height = $height; + } + + /** + * GraphMaker::SetBarPadding() + * Sets padding (border) around each bar + **/ + function SetBarPadding($padding) { + if ($padding > 0) $this->bar_padding = $padding; + } + + /**************************************************************** + AXIS + ****************************************************************/ + + /** + * GraphMaker::SetAxisBorderColor() + * Sets border color for axis + **/ + function SetAxisBorderColor($red, $green, $blue) { + $this->axis_bordercolor = array($red, $green, $blue); + } + + /** + * GraphMaker::SetAxisBackgroundColor() + * Sets background color for axis + **/ + function SetAxisBackgroundColor($red, $green, $blue) { + $this->axis_bgcolor = array($red, $green, $blue); + } + + /** + * GraphMaker::SetAxisStep() + * Sets axis scale step + **/ + function SetAxisStep($step) { + if ($step > 0) $this->axis_step = $step; + } + + /** + * GraphMaker::GetFinalGraphDimensions() + * From the values already setted, it calculates image + * width and height + **/ + function GetFinalGraphDimensions() { + $w = $this->graph_padding['left'] + + (count($this->bar_data) * ($this->bar_width + ($this->bar_padding * 2))) + + $this->graph_padding['right']; + $h = $this->graph_padding['top'] + + $this->graph_areaheight + + $this->graph_padding['bottom']; + return array($w, $h); + } + + /** + * GraphMaker::LoadGraph() + * Loads definitions from a file + **/ + function LoadGraph($path) { + if (($fp = @fopen($path, "r")) !== false) { + $content = ""; + while (!feof($fp)) { // I do not use filesize() here + $content .= fread($fp, 4096); // because of remote files. If + } // there is no problem with them + fclose($fp); // please let me know + $this->__LoadGraphDefinitions($content); + return true; + } else return false; + } + + /** + * GraphMaker::DrawGraph() + * Draw all the graph: bg, axis, bars, text.. and output it + * Optional file parameter turns output to file, and bool on success + **/ + function DrawGraph($file = "") { + list($w, $h) = $this->GetFinalGraphDimensions(); + $this->graph_width = $w; + $this->graph_height = $h; + + $this->im = imagecreatetruecolor($w, $h); + if ($this->graph_transparencylevel) { + imagealphablending($this->im, true); + } + + $this->__PaintBackground(); + $this->__DrawAxis(); + + $p = 0; + foreach ($this->bar_data as $name => $value) { + $p++; + $this->__DrawBarText($p, $name); + $this->__DrawBar($p, $value); + } + + if (strlen($this->graph_title)) { + $this->__AllocateColor("im_graph_titlecolor", + $this->graph_titlecolor, + $this->graph_transparencylevel); + $this->__DrawText($this->graph_title, + floor($this->graph_width / 2), + $this->graph_borderwidth + 2, + $this->im_graph_titlecolor, + 2, + 1); + } + + if (strlen($file)) { + $ret = imagepng($this->im, $file); + } else { + header('Content-Type: image/png'); + imagepng($this->im); + $ret = true; + } + imagedestroy($this->im); + return $ret; + } + + /** + * GraphMaker::PaintBackground() + * Draw all the graph: bg, axis, bars, text.. and output it + * Optional file parameter turns output to file, and bool on success + **/ + function __PaintBackground() { + $this->__AllocateColor("im_graph_bgcolor", + $this->graph_bgcolor, + 0); + imagefilledrectangle($this->im, + 0, + 0, + $this->graph_width, + $this->graph_height, + $this->im_graph_bgcolor); + if ($this->graph_bgtransparent) { + imagecolortransparent($this->im, $this->im_graph_bgcolor); + } + if ($this->graph_borderwidth) { + $this->__AllocateColor("im_graph_bordercolor", + $this->graph_bordercolor, + $this->graph_transparencylevel); + for ($i = 0; $i < $this->graph_borderwidth; $i++) { + imagerectangle($this->im, + $i, + $i, + $this->graph_width - 1 - $i, + $this->graph_height - 1 - $i, + $this->im_graph_bordercolor); + } + } + } + + /** + * GraphMaker::__DrawAxis() + * Draws all the axis stuff (and scale steps) + **/ + function __DrawAxis() { + $this->__AllocateColor("im_axis_bordercolor", + $this->axis_bordercolor, + $this->graph_transparencylevel); + $this->__AllocateColor("im_axis_bgcolor", + $this->axis_bgcolor, + $this->graph_transparencylevel); + $this->__DrawPolygon($this->graph_padding['left'], $this->graph_height - $this->graph_padding['bottom'], + $this->graph_padding['left'], $this->graph_padding['top'], + $this->graph_padding['left'] + $this->bar_height - 1, $this->graph_padding['top'] - $this->bar_height + 1, + $this->graph_padding['left'] + $this->bar_height - 1, $this->graph_height - $this->graph_padding['bottom'] - $this->bar_height + 1, + $this->im_axis_bgcolor, true); + $this->__DrawPolygon($this->graph_padding['left'], $this->graph_height - $this->graph_padding['bottom'], + $this->graph_padding['left'], $this->graph_padding['top'], + $this->graph_padding['left'] + $this->bar_height - 1, $this->graph_padding['top'] - $this->bar_height + 1, + $this->graph_padding['left'] + $this->bar_height - 1, $this->graph_height - $this->graph_padding['bottom'] - $this->bar_height + 1, + $this->im_axis_bordercolor); + + $this->__DrawPolygon($this->graph_padding['left'], $this->graph_height - $this->graph_padding['bottom'], + $this->graph_padding['left'] + $this->bar_height - 1, $this->graph_height - $this->graph_padding['bottom'] - $this->bar_height + 1, + $this->graph_width - $this->graph_padding['right'] + $this->bar_height - 1, $this->graph_height - $this->graph_padding['bottom'] - $this->bar_height + 1, + $this->graph_width - $this->graph_padding['right'], $this->graph_height - $this->graph_padding['bottom'], + $this->im_axis_bgcolor, true); + $this->__DrawPolygon($this->graph_padding['left'], $this->graph_height - $this->graph_padding['bottom'], + $this->graph_padding['left'] + $this->bar_height - 1, $this->graph_height - $this->graph_padding['bottom'] - $this->bar_height + 1, + $this->graph_width - $this->graph_padding['right'] + $this->bar_height - 1, $this->graph_height - $this->graph_padding['bottom'] - $this->bar_height + 1, + $this->graph_width - $this->graph_padding['right'], $this->graph_height - $this->graph_padding['bottom'], + $this->im_axis_bordercolor); + + // draw lines that separate bars + $total_bars = count($this->bar_data); + for ($i = 1; $i < $total_bars; $i++) { + $offset = $this->graph_padding['left'] + + (($this->bar_width + ($this->bar_padding * 2)) * $i); + imageline($this->im, + $offset, + $this->graph_height - $this->graph_padding['bottom'], + $offset + $this->bar_height - 1, + $this->graph_height - $this->graph_padding['bottom'] - $this->bar_height + 1, + $this->im_axis_bordercolor); + } + + // draw scale steps + $max_value = $this->__GetMaxGraphValue(); + if (($max_value % 10) > 0) { + $max_value = $max_value + (10 - ($max_value % 10)); + } + $this->axis_max = $max_value; + $y = 0; + $style = array($this->im_axis_bordercolor, $this->im_graph_bgcolor); + imagesetstyle($this->im, $style); + while ($y <= $max_value) { + if ($max_value == 0) { $max_value=1; } // corrected by Marcelo Trenkenchu + $offset = floor($this->graph_height - $this->graph_padding['bottom'] - + ($y * $this->graph_areaheight / $max_value)); + imageline($this->im, + $this->graph_padding['left'], + $offset, + $this->graph_padding['left'] + $this->bar_height - 1, + $offset - $this->bar_height + 1, + $this->im_axis_bordercolor); + $this->__DrawText($y, + $this->graph_padding['left'], + $offset, + $this->im_axis_bordercolor, + 1, + 2, + 1); + // gridline + if ($y > 0) { + imageline($this->im, + $this->graph_padding['left'] + $this->bar_height, + $offset - $this->bar_height + 1, + $this->graph_width - $this->graph_padding['right'] + $this->bar_height - 1, + $offset - $this->bar_height + 1, + IMG_COLOR_STYLED); + } + $y += $this->axis_step; + } + + imageline($this->im, + $this->graph_width - $this->graph_padding['right'] + $this->bar_height - 1, + $this->graph_padding['top'] - $this->bar_height + 1, + $this->graph_width - $this->graph_padding['right'] + $this->bar_height - 1, + $this->graph_height - $this->graph_padding['bottom'] - $this->bar_height, + IMG_COLOR_STYLED); + } + + /** + * GraphMaker::__DrawText() + * Draws text on image with color, size and alignment options + **/ + function __DrawText($text, $x, $y, $color, $size = 1, $align = 0, $valign = 0) { + /* + * Align: 0=left | 1=center | 2=right + */ + if ($align == 1) $x -= floor(strlen($text) * imagefontwidth($size) / 2); + elseif ($align == 2) $x -= (strlen($text) * imagefontwidth($size)); + if ($valign == 1) $y -= floor(imagefontheight($size) / 2); + elseif ($valign == 2) $y -= imagefontheight($size); + imagestring($this->im, + $size, + $x, + $y, + $text, + $color); + } + + /** + * GraphMaker::__GetMaxGraphValue() + * Returns max bar value + **/ + function __GetMaxGraphValue() { + $max_value = 0; + foreach ($this->bar_data as $name => $value) { + if ($value > $max_value) $max_value = $value; + } + return $max_value; + } + + /** + * GraphMaker::__DrawBarText() + * Determines top and left to draw text to a choosen bar + **/ + function __DrawBarText($bar, $text) { + $this->__DrawText($text, + $this->graph_padding['left'] + (($this->bar_width + ($this->bar_padding * 2)) * ($bar - 0.5)), + $this->graph_height - $this->graph_padding['bottom'] + 1, + $this->axis_bordercolor, + 1, + 1); + } + + /** + * GraphMaker::__DrawBar() + * Draws a choosen bar with it's value + **/ + function __DrawBar($bar, $value) { + $x = $this->graph_padding['left'] + + (($this->bar_width + ($this->bar_padding * 2)) * ($bar - 1)) + + $this->bar_padding; + if ($this->axis_max == 0) { $this->axis_max = 1; } // corrected by Marcelo Trenkenchu + $y = $value * $this->graph_areaheight / $this->axis_max; + $this->____DrawBar($x, + $this->graph_height - $this->graph_padding['bottom'] - $y, + $x + $this->bar_width, + $this->graph_height - $this->graph_padding['bottom']); + } + + /** + * GraphMaker::____DrawBar() + * Draws the actual rectangles that form a bar + **/ + function ____DrawBar($x1, $y1, $x2, $y2) { + $this->__AllocateColor("im_bar_bordercolor", + $this->bar_bordercolor, + $this->graph_transparencylevel); + $this->__AllocateColor("im_bar_bgcolor", + $this->bar_bgcolor, + $this->graph_transparencylevel); + $this->__DrawPolygon($x1, $y1, + $x2, $y1, + $x2, $y2, + $x1, $y2, + $this->im_bar_bgcolor, true); + $this->__DrawPolygon($x1, $y1, + $x2, $y1, + $x2, $y2, + $x1, $y2, + $this->im_bar_bordercolor); + $this->__DrawPolygon($x1, $y1, + $x2, $y1, + $x2 + $this->bar_height - 1, $y1 - $this->bar_height + 1, + $x1 + $this->bar_height - 1, $y1 - $this->bar_height + 1, + $this->im_bar_bgcolor, true); + $this->__DrawPolygon($x1, $y1, + $x2, $y1, + $x2 + $this->bar_height - 1, $y1 - $this->bar_height + 1, + $x1 + $this->bar_height - 1, $y1 - $this->bar_height + 1, + $this->im_bar_bordercolor); + $this->__DrawPolygon($x2, $y2, + $x2, $y1, + $x2 + $this->bar_height - 1, $y1 - $this->bar_height + 1, + $x2 + $this->bar_height - 1, $y2 - $this->bar_height + 1, + $this->im_bar_bgcolor, true); + $this->__DrawPolygon($x2, $y2, + $x2, $y1, + $x2 + $this->bar_height - 1, $y1 - $this->bar_height + 1, + $x2 + $this->bar_height - 1, $y2 - $this->bar_height + 1, + $this->im_bar_bordercolor); + } + + /** + * GraphMaker::__DrawPolygon() + * Draws a (filled) (ir)regular polygon + **/ + function __DrawPolygon($x1, $y1, $x2, $y2, $x3, $y3, $x4, $y4, $color, $filled = false) { + if ($filled) { + imagefilledpolygon($this->im, array($x1, $y1, $x2, $y2, $x3, $y3, $x4, $y4), 4, $color); + } else { + imagepolygon($this->im, array($x1, $y1, $x2, $y2, $x3, $y3, $x4, $y4), 4, $color); + } + } + + /** + * GraphMaker::__LoadGraphDefinitions() + * Loads definitions to a graph from text lines (normaly + * they come from a file). This function is called by + * GraphMaker::LoadGraph() + **/ + function __LoadGraphDefinitions($text) { + $text = preg_split("/\r?\n/", $text); + $data = array(); + $section = ''; + for ($i = 0; $i < count($text); $i++) { + if (preg_match("/^\s*#/", $text[$i])) { + //ignore.. it's just a comment + } elseif (preg_match("/^\s*\}\s*/", $text[$i])) { + $section = ''; + } elseif (preg_match("/^\s*(\w+)\s*\{\s*$/", $text[$i], $r)) { + $section = $r[1]; + } else { + $p = strpos($text[$i], "="); + if ($p !== false) { + $data[$section][trim(substr($text[$i], 0, $p))] = trim(substr($text[$i], $p + 1)); + } + } + } + if (is_array($data['graph'])) { + $this->__LoadGraphValues($data['graph']); + } + if (is_array($data['bar'])) { + $this->__LoadBarValues($data['bar']); + } + if (is_array($data['axis'])) { + $this->__LoadAxisValues($data['axis']); + } + if (is_array($data['data'])) { + $this->bar_data = $data['data']; + } + } + + /** + * GraphMaker::__LoadGraphValues() + * Loads definitions to main graph settings + **/ + function __LoadGraphValues($data) { + foreach ($data as $name => $value) { + $name = strtolower($name); + switch ($name) { + case 'background-color': + $this->__SetColorToValue("graph_bgcolor", $value); + break; + case 'border-color': + $this->__SetColorToValue("graph_bordercolor", $value); + break; + case 'title-color': + $this->__SetColorToValue("graph_titlecolor", $value); + break; + case 'background-transparent': + $this->graph_bgtransparent = ($value == 1 || $value == 'yes' ? 1 : 0); + break; + case 'transparency': + $this->SetGraphTransparency(str_replace('%', '', $value)); + break; + case 'title': + $this->graph_title = $value; + break; + case 'border-width': + $this->graph_borderwidth = (int) $value; + break; + case 'area-height': + $this->graph_areaheight = (int) $value; + break; + default: + if (substr($name, 0, 8) == 'padding-' && strlen($name) > 8) { + $this->graph_padding[substr($name, 8)] = $value; + } + } + } + } + + /** + * GraphMaker::__LoadBarValues() + * Loads definitions to bar settings + **/ + function __LoadBarValues($data) { + foreach ($data as $name => $value) { + $name = strtolower($name); + switch ($name) { + case 'background-color': + $this->__SetColorToValue("bar_bgcolor", $value); + break; + case 'border-color': + $this->__SetColorToValue("bar_bordercolor", $value); + break; + case 'padding': + $this->bar_padding = $value; + break; + case 'width': + $this->bar_width = (int) $value; + break; + case 'height': + $this->bar_height = (int) $value; + break; + } + } + } + + /** + * GraphMaker::__LoadAxisValues() + * Loads definitions to axis settings + **/ + function __LoadAxisValues($data) { + foreach ($data as $name => $value) { + switch (strtolower($name)) { + case 'step': + $this->SetAxisStep($value); + break; + case 'background-color': + $this->__SetColorToValue("axis_bgcolor", $value); + break; + case 'border-color': + $this->__SetColorToValue("axis_bordercolor", $value); + } + } + } + + /** + * GraphMaker::__SetColorToValue() + * Sets a color (rgb or in html format) to a variable + **/ + function __SetColorToValue($varname, $color) { + if ($color[0] == "#") { // if it's hex (html format), change to rgb array + if (strlen($color) == 4) { + // if only 3 hex values (I assume it's a shade of grey: #ddd) + $color .= substr($color, -3); + } + $color = array(hexdec($color[1].$color[2]), + hexdec($color[3].$color[4]), + hexdec($color[5].$color[6])); + } + $this->$varname = $color; + } + + function __AllocateColor($varname, $color, $alpha) { + $this->$varname = imagecolorallocatealpha($this->im, + $color[0], + $color[1], + $color[2], + $alpha); + } +} + +// Graph Generator for PHP +// Originally located at http://szewo.com/php/graph, but link was broken, so this file was retrieved from: +// http://web.archive.org/web/20030130065944/szewo.com/php/graph/graph.class.php3.txt +// License unknown + +class GraphMaker_compat { + var $_values; + var $_ShowLabels; + var $_ShowCounts; + var $_ShowCountsMode; + + var $_BarWidth; + var $_GraphWidth; + var $_BarImg; + var $_BarBorderWidth; + var $_BarBorderColor; + var $_RowSortMode; + var $_TDClassHead; + var $_TDClassLabel; + var $_TDClassCount; + var $_GraphTitle; + + function __construct() { + $this->_values = array(); + $this->_ShowLabels = true; + $this->_BarWidth = 16; + $this->_GraphWidth = 360; + $this->_BarImg = "NULL"; + $this->_BarBorderWidth = 0; + $this->_BarBorderColor = "red"; + $this->_ShowCountsMode = 2; + $this->_RowSortMode = 1; + $this->_TDClassHead = "grphh"; + $this->_TDClassLabel = "grph"; + $this->_TDClassCount = "grphc"; + $this->_GraphTitle="Graph title"; + } + + function GraphMaker_compat() { + $this->__construct(); + } + + function SetBarBorderWidth($width) { + $this->_BarBorderWidth = $width; + } + function SetBorderColor($color) { + $this->_BarBorderColor = $color; + } + +// mode = 1 labels asc, 2 label desc + function SetSortMode($mode) { + switch ($mode) { + case 1: + asort($this->_values); + break; + case 2: + arsort($this->_values); + break; + default: + break; + } + + } + + function AddValue($labelName, $theValue) { + array_push($this->_values, array("label" => $labelName, "value" => $theValue)); + + } + function SetBarWidth($width) { + $this->_BarWidth = $width; + } + function SetBarImg($img) { + $this->_BarImg = $img; + } + function SetShowLabels($lables) { + $this->_ShowLabels = $labels; + } + function SetGraphWidth($width) { + $this->_GraphWidth = $width; + } + function SetGraphTitle($title) { + $this->_GraphTitle = $title; + } + //mode = percentage or counts + function SetShowCountsMode($mode) { + $this->_ShowCountsMode = $mode; + } + //mode = none(0) label(1) or count(2) + function SetRowSortMode($sortmode) { + $this->_RowSortMode = $sortmode; + } + + function SetTDClassHead($class) { + $this->_TDClassHead = $class; + } + function SetTDClassLabel($class) { + $this->_TDClassLabel = $class; + } + function SetTDClassCount($class) { + $this->_TDClassCount = $class; + } + function GetMaxVal() { + $maxval = 0; + foreach($this->_values as $value) if($maxval<$value["value"]) $maxval = $value["value"]; + return $maxval; + } + function BarGraphVert() { + $maxval = $this->GetMaxVal(); + foreach($this->_values as $value) $sumval += $value["value"]; + $this->SetSortMode($this->_RowSortMode); + echo ""; + if (strlen($this->_GraphTitle)>0) echo ""; + echo ""; + foreach($this->_values as $value) { + echo ""; + } + echo ""; + if ($this->_ShowCountsMode>0) { + echo ""; + foreach($this->_values as $value) { + switch ($this->_ShowCountsMode) { + case 1: + $count = round(100*$value["value"]/$sumval)."%"; + break; + case 2: + $count = $value["value"]; + break; /* Exit the switch and the while. */ + default: + break; + } + echo ""; + } + echo ""; + } + + if ($this->_ShowLabels) { + echo ""; + foreach($this->_values as $value) { + echo ""; + } + echo ""; + } + + echo "
_TDClassHead."\">".$this->_GraphTitle."
"; + $height = $this->_BarWidth; + $width=ceil($value["value"]*$this->_GraphWidth/$maxval); + echo "
_BarBorderWidth."px solid ".$this->_BarBorderColor."\""; + echo ">"; + echo "
$count
".$value["label"]."
"; + } + + + + function BarGraphHoriz() { + $maxval = $this->GetMaxVal(); + foreach($this->_values as $value) $sumval += $value["value"]; + $this->SetSortMode($this->_RowSortMode); + echo ""; + if (strlen($this->_GraphTitle)>0) { + echo ""; + } + foreach($this->_values as $value) { + if ($this->_ShowLabels) { + echo ""; + echo ""; + } + echo ""; + if ($this->_ShowCountsMode>0) { + switch ($this->_ShowCountsMode) { + case 1: + $count = round(100*$value["value"]/$sumval)."%"; + break; + case 2: + $count = $value["value"]; + break; /* Exit the switch and the while. */ + default: + break; + } + echo ""; + } + echo ""; + } + echo "
_ShowCountsMode>0) echo " colspan=2"; + echo " class=\"".$this->_TDClassHead."\">".$this->_GraphTitle."
".$value["label"]."
$count"; + $height = $this->_BarWidth; + $width=ceil($value["value"]*$this->_GraphWidth/$maxval); + echo "_BarImg."\" height=$height width=$width "; + echo " style=\"border: ".$this->_BarBorderWidth."px solid ".$this->_BarBorderColor."\""; + echo ">"; + echo "
"; + } +} + diff -r 902822492a68 -r fe660c52c48f includes/index.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/index.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,13 @@ +perms['edit_page'] = AUTH_DENY; +$session->perms['view_source'] = AUTH_DENY; +$template->tpl_strings['PAGE_NAME'] = 'Access denied'; + +$template->header(); +echo '

The administrator has flagged the page "' . $_SERVER['REQUEST_URI'] . '" so that it cannot be accessed from the web. Perhaps this is because this is a cache or includes directory and only needs to be accessed by scripts.

HTTP error: 403 Forbidden

'; +$template->footer(); +$db->close(); diff -r 902822492a68 -r fe660c52c48f includes/js-compressor.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/js-compressor.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,496 @@ +file_get_contents("mySource.js"))) + * (i.e. array('code'=>file_get_contents("mySource.js"), 'name'=>'mySource')) + * ... and should be an array of arrays created with theese rules + * array( + * file_get_contents("secret.js"), + * array('code'=>$anotherJS), + * array('code'=>$myJSapplication, 'name'=>'JSApplication V 1.0') + * ) + * + * The name used on dedicated key, will be write on parsed source header + * -------------------------------------------------------------- + * Note about returned strings: + * Your browser should wrap very long strings, then don't use + * cut and paste from your browser, save output into your database or directly + * in a file or print them only inside tags + * -------------------------------------------------------------- + * Note about parser performance: + * With pure PHP embed code this class should be slow and not really safe + * for your server performance then don't parse JavaScript runtime for each + * file you need and create some "parsed" caching system + * (at least while i've not created a compiled version of theese class functions). + * Here there's a caching system example: http://www.phpclasses.org/browse/package/3158.html + * -------------------------------------------------------------- + * Note about JavaScript packed compatibility: + * To be sure about compatibility include before every script JSL Library: + * http://www.devpro.it/JSL/ + * JSL library add some features for old or buggy browsers, one of + * those functions is String.replace with function as second argument, + * used by JavaScript generated packed code to rebuild original code. + * + * Remember that KDE 3.5, Safari and IE5 will not work correctly with packed version + * if you'll not include JSL. + * -------------------------------------------------------------- + * @Compatibility >= PHP 4 + * @Author Andrea Giammarchi + * @see http://www.devpro.it/ + * @since 2006/05/31 + * @since 2006/08/01 [requires SourceMap.class.php to parse source faster and better (dojo.js.uncompressed.js file (211Kb) successfull cleaned or packed)] + * @version 0.8 + * Dependencies: + * Server: BaseConvert.class.php + * Server: SourceMap.class.php + * Client: JSL.js (http://www.devpro.it/JSL/) + * Convertion is supported by every browser with JSL Library (FF 1+ Opera 8+ and IE5.5+ are supported without JSL too) + * @copyright Dean Edwards for his originally idea [dean.edwards.name] and his JavaScript packer + */ +class JavaScriptCompressor { + + /** + * public variables + * stats:string after every compression has some informations + * version:string version of this class + */ + var $stats = '', + $version = '0.8'; + + /** 'private' variables, any comment sorry */ + var $__startTime = 0, + $__sourceLength = 0, + $__sourceNewLength = 0, + $__totalSources = 0, + $__sources = array(), + $__delimeter = array(), + $__cleanFinder = array("/(\n|\r)+/", "/( |\t)+/", "/(\n )|( \n)|( \n )/", "/[[:space:]]+(\)|})/", "/(\(|{)[[:space:]]+/", "/[[:space:]]*(;|,|:|<|>|\&|\||\=|\?|\+|\-|\%)[[:space:]]*/", "/\)[[:space:]]+{/", "/}[[:space:]]+\(/"), + $__cleanReplacer = array("\n", " ", "\n", "\\1", "\\1", "\\1", "){", "}("), + $__BC = null, + $__SourceMap = null; + + /** + * public constructor + * creates a new BaseConvert class variable (base 36) + */ + function JavaScriptCompressor() { + $this->__SourceMap = new SourceMap(); + $this->__BC = new BaseConvert('0123456789abcdefghijklmnopqrstuvwxyz'); + $this->__delimeter = array( + array('name'=>'doublequote', 'start'=>'"', 'end'=>'"', 'noslash'=>true), + array('name'=>'singlequote', 'start'=>"'", 'end'=>"'", 'noslash'=>true), + array('name'=>'singlelinecomment', 'start'=>'//', 'end'=>array("\n", "\r")), + array('name'=>'multilinecomment', 'start'=>'/*', 'end'=>'*/'), + array('name'=>'regexp', 'start'=>'/', 'end'=>'/', 'match'=>"/^\/[^\n\r]+\/$/", 'noslash'=>true) + ); + } + + /** + * public method + * getClean(mixed [, bool]):string + * compress JavaScript removing comments and somespaces (on by default) + * @param mixed view example and notes on class comments + */ + function getClean($jsSource) { + return $this->__commonInitMethods($jsSource, false); + } + + /** + * public method + * getPacked(mixed):string + * compress JavaScript replaceing words and removing comments and some spaces + * @param mixed view example and notes on class comments + */ + function getPacked($jsSource) { + return $this->__commonInitMethods($jsSource, true); + } + + /** 'private' methods, any comment sorry */ + function __addCleanCode($str) { + return preg_replace($this->__cleanFinder, $this->__cleanReplacer, trim($str)); + } + function __addClean(&$arr, &$str, &$start, &$end, $clean) { + if($clean) + array_push($arr, $this->__addCleanCode(substr($str, $start, $end - $start))); + else + array_push($arr, substr($str, $start, $end - $start)); + } + function __clean(&$str) { + $len = strlen($str); + $type = ''; + $clean = array(); + $map = $this->__SourceMap->getMap($str, $this->__delimeter); + for($a = 0, $b = 0, $c = count($map); $a < $c; $a++) { + $type = &$map[$a]['name']; + switch($type) { + case 'code': + case 'regexp': + case 'doublequote': + case 'singlequote': + $this->__addClean($clean, $str, $map[$a]['start'], $map[$a]['end'], ($type === 'code')); + if($type !== 'regexp') + array_push($clean, "\n"); + break; + } + } + return preg_replace("/(\n)+/", "\n", trim(implode('', $clean))); + } + function __commonInitMethods(&$jsSource, $packed) { + $header = ''; + $this->__startTime = $this->__getTime(); + $this->__sourceLength = 0; + $this->__sourceManager($jsSource); + for($a = 0, $b = $this->__totalSources; $a < $b; $a++) + $this->__sources[$a]['code'] = $this->__clean($this->__sources[$a]['code']); + $header = $this->__getHeader(); + for($a = 0, $b = $this->__totalSources; $a < $b; $a++) + $this->__sources[$a] = &$this->__sources[$a]['code']; + $this->__sources = implode(';', $this->__sources); + if($packed) + $this->__sources = $this->__pack($this->__sources); + $this->__sourceNewLength = strlen($this->__sources); + $this->__setStats(); + return $header.$this->__sources; + } + function __getHeader() { + return implode('', array( + '/* ',$this->__getScriptNames(),'JavaScriptCompressor ',$this->version,' [www.devpro.it], ', + 'thanks to Dean Edwards for idea [dean.edwards.name]', + " */\r\n" + )); + } + function __getScriptNames() { + $a = 0; + $result = array(); + for($b = $this->__totalSources; $a < $b; $a++) { + if($this->__sources[$a]['name'] !== '') + array_push($result, $this->__sources[$a]['name']); + } + $a = count($result); + if($a-- > 0) + $result[$a] .= ' with '; + return $a < 0 ? '' : implode(', ', $result); + } + function __getSize($size, $dec = 2) { + $toEval = ''; + $type = array('bytes', 'Kb', 'Mb', 'Gb'); + $nsize = $size; + $times = 0; + while($nsize > 1024) { + $nsize = $nsize / 1024; + $toEval .= '/1024'; + $times++; + } + if($times === 0) + $fSize = $size.' '.$type[$times]; + else { + eval('$size=($size'.$toEval.');'); + $fSize = number_format($size, $dec, '.', '').' '.$type[$times]; + } + return $fSize; + } + function __getTime($startTime = null) { + list($usec, $sec) = explode(' ', microtime()); + $newtime = (float)$usec + (float)$sec; + if($startTime !== null) + $newtime = number_format(($newtime - $startTime), 3); + return $newtime; + } + function __pack(&$str) { + $container = array(); + $str = preg_replace("/(\w+)/e", '$this->__BC->toBase($this->__wordsParser("\\1",$container));', $this->__clean($str)); + $str = str_replace("\n", '\n', addslashes($str)); + return 'eval(function(A,G){return A.replace(/(\\w+)/g,function(a,b){return G[parseInt(b,36)]})}("'.$str.'","'.implode(',', $container).'".split(",")));'; + } + function __setStats() { + $this->stats = implode(' ', array( + $this->__getSize($this->__sourceLength), + 'to', + $this->__getSize($this->__sourceNewLength), + 'in', + $this->__getTime($this->__startTime), + 'seconds' + )); + } + function __sourceManager(&$jsSource) { + $b = count($jsSource); + $this->__sources = array(); + if(is_string($jsSource)) + $this->__sourcePusher($jsSource, ''); + elseif(is_array($jsSource) && $b > 0) { + if(isset($jsSource['code'])) + $this->__sourcePusher($jsSource['code'], (isset($jsSource['name']) ? $jsSource['name'] : '')); + else { + for($a = 0; $a < $b; $a++) { + if(is_array($jsSource[$a]) && isset($jsSource[$a]['code'], $jsSource[$a]['name'])) + $this->__sourcePusher($jsSource[$a]['code'], trim($jsSource[$a]['name'])); + elseif(is_string($jsSource[$a])) + $this->__sourcePusher($jsSource[$a], ''); + } + } + } + $this->__totalSources = count($this->__sources); + } + function __sourcePusher(&$code, $name) { + $this->__sourceLength += strlen($code); + array_push($this->__sources, array('code'=>$code, 'name'=>$name)); + } + function __wordsParser($str, &$d) { + if(is_null($key = array_shift($key = array_keys($d,$str)))) + $key = array_push($d, $str) - 1; + return $key; + } +} + +/** + * BaseConvert class, + * converts an unsigned base 10 integer to a different base and vice versa. + * ______________________________________________________________ + * BaseConvert + * | + * |________ constructor(newBase:string) + * | uses newBase string var for convertion + * | [i.e. "0123456789abcdef" for an hex convertion] + * | + * |________ toBase(unsignedInteger:uint):string + * | return base value of input + * | + * |________ fromBase(baseString:string):uint + * return base 10 integer value of base input + * -------------------------------------------------------------- + * REMEMBER: PHP < 6 doesn't work correctly with integer greater than 2147483647 (2^31 - 1) + * -------------------------------------------------------------- + * @Compatibility >= PHP 4 + * @Author Andrea Giammarchi + * @Site http://www.devpro.it/ + * @Date 2006/06/05 + * @Version 1.0 + */ + +class BaseConvert { + + var $base, $baseLength; + + function BaseConvert($base) { + $this->base = &$base; + $this->baseLength = strlen($base); + } + + function toBase($num) { + $module = 0; $result = ''; + while($num) { + $result = $this->base{($module = $num % $this->baseLength)}.$result; + $num = (int)(($num - $module) / $this->baseLength); + } + return $result !== '' ? $result : $this->base{0}; + } + + function fromBase($str) { + $pos = 0; $len = strlen($str) - 1; $result = 0; + while($pos < $len) + $result += pow($this->baseLength, ($len - $pos)) * strpos($this->base, $str{($pos++)}); + return $len >= 0 ? $result + strpos($this->base, $str{($pos)}) : null; + } +} + +/** +* SourceMap class, +* reads a generic language source code and returns its map. +* ______________________________________________________________ +* The SourceMap goals is to create a map of a generic script/program language. +* The getMap method returns an array/list of arrays/dictionary/objects +* of source map using delimeters variable to map correctly: +* - multi line comments +* - single line comments +* - double quoted strings +* - single quoted strings +* - pure code +* - everything else (for example regexp [/re/] with javascript), just adding a correct delimeter +* -------------------------------------------------------------- +* What about the delimeter +* It's an array/list of arrays/dictionary/obects with some properties to find what you're looking for. +* +* parameters are: +* - name, the name of the delimeter (i.e. "doublequote") +* - start, one or mode chars to find as start delimeter (i.e. " for double quoted string) +* - end, one or mode chars to find as end delimeter (i.e. " for double quoted string) [end should be an array/list too] +* +* optional parameters are: +* - noslash, if true find the end of the delimeter only if last char is not slashed (i.e. "string\"test" find " after test) +* - match, if choosed language has regexp, verify if string from start to end matches used regexp (i.e. /^\/[^\n\r]+\/$/ for JavaScript regexp) +* +* If end parameter is an array, match and noslash are not supported (i.e. ["\n", "\r"] for end delimeter of a single line comment) +* -------------------------------------------------------------- +* What about SourceMap usage +* It should be a good solution to create sintax highlighter, parser, +* verifier or some other source code parsing procedure +* -------------------------------------------------------------- +* What about SourceMap performance script/languages +* I've created different version of this class to test each script/program language performance too. +* Python with or without Psyco is actually the faster parser. +* However with this PHP version this class has mapped "dojo.js.uncompressed.js" file (about 211Kb) in less than 0.5 second. +* Test has been done with embed class and PHP as module, any accelerator was used for this PHP test. +* -------------------------------------------------------------- +* @Compatibility >= PHP 4 +* @Author Andrea Giammarchi +* @Site http://www.devpro.it/ +* @Date 2006/08/01 +* @LastMOd 2006/08/01 +* @Version 0.1 +* @Application Last version of JavaScriptCompressor class use this one to map source code. +*/ +class SourceMap { + + /** + * public method + * getMap(&$source:string, &$delimeters:array):array + * Maps the source code using $delimeters rules and returns map as an array + * NOTE: read comments to know more about map and delimeter + * + * @param string generic source code + * @param array array with nested array with code rules + */ + function getMap(&$source, &$delimeters) { + + # "unsigned" integer variables + $sourcePosition = 0; + $delimetersPosition = 0; + $findLength = 0; + $len = 0; + $tempIndex = 0; + $sourceLength = strlen($source); + $delimetersLength = count($delimeters); + + # integer variables + $tempPosition = -1; + $endPosition = -1; + + # array variables + $map = array(); + $tempMap = array(); + $tempDelimeter = array(); + + while($sourcePosition < $sourceLength) { + $endPosition = -1; + for($delimetersPosition = 0; $delimetersPosition < $delimetersLength; $delimetersPosition++) { + $tempPosition = strpos($source, $delimeters[$delimetersPosition]['start'], $sourcePosition); + if($tempPosition !== false && ($tempPosition < $endPosition || $endPosition === -1)) { + $endPosition = $tempPosition; + $tempIndex = $delimetersPosition; + } + } + if($endPosition !== -1) { + $sourcePosition = $endPosition; + $tempDelimeter = &$delimeters[$tempIndex]; + $findLength = strlen($tempDelimeter['start']); + if(is_array($tempDelimeter['end'])) { + $delimetersPosition = 0; + $endPosition = -1; + for($len = count($tempDelimeter['end']); $delimetersPosition < $len; $delimetersPosition++) { + $tempPosition = strpos($source, $tempDelimeter['end'][$delimetersPosition], $sourcePosition + $findLength); + if($tempPosition !== false && ($tempPosition < $endPosition || $endPosition === -1)) { + $endPosition = $tempPosition; + $tempIndex = $delimetersPosition; + } + } + if($endPosition !== -1) + $endPosition = $endPosition + strlen($tempDelimeter['end'][$tempIndex]); + else + $endPosition = $sourceLength; + array_push($map, array('name'=>$tempDelimeter['name'], 'start'=>$sourcePosition, 'end'=>$endPosition)); + $sourcePosition = $endPosition - 1; + } + elseif(isset($tempDelimeter['match'])) { + $tempPosition = strpos($source, $tempDelimeter['end'], $sourcePosition + $findLength); + $len = strlen($tempDelimeter['end']); + if($tempPosition !== false && preg_match($tempDelimeter['match'], substr($source, $sourcePosition, $tempPosition - $sourcePosition + $len))) { + $endPosition = isset($tempDelimeter['noslash']) ? $this->__endCharNoSlash($source, $sourcePosition, $tempDelimeter['end'], $sourceLength) : $tempPosition + $len; + array_push($map, array('name'=>$tempDelimeter['name'], 'start'=>$sourcePosition, 'end'=>$endPosition)); + $sourcePosition = $endPosition - 1; + } + } + else { + if(isset($tempDelimeter['noslash'])) + $endPosition = $this->__endCharNoSlash($source, $sourcePosition, $tempDelimeter['end'], $sourceLength); + else { + $tempPosition = strpos($source, $tempDelimeter['end'], $sourcePosition + $findLength); + if($tempPosition !== false) + $endPosition = $tempPosition + strlen($tempDelimeter['end']); + else + $endPosition = $sourceLength; + } + array_push($map, array('name'=>$tempDelimeter['name'], 'start'=>$sourcePosition, 'end'=>$endPosition)); + $sourcePosition = $endPosition - 1; + } + } + else + $sourcePosition = $sourceLength - 1; + ++$sourcePosition; + } + $len = count($map); + if($len === 0) + array_push($tempMap, array('name'=>'code', 'start'=>0, 'end'=>$sourceLength)); + else { + for($tempIndex = 0; $tempIndex < $len; $tempIndex++) { + if($tempIndex === 0 && $map[$tempIndex]['start'] > 0) + array_push($tempMap, array('name'=>'code', 'start'=>0, 'end'=>$map[$tempIndex]['start'])); + elseif($tempIndex > 0 && $map[$tempIndex]['start'] > $map[$tempIndex-1]['end']) + array_push($tempMap, array('name'=>'code', 'start'=>$map[$tempIndex-1]['end'], 'end'=>$map[$tempIndex]['start'])); + array_push($tempMap, array('name'=>$map[$tempIndex]['name'], 'start'=>$map[$tempIndex]['start'], 'end'=>$map[$tempIndex]['end'])); + if($tempIndex + 1 === $len && $map[$tempIndex]['end'] < $sourceLength) + array_push($tempMap, array('name'=>'code', 'start'=>$map[$tempIndex]['end'], 'end'=>$sourceLength)); + } + } + return $tempMap; + } + + function __endCharNoSlash(&$source, $position, &$find, &$len) { + $temp = strlen($find); + do { + $position = strpos($source, $find, $position + 1); + }while($position !== false && !$this->__charNoSlash($source, $position)); + if($position === false) $position = $len - $temp; + return $position + $temp; + } + + function __charNoSlash(&$source, &$position) { + $next = 1; $len = $position - $next; + while($len > 0 && $source{$len} === '\\') $len = $position - (++$next); + return (($next - 1) % 2 === 0); + } +} +?> \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/json.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/json.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,806 @@ + +* @author Matt Knapp +* @author Brett Stimmerman +* @copyright 2005 Michal Migurski +* @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $ +* @license http://www.opensource.org/licenses/bsd-license.php +* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 +*/ + +/** +* Marker constant for Services_JSON::decode(), used to flag stack state +*/ +define('SERVICES_JSON_SLICE', 1); + +/** +* Marker constant for Services_JSON::decode(), used to flag stack state +*/ +define('SERVICES_JSON_IN_STR', 2); + +/** +* Marker constant for Services_JSON::decode(), used to flag stack state +*/ +define('SERVICES_JSON_IN_ARR', 3); + +/** +* Marker constant for Services_JSON::decode(), used to flag stack state +*/ +define('SERVICES_JSON_IN_OBJ', 4); + +/** +* Marker constant for Services_JSON::decode(), used to flag stack state +*/ +define('SERVICES_JSON_IN_CMT', 5); + +/** +* Behavior switch for Services_JSON::decode() +*/ +define('SERVICES_JSON_LOOSE_TYPE', 16); + +/** +* Behavior switch for Services_JSON::decode() +*/ +define('SERVICES_JSON_SUPPRESS_ERRORS', 32); + +/** + * Converts to and from JSON format. + * + * Brief example of use: + * + * + // create a new instance of Services_JSON + $json = new Services_JSON(); + + // convert a complexe value to JSON notation, and send it to the browser + $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); + $output = $json->encode($value); + + print($output); + // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] + + // accept incoming POST data, assumed to be in JSON notation + $input = file_get_contents('php://input', 1000000); + $value = $json->decode($input); + + */ +class Services_JSON +{ + /** + * constructs a new JSON instance + * + * @param int $use object behavior flags; combine with boolean-OR + * + * possible values: + * - SERVICES_JSON_LOOSE_TYPE: loose typing. + * "{...}" syntax creates associative arrays + * instead of objects in decode(). + * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. + * Values which can't be encoded (e.g. resources) + * appear as NULL instead of throwing errors. + * By default, a deeply-nested resource will + * bubble up with an error, so all return values + * from encode() should be checked with isError() + */ + function Services_JSON($use = 0) + { + $this->use = $use; + } + + /** + * convert a string from one UTF-16 char to one UTF-8 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf16 UTF-16 character + * @return string UTF-8 character + * @access private + */ + function utf162utf8($utf16) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); + } + + $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); + + switch(true) { + case ((0x7F & $bytes) == $bytes): + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x7F & $bytes); + + case (0x07FF & $bytes) == $bytes: + // return a 2-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xC0 | (($bytes >> 6) & 0x1F)) + . chr(0x80 | ($bytes & 0x3F)); + + case (0xFFFF & $bytes) == $bytes: + // return a 3-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xE0 | (($bytes >> 12) & 0x0F)) + . chr(0x80 | (($bytes >> 6) & 0x3F)) + . chr(0x80 | ($bytes & 0x3F)); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + function utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch(strlen($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function encode($var) + { + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = strlen($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2})); + $c += 2; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + $properties = array_map(array($this, 'name_value'), + array_keys($var), + array_values($var)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + } + + // treat it like a regular array + $elements = array_map(array($this, 'encode'), $var); + + foreach($elements as $element) { + if(Services_JSON::isError($element)) { + return $element; + } + } + + return '[' . join(',', $elements) . ']'; + + case 'object': + $vars = get_object_vars($var); + + $properties = array_map(array($this, 'name_value'), + array_keys($vars), + array_values($vars)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + + default: + return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) + ? 'null' + : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + function name_value($name, $value) + { + $encoded_value = $this->encode($value); + + if(Services_JSON::isError($encoded_value)) { + return $encoded_value; + } + + return $this->encode(strval($name)) . ':' . $encoded_value; + } + + /** + * reduce a string by removing leading and trailing comments and whitespace + * + * @param $str string string value to strip of comments and whitespace + * + * @return string string value stripped of comments and whitespace + * @access private + */ + function reduce_string($str) + { + $str = preg_replace(array( + + // eliminate single line comments in '// ...' form + '#^\s*//(.+)$#m', + + // eliminate multi-line comments in '/* ... */' form, at start of string + '#^\s*/\*(.+)\*/#Us', + + // eliminate multi-line comments in '/* ... */' form, at end of string + '#/\*(.+)\*/\s*$#Us' + + ), '', $str); + + // eliminate extraneous space + return trim($str); + } + + /** + * decodes a JSON string into appropriate variable + * + * @param string $str JSON-formatted string + * + * @return mixed number, boolean, string, array, or object + * corresponding to given JSON input string. + * See argument 1 to Services_JSON() above for object-output behavior. + * Note that decode() always returns strings + * in ASCII or UTF-8 format! + * @access public + */ + function decode($str) + { + $str = $this->reduce_string($str); + + switch (strtolower($str)) { + case 'true': + return true; + + case 'false': + return false; + + case 'null': + return null; + + default: + $m = array(); + + if (is_numeric($str)) { + // Lookie-loo, it's a number + + // This would work on its own, but I'm trying to be + // good about returning integers where appropriate: + // return (float)$str; + + // Return float or int, as appropriate + return ((float)$str == (integer)$str) + ? (integer)$str + : (float)$str; + + } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { + // STRINGS RETURNED IN UTF-8 FORMAT + $delim = substr($str, 0, 1); + $chrs = substr($str, 1, -1); + $utf8 = ''; + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c < $strlen_chrs; ++$c) { + + $substr_chrs_c_2 = substr($chrs, $c, 2); + $ord_chrs_c = ord($chrs{$c}); + + switch (true) { + case $substr_chrs_c_2 == '\b': + $utf8 .= chr(0x08); + ++$c; + break; + case $substr_chrs_c_2 == '\t': + $utf8 .= chr(0x09); + ++$c; + break; + case $substr_chrs_c_2 == '\n': + $utf8 .= chr(0x0A); + ++$c; + break; + case $substr_chrs_c_2 == '\f': + $utf8 .= chr(0x0C); + ++$c; + break; + case $substr_chrs_c_2 == '\r': + $utf8 .= chr(0x0D); + ++$c; + break; + + case $substr_chrs_c_2 == '\\"': + case $substr_chrs_c_2 == '\\\'': + case $substr_chrs_c_2 == '\\\\': + case $substr_chrs_c_2 == '\\/': + if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || + ($delim == "'" && $substr_chrs_c_2 != '\\"')) { + $utf8 .= $chrs{++$c}; + } + break; + + case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): + // single, escaped unicode character + $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) + . chr(hexdec(substr($chrs, ($c + 4), 2))); + $utf8 .= $this->utf162utf8($utf16); + $c += 5; + break; + + case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): + $utf8 .= $chrs{$c}; + break; + + case ($ord_chrs_c & 0xE0) == 0xC0: + // characters U-00000080 - U-000007FF, mask 110XXXXX + //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 2); + ++$c; + break; + + case ($ord_chrs_c & 0xF0) == 0xE0: + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 3); + $c += 2; + break; + + case ($ord_chrs_c & 0xF8) == 0xF0: + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 4); + $c += 3; + break; + + case ($ord_chrs_c & 0xFC) == 0xF8: + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 5); + $c += 4; + break; + + case ($ord_chrs_c & 0xFE) == 0xFC: + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 6); + $c += 5; + break; + + } + + } + + return $utf8; + + } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { + // array, or object notation + + if ($str{0} == '[') { + $stk = array(SERVICES_JSON_IN_ARR); + $arr = array(); + } else { + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = array(); + } else { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = new stdClass(); + } + } + + array_push($stk, array('what' => SERVICES_JSON_SLICE, + 'where' => 0, + 'delim' => false)); + + $chrs = substr($str, 1, -1); + $chrs = $this->reduce_string($chrs); + + if ($chrs == '') { + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } else { + return $obj; + + } + } + + //print("\nparsing {$chrs}\n"); + + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c <= $strlen_chrs; ++$c) { + + $top = end($stk); + $substr_chrs_c_2 = substr($chrs, $c, 2); + + if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { + // found a comma that is not inside a string, array, etc., + // OR we've reached the end of the character list + $slice = substr($chrs, $top['where'], ($c - $top['where'])); + array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); + //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + // we are in an array, so just push an element onto the stack + array_push($arr, $this->decode($slice)); + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + // we are in an object, so figure + // out the property name and set an + // element in an associative array, + // for now + $parts = array(); + + if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // "name":value pair + $key = $this->decode($parts[1]); + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // name:value pair, where name is unquoted + $key = $parts[1]; + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } + + } + + } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { + // found a quote, and we are not inside a string + array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); + //print("Found start of string at {$c}\n"); + + } elseif (($chrs{$c} == $top['delim']) && + ($top['what'] == SERVICES_JSON_IN_STR) && + ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { + // found a quote, we're in a string, and it's not escaped + // we know that it's not escaped becase there is _not_ an + // odd number of backslashes at the end of the string so far + array_pop($stk); + //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '[') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-bracket, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); + //print("Found start of array at {$c}\n"); + + } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { + // found a right-bracket, and we're in an array + array_pop($stk); + //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '{') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-brace, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); + //print("Found start of object at {$c}\n"); + + } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { + // found a right-brace, and we're in an object + array_pop($stk); + //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($substr_chrs_c_2 == '/*') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a comment start, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); + $c++; + //print("Found start of comment at {$c}\n"); + + } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { + // found a comment end, and we're in one now + array_pop($stk); + $c++; + + for ($i = $top['where']; $i <= $c; ++$i) + $chrs = substr_replace($chrs, ' ', $i, 1); + + //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } + + } + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + return $obj; + + } + + } + } + } + + /** + * @todo Ultimately, this should just call PEAR::isError() + */ + function isError($data, $code = null) + { + if (class_exists('pear')) { + return PEAR::isError($data, $code); + } elseif (is_object($data) && (get_class($data) == 'services_json_error' || + is_subclass_of($data, 'services_json_error'))) { + return true; + } + + return false; + } +} + +if (class_exists('PEAR_Error')) { + + class Services_JSON_Error extends PEAR_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + parent::PEAR_Error($message, $code, $mode, $options, $userinfo); + } + } + +} else { + + /** + * @todo Ultimately, this class shall be descended from PEAR_Error + */ + class Services_JSON_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + + } + } + +} + +?> \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/magic.mime.mgc Binary file includes/magic.mime.mgc has changed diff -r 902822492a68 -r fe660c52c48f includes/pageprocess.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/pageprocess.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,457 @@ + + */ + +class PageProcessor +{ + + /** + * Page ID and namespace of the page handled by this instance + * @var string + */ + + var $page_id; + var $namespace; + + /** + * Tracks if the page we're loading exists in the database or not. + * @var bool + */ + + var $page_exists = false; + + /** + * Permissions! + * @var object + */ + + var $perms = null; + + /** + * Switch to track if redirects are allowed. Defaults to true. + * @var bool + */ + + var $allow_redir = true; + + /** + * If this is set to true, this will call the header and footer funcs on $template when render() is called. + * @var bool + */ + + var $send_headers = false; + + /** + * Cache the fetched text so we don't fetch it from the DB twice. + * @var string + */ + + var $text_cache = ''; + + /** + * Debugging information to track errors. You can set enable to false to disable sending debug information. + * @var array + */ + + var $debug = array( + 'enable' => true, + 'works' => false + ); + + /** + * Constructor. + * @param string The page ID (urlname) of the page + * @param string The namespace of the page + */ + + function __construct( $page_id, $namespace ) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + // See if we can get some debug info + if ( function_exists('debug_backtrace') && $this->debug['enable'] ) + { + $this->debug['works'] = true; + $this->debug['backtrace'] = enano_debug_print_backtrace(true); + } + + // First things first - check page existence and permissions + + if ( !isset($paths->nslist[$namespace]) ) + { + $this->send_error('The namespace "' . htmlspecialchars($namespace) . '" does not exist.'); + } + + $this->_setup( $page_id, $namespace ); + + } + + /** + * The main method to send the page content. Also responsible for checking permissions. + */ + + function send() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if ( !$this->perms->get_permissions('read') ) + { + $this->err_access_denied(); + return false; + } + if ( $this->namespace == 'Special' || $this->namespace == 'Admin' ) + { + if ( !$this->page_exists ) + { + redirect( makeUrl(getConfig('main_page')), 'Can\'t find special page', 'The special or administration page you requested does not exist. You will now be transferred to the main page.', 2 ); + } + $func_name = "page_{$this->namespace}_{$this->page_id}"; + if ( function_exists($func_name) ) + { + return @call_user_func($func_name); + } + else + { + $title = 'Page backend not found'; + $message = "The administration page you are looking for was properly registered using the page API, but the backend function + ($fname) was not found. If this is a plugin page, then this is almost certainly a bug with the plugin."; + + if ( $this->send_headers ) + { + $template->tpl_strings['PAGE_NAME'] = $title; + $template->header(); + echo "

$message

"; + $template->footer(); + } + else + { + echo "

$title

+

$message

"; + } + return false; + } + } + else if ( in_array($this->namespace, array('Article', 'User', 'Project', 'Help', 'File', 'Category')) && $this->page_exists ) + { + // Send as regular page + $text = $this->fetch_text(); + if ( $text == 'err_no_text_rows' ) + { + $this->err_no_rows(); + return false; + } + else + { + $this->render(); + } + } + else if ( ( $this->namespace == 'Template' || $this->namespace == 'System' ) && $this->page_exists ) + { + $this->header(); + + $text = $this->fetch_text(); + $text = preg_replace('/(.*?)<\/noinclude>/is', '\\1', $text); + $text = preg_replace('/(.*?)<\/nodisplay>/is', '', $text); + + $text = RenderMan::render( $text ); + + echo $text; + + $this->footer(); + + } + else if ( !$this->page_exists ) + { + // Perhaps this is hooked? + ob_start(); + + $code = $plugins->setHook('page_not_found'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + $ob = ob_get_contents(); + + if ( empty($ob) ) + { + $this->err_page_not_existent(); + } + + } + + } + + /** + * Sets internal variables. + * @access private + */ + + function _setup($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $page_id_cleaned = sanitize_page_id($page_id); + + $this->page_id = $page_id_cleaned; + $this->namespace = $namespace; + + $this->perms = $session->fetch_page_acl( $page_id, $namespace ); + + // Exception for Admin: pages + if ( $this->namespace == 'Admin' ) + { + $fname = "page_Admin_{$this->page_id}"; + } + + // Does the page "exist"? + if ( $paths->cpage['urlname_nons'] == $page_id && $paths->namespace == $namespace && !$paths->page_exists && ( $this->namespace == 'Admin' && !function_exists($fname) ) ) + { + $this->page_exists = false; + } + else if ( !isset( $paths->pages[ $paths->nslist[$namespace] . $page_id ] ) && ( $this->namespace == 'Admin' && !function_exists($fname) ) ) + { + $this->page_exists = false; + } + else + { + $this->page_exists = true; + } + } + + /** + * Renders it all in one go, and echoes it out. This assumes that the text is in the DB. + * @access private + */ + + function render() + { + $text = $this->fetch_text(); + + $this->header(); + display_page_headers(); + echo RenderMan::render($text); + display_page_footers(); + $this->footer(); + } + + /** + * Sends the page header, dependent on, of course, whether we're supposed to. + */ + + function header() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if ( $this->send_headers ) + $template->header(); + } + + /** + * Sends the page footer, dependent on, of course, whether we're supposed to. + */ + + function footer() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if ( $this->send_headers ) + $template->footer(); + } + + /** + * Fetches the raw, unfiltered page text. + * @access public + */ + + function fetch_text() + { + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( !empty($this->text_cache) ) + { + return $this->text_cache; + } + + $q = $db->sql_query('SELECT page_text, char_tag FROM '.table_prefix.'page_text WHERE page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\';'); + if ( !$q ) + { + $this->send_error('Error during SQL query.', true); + } + if ( $db->numrows() < 1 ) + { + $this->page_exists = false; + return 'err_no_text_rows'; + } + + $row = $db->fetchrow(); + $db->free_result(); + + if ( !empty($row['char_tag']) ) + { + // This page text entry uses the old text-escaping format + $from = array( + "{APOS:{$row['char_tag']}}", + "{QUOT:{$row['char_tag']}}", + "{SLASH:{$row['char_tag']}}" + ); + $to = array("'", '"', '\\'); + $row['page_text'] = str_replace($from, $to, $row['page_text']); + } + + $this->text_cache = $row['page_text']; + + return $row['page_text']; + + } + + /** + * Send the error message to the user that the access to this page is denied. + * @access private + */ + + function err_access_denied() + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $ob = ''; + $template->tpl_strings['PAGE_NAME'] = 'Access denied'; + + if ( $this->send_headers ) + { + $ob .= $template->getHeader(); + } + + $ob .= '
Access to this page is denied.
This may be because you are not logged in or you have not met certain criteria for viewing this page.
'; + + if ( $this->send_headers ) + { + $ob .= $template->getFooter(); + } + echo $ob; + } + + /** + * Send the error message to the user complaining that there weren't any rows. + * @access private + */ + + function err_no_rows() + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $title = 'No text rows'; + $message = 'While the page\'s existence was verified, there were no rows in the database that matched the query for the text. This may indicate a bug with the software; ask the webmaster for more information. The offending query was:
' . $db->latest_query . '
'; + if ( $this->send_headers ) + { + $template->tpl_strings['PAGE_NAME'] = $title; + $template->header(); + echo "

$message

"; + $template->footer(); + } + else + { + echo "

$title

+

$message

"; + } + } + + /** + * Tell the user the page doesn't exist, and present them with their options. + * @access private + */ + + function err_page_not_existent() + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $this->header(); + header('HTTP/1.1 404 Not Found'); + echo '

There is no page with this title yet.

+

You have requested a page that doesn\'t exist yet.'; + if ( $session->get_permissions('create_page') ) + { + echo ' You can create this page, or return to the homepage.'; + } + else + { + echo ' Return to the homepage.

'; + } + if ( $session->get_permissions('history_rollback') ) + { + $e = $db->sql_query('SELECT * FROM ' . table_prefix . 'logs WHERE action=\'delete\' AND page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\' ORDER BY time_id DESC;'); + if ( !$e ) + { + $db->_die('The deletion log could not be selected.'); + } + if ( $db->numrows() > 0 ) + { + $r = $db->fetchrow(); + echo '

This page also appears to have some log entries in the database - it seems that it was deleted on ' . $r['date_string'] . '. You can probably roll back the deletion.

'; + } + $db->free_result(); + } + echo '

+ HTTP Error: 404 Not Found +

'; + $this->footer(); + } + + /** + * PHP 4 constructor. + * @see PageProcessor::__construct() + */ + + function PageProcessor( $page_id, $namespace ) + { + $this->__construct($page_id, $namespace); + } + + /** + * Send an error message and die + * @var string Error message + * @var bool If true, send DBAL's debugging information as well + */ + + function send_error($message, $sql = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $content = "

$message

"; + $template->tpl_strings['PAGE_NAME'] = 'General error in page fetcher'; + + if ( $this->debug['works'] ) + { + $content .= $this->debug['backtrace']; + } + + header('HTTP/1.1 500 Internal Server Error'); + + $template->header(); + echo $content; + $template->footer(); + + $db->close(); + + exit; + + } + +} // class PageProcessor + +?> diff -r 902822492a68 -r fe660c52c48f includes/pageprocess.php~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/pageprocess.php~ Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,455 @@ + + */ + +class PageProcessor +{ + + /** + * Page ID and namespace of the page handled by this instance + * @var string + */ + + var $page_id; + var $namespace; + + /** + * Tracks if the page we're loading exists in the database or not. + * @var bool + */ + + var $page_exists = false; + + /** + * Permissions! + * @var object + */ + + var $perms = null; + + /** + * Switch to track if redirects are allowed. Defaults to true. + * @var bool + */ + + var $allow_redir = true; + + /** + * If this is set to true, this will call the header and footer funcs on $template when render() is called. + * @var bool + */ + + var $send_headers = false; + + /** + * Cache the fetched text so we don't fetch it from the DB twice. + * @var string + */ + + var $text_cache = ''; + + /** + * Debugging information to track errors. You can set enable to false to disable sending debug information. + * @var array + */ + + var $debug = array( + 'enable' => true, + 'works' => false + ); + + /** + * Constructor. + * @param string The page ID (urlname) of the page + * @param string The namespace of the page + */ + + function __construct( $page_id, $namespace ) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + // See if we can get some debug info + if ( function_exists('debug_backtrace') && $this->debug['enable'] ) + { + $this->debug['works'] = true; + $this->debug['backtrace'] = enano_debug_print_backtrace(true); + } + + // First things first - check page existence and permissions + + if ( !isset($paths->nslist[$namespace]) ) + { + $this->send_error('The namespace "' . htmlspecialchars($namespace) . '" does not exist.'); + } + + $this->_setup( $page_id, $namespace ); + + } + + /** + * The main method to send the page content. Also responsible for checking permissions. + */ + + function send() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if ( !$this->perms->get_permissions('read') ) + { + $this->err_access_denied(); + return false; + } + if ( $this->namespace == 'Special' || $this->namespace == 'Admin' ) + { + if ( !$this->page_exists ) + { + redirect( makeUrl(getConfig('main_page')), 'Can\'t find special page', 'The special or administration page you requested does not exist. You will now be transferred to the main page.', 2 ); + } + $func_name = "page_{$this->namespace}_{$this->page_id}"; + if ( function_exists($func_name) ) + { + return @call_user_func($func_name); + } + else + { + $title = 'Page backend not found'; + $message = "The administration page you are looking for was properly registered using the page API, but the backend function + ($fname) was not found. If this is a plugin page, then this is almost certainly a bug with the plugin."; + + if ( $this->send_headers ) + { + $template->tpl_strings['PAGE_NAME'] = $title; + $template->header(); + echo "

$message

"; + $template->footer(); + } + else + { + echo "

$title

+

$message

"; + } + return false; + } + } + else if ( in_array($this->namespace, array('Article', 'User', 'Project', 'Help', 'File', 'Category')) && $this->page_exists ) + { + // Send as regular page + $text = $this->fetch_text(); + if ( $text == 'err_no_text_rows' ) + { + $this->err_no_rows(); + return false; + } + else + { + $this->render(); + } + } + else if ( ( $this->namespace == 'Template' || $this->namespace == 'System' ) && $this->page_exists ) + { + $this->header(); + + $text = $this->fetch_text(); + $text = preg_replace('/(.*?)<\/noinclude>/is', '\\1', $text); + $text = preg_replace('/(.*?)<\/nodisplay>/is', '', $text); + + $text = RenderMan::render( $text ); + + echo $text; + + $this->footer(); + + } + else if ( !$this->page_exists ) + { + // Perhaps this is hooked? + ob_start(); + + $code = $plugins->setHook('page_not_found'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + $ob = ob_get_contents(); + + if ( empty($ob) ) + { + $this->err_page_not_existent(); + } + + } + + } + + /** + * Sets internal variables. + * @access private + */ + + function _setup($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $page_id_cleaned = sanitize_page_id($page_id); + + $this->page_id = $page_id_cleaned; + $this->namespace = $namespace; + + $this->perms = $session->fetch_page_acl( $page_id, $namespace ); + + // Exception for Admin: pages + if ( $this->namespace == 'Admin' ) + { + $fname = "page_Admin_{$this->page_id}"; + } + + // Does the page "exist"? + if ( $paths->cpage['urlname_nons'] == $page_id && $paths->namespace == $namespace && !$paths->page_exists && ( $this->namespace == 'Admin' && !function_exists($fname) ) ) + { + $this->page_exists = false; + } + else if ( !isset( $paths->pages[ $paths->nslist[$namespace] . $page_id ] ) && ( $this->namespace == 'Admin' && !function_exists($fname) ) ) + { + $this->page_exists = false; + } + else + { + $this->page_exists = true; + } + } + + /** + * Renders it all in one go, and echoes it out. This assumes that the text is in the DB. + * @access private + */ + + function render() + { + $text = $this->fetch_text(); + + $this->header(); + echo RenderMan::render($text); + $this->footer(); + } + + /** + * Sends the page header, dependent on, of course, whether we're supposed to. + */ + + function header() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if ( $this->send_headers ) + $template->header(); + } + + /** + * Sends the page footer, dependent on, of course, whether we're supposed to. + */ + + function footer() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if ( $this->send_headers ) + $template->footer(); + } + + /** + * Fetches the raw, unfiltered page text. + * @access public + */ + + function fetch_text() + { + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( !empty($this->text_cache) ) + { + return $this->text_cache; + } + + $q = $db->sql_query('SELECT page_text, char_tag FROM '.table_prefix.'page_text WHERE page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\';'); + if ( !$q ) + { + $this->send_error('Error during SQL query.', true); + } + if ( $db->numrows() < 1 ) + { + $this->page_exists = false; + return 'err_no_text_rows'; + } + + $row = $db->fetchrow(); + $db->free_result(); + + if ( !empty($row['char_tag']) ) + { + // This page text entry uses the old text-escaping format + $from = array( + "{APOS:{$row['char_tag']}}", + "{QUOT:{$row['char_tag']}}", + "{SLASH:{$row['char_tag']}}" + ); + $to = array("'", '"', '\\'); + $row['page_text'] = str_replace($from, $to, $row['page_text']); + } + + $this->text_cache = $row['page_text']; + + return $row['page_text']; + + } + + /** + * Send the error message to the user that the access to this page is denied. + * @access private + */ + + function err_access_denied() + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $ob = ''; + $template->tpl_strings['PAGE_NAME'] = 'Access denied'; + + if ( $this->send_headers ) + { + $ob .= $template->getHeader(); + } + + $ob .= '
Access to this page is denied.
This may be because you are not logged in or you have not met certain criteria for viewing this page.
'; + + if ( $this->send_headers ) + { + $ob .= $template->getFooter(); + } + echo $ob; + } + + /** + * Send the error message to the user complaining that there weren't any rows. + * @access private + */ + + function err_no_rows() + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $title = 'No text rows'; + $message = 'While the page\'s existence was verified, there were no rows in the database that matched the query for the text. This may indicate a bug with the software; ask the webmaster for more information.'; + if ( $this->send_headers ) + { + $template->tpl_strings['PAGE_NAME'] = $title; + $template->header(); + echo "

$message

"; + $template->footer(); + } + else + { + echo "

$title

+

$message

"; + } + } + + /** + * Tell the user the page doesn't exist, and present them with their options. + * @access private + */ + + function err_page_not_existent() + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $this->header(); + header('HTTP/1.1 404 Not Found'); + echo '

There is no page with this title yet.

+

You have requested a page that doesn\'t exist yet.'; + if ( $session->get_permissions('create_page') ) + { + echo ' You can create this page, or return to the homepage.'; + } + else + { + echo ' Return to the homepage.

'; + } + if ( $session->get_permissions('history_rollback') ) + { + $e = $db->sql_query('SELECT * FROM ' . table_prefix . 'logs WHERE action=\'delete\' AND page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\' ORDER BY time_id DESC;'); + if ( !$e ) + { + $db->_die('The deletion log could not be selected.'); + } + if ( $db->numrows() > 0 ) + { + $r = $db->fetchrow(); + echo '

This page also appears to have some log entries in the database - it seems that it was deleted on ' . $r['date_string'] . '. You can probably roll back the deletion.

'; + } + $db->free_result(); + } + echo '

+ HTTP Error: 404 Not Found +

'; + $this->footer(); + } + + /** + * PHP 4 constructor. + * @see PageProcessor::__construct() + */ + + function PageProcessor( $page_id, $namespace ) + { + $this->__construct($page_id, $namespace); + } + + /** + * Send an error message and die + * @var string Error message + * @var bool If true, send DBAL's debugging information as well + */ + + function send_error($message, $sql = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $content = "

$message

"; + $template->tpl_strings['PAGE_NAME'] = 'General error in page fetcher'; + + if ( $this->debug['works'] ) + { + $content .= $this->debug['backtrace']; + } + + header('HTTP/1.1 500 Internal Server Error'); + + $template->header(); + echo $content; + $template->footer(); + + $db->close(); + + exit; + + } + +} // class PageProcessor + +?> diff -r 902822492a68 -r fe660c52c48f includes/pageutils.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/pageutils.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,2060 @@ +sql_query('SELECT username FROM '.table_prefix.'users WHERE username=\''.$db->escape(rawurldecode($name)).'\''); + if(!$q) die(mysql_error()); + if($db->numrows() < 1) { $db->free_result(); return('good'); } + else { $db->free_result(); return('bad'); } + } + + /** + * Get the wiki formatting source for a page + * @param $page the full page id (Namespace:Pagename) + * @return string + * @todo (DONE) Make it require a password (just for security purposes) + */ + + function getsource($page, $password = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!isset($paths->pages[$page])) + { + return ''; + } + + if(strlen($paths->pages[$page]['password']) == 40) + { + if(!$password || ( $password != $paths->pages[$page]['password'])) + { + return 'invalid_password'; + } + } + + if(!$session->get_permissions('view_source')) // Dependencies handle this for us - this also checks for read privileges + return 'access_denied'; + $pid = RenderMan::strToPageID($page); + if($pid[1] == 'Special' || $pid[1] == 'Admin') + { + die('This type of page ('.$paths->nslist[$pid[1]].') cannot be edited because the page source code is not stored in the database.'); + } + + $e = $db->sql_query('SELECT page_text,char_tag FROM '.table_prefix.'page_text WHERE page_id=\''.$pid[0].'\' AND namespace=\''.$pid[1].'\''); + if ( !$e ) + { + $db->_die('The page text could not be selected.'); + } + if( $db->numrows() < 1 ) + { + return ''; //$db->_die('There were no rows in the text table that matched the page text query.'); + } + + $r = $db->fetchrow(); + $db->free_result(); + $message = $r['page_text']; + + return htmlspecialchars($message); + } + + /** + * Basically a frontend to RenderMan::getPage(), with the ability to send valid data for nonexistent pages + * @param $page the full page id (Namespace:Pagename) + * @param $send_headers true if the theme headers should be sent (still dependent on current page settings), false otherwise + * @return string + */ + + function getpage($page, $send_headers = false, $hist_id = false) + { + die('PageUtils->getpage is deprecated.'); + global $db, $session, $paths, $template, $plugins; // Common objects + ob_start(); + $pid = RenderMan::strToPageID($page); + //die('
'.print_r($pid, true).'
'); + if(isset($paths->pages[$page]['password']) && strlen($paths->pages[$page]['password']) == 40) + { + password_prompt($page); + } + if(isset($paths->pages[$page])) + { + doStats($pid[0], $pid[1]); + } + if($paths->custom_page || $pid[1] == 'Special') + { + // If we don't have access to the page, get out and quick! + if(!$session->get_permissions('read') && $pid[0] != 'Login' && $pid[0] != 'Register') + { + $template->tpl_strings['PAGE_NAME'] = 'Access denied'; + + if ( $send_headers ) + { + $template->header(); + } + + echo '
Access to this page is denied.
This may be because you are not logged in or you have not met certain criteria for viewing this page.
'; + + if ( $send_headers ) + { + $template->footer(); + } + + $r = ob_get_contents(); + ob_end_clean(); + return $r; + } + + $fname = 'page_'.$pid[1].'_'.$paths->pages[$page]['urlname_nons']; + @call_user_func($fname); + + } + else if ( $pid[1] == 'Admin' ) + { + // If we don't have access to the page, get out and quick! + if(!$session->get_permissions('read')) + { + $template->tpl_strings['PAGE_NAME'] = 'Access denied'; + if ( $send_headers ) + { + $template->header(); + } + echo '
Access to this page is denied.
This may be because you are not logged in or you have not met certain criteria for viewing this page.
'; + if ( $send_headers ) + { + $template->footer(); + } + $r = ob_get_contents(); + ob_end_clean(); + return $r; + } + + $fname = 'page_'.$pid[1].'_'.$pid[0]; + if ( !function_exists($fname) ) + { + $title = 'Page backend not found'; + $message = "The administration page you are looking for was properly registered using the page API, but the backend function + ($fname) was not found. If this is a plugin page, then this is almost certainly a bug with the plugin."; + if ( $send_headers ) + { + die_friendly($title, "

$message

"); + } + else + { + echo "

$title

\n

$message

"; + } + } + @call_user_func($fname); + } + else if ( !isset( $paths->pages[$page] ) ) + { + ob_start(); + $code = $plugins->setHook('page_not_found'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + $text = ob_get_contents(); + if ( $text != '' ) + { + ob_end_clean(); + return $text; + } + $template->header(); + if($m = $paths->sysmsg('Page_not_found')) + { + eval('?>'.RenderMan::render($m)); + } + else + { + header('HTTP/1.1 404 Not Found'); + echo '

There is no page with this title yet.

+

You have requested a page that doesn\'t exist yet.'; + if($session->get_permissions('create_page')) echo ' You can create this page, or return to the homepage.'; + else echo ' Return to the homepage.

'; + if($session->get_permissions('history_rollback')) { + $e = $db->sql_query('SELECT * FROM '.table_prefix.'logs WHERE action=\'delete\' AND page_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\''.$pid[1].'\' ORDER BY time_id DESC;'); + if(!$e) $db->_die('The deletion log could not be selected.'); + if($db->numrows() > 0) { + $r = $db->fetchrow(); + echo '

This page also appears to have some log entries in the database - it seems that it was deleted on '.$r['date_string'].'. You can probably roll back the deletion.

'; + } + $db->free_result(); + } + echo '

+ HTTP Error: 404 Not Found +

'; + } + $template->footer(); + } + else + { + + // If we don't have access to the page, get out and quick! + if(!$session->get_permissions('read')) + { + $template->tpl_strings['PAGE_NAME'] = 'Access denied'; + if($send_headers) $template->header(); + echo '
Access to this page is denied.
This may be because you are not logged in or you have not met certain criteria for viewing this page.
'; + if($send_headers) $template->footer(); + $r = ob_get_contents(); + ob_end_clean(); + return $r; + } + + ob_start(); + $code = $plugins->setHook('page_custom_handler'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + $text = ob_get_contents(); + if ( $text != '' ) + { + ob_end_clean(); + return $text; + } + + if($hist_id) { + $e = $db->sql_query('SELECT page_text,date_string,char_tag FROM '.table_prefix.'logs WHERE page_id=\''.$paths->pages[$page]['urlname_nons'].'\' AND namespace=\''.$pid[1].'\' AND log_type=\'page\' AND action=\'edit\' AND time_id='.$db->escape($hist_id).''); + if($db->numrows() < 1) + { + $db->_die('There were no rows in the text table that matched the page text query.'); + } + $r = $db->fetchrow(); + $db->free_result(); + $message = '
Notice:
The page you are viewing was archived on '.$r['date_string'].'.
View current version | Restore this version

'.RenderMan::render($r['page_text']); + + if( !$paths->pages[$page]['special'] ) + { + if($send_headers) + { + $template->header(); + } + display_page_headers(); + } + + eval('?>'.$message); + + if( !$paths->pages[$page]['special'] ) + { + display_page_footers(); + if($send_headers) + { + $template->footer(); + } + } + + } else { + if(!$paths->pages[$page]['special']) + { + $message = RenderMan::getPage($paths->pages[$page]['urlname_nons'], $pid[1]); + } + else + { + $message = RenderMan::getPage($paths->pages[$page]['urlname_nons'], $pid[1], 0, false, false, false, false); + } + // This line is used to debug wikiformatted code + // die('
'.htmlspecialchars($message).'
'); + + if( !$paths->pages[$page]['special'] ) + { + if($send_headers) + { + $template->header(); + } + display_page_headers(); + } + + // This is it, this is what all of Enano has been working up to... + + eval('?>'.$message); + + if( !$paths->pages[$page]['special'] ) + { + display_page_footers(); + if($send_headers) + { + $template->footer(); + } + } + } + } + $ret = ob_get_contents(); + ob_end_clean(); + return $ret; + } + + /** + * Writes page data to the database, after verifying permissions and running the XSS filter + * @param $page_id the page ID + * @param $namespace the namespace + * @param $message the text to save + * @return string + */ + + function savepage($page_id, $namespace, $message, $summary = 'No edit summary given', $minor = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $uid = sha1(microtime()); + $pname = $paths->nslist[$namespace] . $page_id; + + if(!$session->get_permissions('edit_page')) + return 'Access to edit pages is denied.'; + + if(!isset($paths->pages[$pname])) + { + if(!PageUtils::createPage($page_id, $namespace)) + return 'The page did not exist, and I was not able to create it. Permissions problem?'; + } + + $prot = ( ( $paths->pages[$pname]['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $paths->pages[$pname]['protected'] == 1) ? true : false; + $wiki = ( ( $paths->pages[$pname]['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $paths->pages[$pname]['wiki_mode'] == 1) ? true : false; + if(($prot || !$wiki) && $session->user_level < USER_LEVEL_ADMIN ) return('You are not authorized to edit this page.'); + + // Strip potentially harmful tags and PHP from the message, if we are in wiki mode and the user is not an administrator + $message = RenderMan::preprocess_text($message, false, false); + + $msg=$db->escape($message); + + $minor = $minor ? 'true' : 'false'; + $q='INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,page_id,namespace,page_text,char_tag,author,edit_summary,minor_edit) VALUES(\'page\', \'edit\', '.time().', \''.date('d M Y h:i a').'\', \''.$paths->cpage['urlname_nons'].'\', \''.$paths->namespace.'\', \''.$msg.'\', \''.$uid.'\', \''.$session->username.'\', \''.$db->escape(htmlspecialchars($summary)).'\', '.$minor.');'; + if(!$db->sql_query($q)) $db->_die('The history (log) entry could not be inserted into the logs table.'); + + $q = 'UPDATE '.table_prefix.'page_text SET page_text=\''.$msg.'\',char_tag=\''.$uid.'\' WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'; + $e = $db->sql_query($q); + if(!$e) $db->_die('Enano was unable to save the page contents. Your changes have been lost :\'(.'); + + $paths->rebuild_page_index($page_id, $namespace); + + return 'good'; + } + + /** + * Creates a page, both in memory and in the database. + * @param string $page_id + * @param string $namespace + * @return bool true on success, false on failure + */ + + function createPage($page_id, $namespace, $name = false, $visible = 1) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(in_array($namespace, Array('Special', 'Admin'))) + { + // echo 'Notice: PageUtils::createPage: You can\'t create a special page in the database
'; + return false; // Can't create a special page + } + + if(!isset($paths->nslist[$namespace])) + { + // echo 'Notice: PageUtils::createPage: Couldn\'t look up the namespace
'; + return false; // Couldn't look up namespace + } + + $pname = $paths->nslist[$namespace] . $page_id; + if(isset($paths->pages[$pname])) + { + // echo 'Notice: PageUtils::createPage: Page already exists
'; + return false; // Page already exists + } + + if(!$session->get_permissions('create_page')) + { + // echo 'Notice: PageUtils::createPage: Not authorized to create pages
'; + return false; // Access denied + } + + if($session->user_level < USER_LEVEL_ADMIN && $namespace == 'System') + { + // echo 'Notice: PageUtils::createPage: Not authorized to create system messages
'; + return false; // Not authorized to create system messages + } + + if ( !$name ) + $name = str_replace('_', ' ', $page_id); + $page = str_replace(' ', '_', $page_id); + $regex = '#^([A-z0-9 _\-\.\/\!\@\(\)]*)$#is'; + if(!preg_match($regex, $page)) + { + //echo 'Notice: PageUtils::createPage: Name contains invalid characters
'; + return false; // Name contains invalid characters + } + + $prot = ( $namespace == 'System' ) ? 1 : 0; + + $paths->add_page(Array( + 'name'=>$name, + 'urlname'=>$page, + 'namespace'=>$namespace, + 'special'=>0,'visible'=>1,'comments_on'=>0,'protected'=>$prot,'delvotes'=>0,'delvote_ips'=>'','wiki_mode'=>2, + )); + + $qa = $db->sql_query('INSERT INTO '.table_prefix.'pages(name,urlname,namespace,visible,protected) VALUES(\''.$db->escape($name).'\', \''.$db->escape($page).'\', \''.$namespace.'\', '. ( $visible ? '1' : '0' ) .', '.$prot.');'); + $qb = $db->sql_query('INSERT INTO '.table_prefix.'page_text(page_id,namespace) VALUES(\''.$db->escape($page).'\', \''.$namespace.'\');'); + $qc = $db->sql_query('INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,author,page_id,namespace) VALUES('.time().', \''.date('d M Y h:i a').'\', \'page\', \'create\', \''.$session->username.'\', \''.$db->escape($page).'\', \''.$namespace.'\');'); + + if($qa && $qb && $qc) + return true; + else + { + echo $db->get_error(); + return false; + } + } + + /** + * Sets the protection level on a page. + * @param $page_id string the page ID + * @param $namespace string the namespace + * @param $level int level of protection - 0 is off, 1 is full, 2 is semi + * @param $reason string why the page is being (un)protected + * @return string - "good" on success, in all other cases, an error string (on query failure, calls $db->_die() ) + */ + function protect($page_id, $namespace, $level, $reason) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $pname = $paths->nslist[$namespace] . $page_id; + $wiki = ( ( $paths->pages[$pname]['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $paths->pages[$pname]['wiki_mode'] == 1) ? true : false; + $prot = ( ( $paths->pages[$pname]['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $paths->pages[$pname]['protected'] == 1) ? true : false; + + if(!$session->get_permissions('protect')) return('Insufficient access rights'); + if(!$wiki) return('Page protection only has an effect when Wiki Mode is enabled.'); + if(!preg_match('#^([0-9]+){1}$#', (string)$level)) return('Invalid $level parameter.'); + + if($reason!='NO_REASON') { + switch($level) + { + case 0: + $q = 'INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,author,page_id,namespace,edit_summary) VALUES('.time().', \''.date('d M Y h:i a').'\', \'page\', \'unprot\', \''.$session->username.'\', \''.$page_id.'\', \''.$namespace.'\', \''.$db->escape(htmlspecialchars($reason)).'\');'; + break; + case 1: + $q = 'INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,author,page_id,namespace,edit_summary) VALUES('.time().', \''.date('d M Y h:i a').'\', \'page\', \'prot\', \''.$session->username.'\', \''.$page_id.'\', \''.$namespace.'\', \''.$db->escape(htmlspecialchars($reason)).'\');'; + break; + case 2: + $q = 'INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,author,page_id,namespace,edit_summary) VALUES('.time().', \''.date('d M Y h:i a').'\', \'page\', \'semiprot\', \''.$session->username.'\', \''.$page_id.'\', \''.$namespace.'\', \''.$db->escape(htmlspecialchars($reason)).'\');'; + break; + default: + return 'PageUtils::protect(): Invalid value for $level'; + break; + } + if(!$db->sql_query($q)) $db->_die('The log entry for the page protection could not be inserted.'); + } + + $q = $db->sql_query('UPDATE '.table_prefix.'pages SET protected='.$_POST['level'].' WHERE urlname=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$q) $db->_die('The pages table was not updated.'); + + return('good'); + } + + /** + * Generates an HTML table with history information in it. + * @param $page_id the page ID + * @param $namespace the namespace + * @return string + */ + + function histlist($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + if(!$session->get_permissions('history_view')) + return 'Access denied'; + + ob_start(); + + $pname = $paths->nslist[$namespace] . $page_id; + $wiki = ( ( $paths->pages[$pname]['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $paths->pages[$pname]['wiki_mode'] == 1) ? true : false; + $prot = ( ( $paths->pages[$pname]['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $paths->pages[$pname]['protected'] == 1) ? true : false; + + $q = 'SELECT time_id,date_string,page_id,namespace,author,edit_summary,minor_edit FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' ORDER BY time_id DESC;'; + if(!$db->sql_query($q)) $db->_die('The history data for the page "'.$paths->cpage['name'].'" could not be selected.'); + echo 'History of edits and actions

Edits:

'; + $numrows = $db->numrows(); + if($numrows < 1) echo 'No history entries in this category.'; + else + { + + echo ' + +
  +
+ + + + + + + + + '."\n"."\n"; + $cls = 'row2'; + $ticker = 0; + + while($r = $db->fetchrow()) { + + $ticker++; + + if($cls == 'row2') $cls = 'row1'; + else $cls = 'row2'; + + echo ''."\n"; + + // Diff selection + if($ticker == 1) + { + $s1 = ''; + $s2 = 'checked="checked" '; + } + elseif($ticker == 2) + { + $s1 = 'checked="checked" '; + $s2 = ''; + } + else + { + $s1 = ''; + $s2 = ''; + } + if($ticker > 1) echo ''."\n"; else echo ''; + if($ticker < $numrows) echo ''."\n"; else echo ''; + + // Date and time + echo ''."\n"; + + // User + if($session->get_permissions('mod_misc') && preg_match('#^([0-9]*){1,3}\.([0-9]*){1,3}\.([0-9]*){1,3}\.([0-9]*){1,3}$#', $r['author'])) $rc = ' style="cursor: pointer;" title="Click cell background for reverse DNS info" onclick="ajaxReverseDNS(this, \''.$r['author'].'\');"'; + else $rc = ''; + echo ''."\n"; + + // Edit summary + echo ''."\n"; + + // Minor edit + echo ''."\n"; + + // Actions! + echo ''."\n"; + echo ''."\n"; + echo ''."\n"; + + echo ''."\n"."\n"; + + } + echo '
DiffDate/timeUserEdit summaryMinorActions
'.$r['date_string'].'nslist['User'] . $r['author'])) echo 'class="wikilink-nonexistent"'; + echo '>'.$r['author'].''.$r['edit_summary'].''. (( $r['minor_edit'] ) ? 'M' : '' ) .'View revisionView user contribsRevert to this revision
+
+
+ + + + '; + } + $db->free_result(); + echo '

Other changes:

'; + $q = 'SELECT time_id,action,date_string,page_id,namespace,author,edit_summary,minor_edit FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action!=\'edit\' AND page_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\''.$paths->namespace.'\' ORDER BY time_id DESC;'; + if(!$db->sql_query($q)) $db->_die('The history data for the page "'.$paths->cpage['name'].'" could not be selected.'); + if($db->numrows() < 1) echo 'No history entries in this category.'; + else { + + echo '
'; + $cls = 'row2'; + while($r = $db->fetchrow()) { + + if($cls == 'row2') $cls = 'row1'; + else $cls = 'row2'; + + echo ''; + + // Date and time + echo ''; + + // User + echo ''; + + + // Minor edit + echo ''; + + // Action taken + echo ''; + + // Actions! + echo ''; + echo ''; + + //echo '(rollback) '.$r['date_string'].' '.$r['author'].' (Userpage, Contrib): '; + + if($r['minor_edit']) echo ' - minor edit'; + echo '
'; + + echo ''; + } + echo '
Date/timeUserMinorAction takenExtra info
'.$r['date_string'].'nslist['User'] . $r['author'])) echo 'class="wikilink-nonexistent"'; + echo '>'.$r['author'].''. (( $r['minor_edit'] ) ? 'M' : '' ) .''; + if ($r['action']=='prot') echo 'Protected pageReason: '.$r['edit_summary']; + elseif($r['action']=='unprot') echo 'Unprotected pageReason: '.$r['edit_summary']; + elseif($r['action']=='semiprot') echo 'Semi-protected pageReason: '.$r['edit_summary']; + elseif($r['action']=='rename') echo 'Renamed pageOld title: '.$r['edit_summary']; + elseif($r['action']=='create') echo 'Created page'; + elseif($r['action']=='delete') echo 'Deleted page'; + elseif($r['action']=='reupload') echo 'Uploaded new file versionReason: '.$r['edit_summary']; + echo 'View user contribsRevert action
'; + } + $db->free_result(); + $ret = ob_get_contents(); + ob_end_clean(); + return $ret; + } + + /** + * Rolls back a logged action + * @param $id the time ID, a.k.a. the primary key in the logs table + * @return string + */ + + function rollback($id) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('history_rollback')) return('You are not authorized to perform rollbacks.'); + if(!preg_match('#^([0-9]+)$#', (string)$id)) return('The value "id" on the query string must be an integer.'); + $e = $db->sql_query('SELECT log_type,action,date_string,page_id,namespace,page_text,char_tag,author,edit_summary FROM '.table_prefix.'logs WHERE time_id='.$id.';'); + if(!$e) $db->_die('The rollback data could not be selected.'); + $rb = $db->fetchrow(); + $db->free_result(); + switch($rb['log_type']) { + case "page": + switch($rb['action']) { + case "edit": + $t = $db->escape($rb['page_text']); + $e = $db->sql_query('UPDATE '.table_prefix.'page_text SET page_text=\''.$t.'\',char_tag=\''.$rb['char_tag'].'\' WHERE page_id=\''.$rb['page_id'].'\' AND namespace=\''.$rb['namespace'].'\''); + if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + else return('The page "'.$paths->pages[$paths->nslist[$rb['namespace']].$rb['page_id']]['name'].'" has been rolled back to the state it was in on '.$rb['date_string'].'.'); + break; + case "rename": + $t = $db->escape($rb['edit_summary']); + $e = $db->sql_query('UPDATE '.table_prefix.'pages SET name=\''.$t.'\' WHERE urlname=\''.$rb['page_id'].'\' AND namespace=\''.$rb['namespace'].'\''); + if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + else return('The page "'.$paths->pages[$paths->nslist[$rb['namespace']].$rb['page_id']]['name'].'" has been rolled back to the name it had ("'.$rb['edit_summary'].'") before '.$rb['date_string'].'.'); + break; + case "prot": + $e = $db->sql_query('UPDATE '.table_prefix.'pages SET protected=0 WHERE urlname=\''.$rb['page_id'].'\' AND namespace=\''.$rb['namespace'].'\''); + if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + else return('The page "'.$paths->pages[$paths->nslist[$rb['namespace']].$rb['page_id']]['name'].'" has been unprotected according to the log created at '.$rb['date_string'].'.'); + break; + case "semiprot": + $e = $db->sql_query('UPDATE '.table_prefix.'pages SET protected=0 WHERE urlname=\''.$rb['page_id'].'\' AND namespace=\''.$rb['namespace'].'\''); + if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + else return('The page "'.$paths->pages[$paths->nslist[$rb['namespace']].$rb['page_id']]['name'].'" has been unprotected according to the log created at '.$rb['date_string'].'.'); + break; + case "unprot": + $e = $db->sql_query('UPDATE '.table_prefix.'pages SET protected=1 WHERE urlname=\''.$rb['page_id'].'\' AND namespace=\''.$rb['namespace'].'\''); + if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + else return('The page "'.$paths->pages[$paths->nslist[$rb['namespace']].$rb['page_id']]['name'].'" has been protected according to the log created at '.$rb['date_string'].'.'); + break; + case "delete": + if(!$session->get_permissions('history_rollback_extra')) return('Administrative privileges are required for page undeletion.'); + if(isset($paths->pages[$paths->cpage['urlname']])) return('You cannot raise a dead page that is alive.'); + $name = str_replace('_', ' ', $rb['page_id']); + $e = $db->sql_query('INSERT INTO '.table_prefix.'pages(name,urlname,namespace) VALUES( \''.$name.'\', \''.$rb['page_id'].'\',\''.$rb['namespace'].'\' )');if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + $e = $db->sql_query('SELECT page_text,char_tag FROM '.table_prefix.'logs WHERE page_id=\''.$rb['page_id'].'\' AND namespace=\''.$rb['namespace'].'\' AND log_type=\'page\' AND action=\'edit\' ORDER BY time_id DESC;'); if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + $r = $db->fetchrow(); + $e = $db->sql_query('INSERT INTO '.table_prefix.'page_text(page_id,namespace,page_text,char_tag) VALUES(\''.$rb['page_id'].'\',\''.$rb['namespace'].'\',\''.$db->escape($r['page_text']).'\',\''.$r['char_tag'].'\')'); if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + return('The page "'.$name.'" has been undeleted according to the log created at '.$rb['date_string'].'.'); + break; + case "reupload": + if(!$session->get_permissions('history_rollbacks_extra')) return('Administrative privileges are required for file rollbacks.'); + $newtime = time(); + $newdate = date('d M Y h:i a'); + if(!$db->sql_query('UPDATE '.table_prefix.'logs SET time_id='.$newtime.',date_string=\''.$newdate.'\' WHERE time_id='.$id)) return('Error during query: '.mysql_error()); + if(!$db->sql_query('UPDATE '.table_prefix.'files SET time_id='.$newtime.' WHERE time_id='.$id)) return('Error during query: '.mysql_error()); + return('The file has been rolled back to the version uploaded on '.date('d M Y h:i a', (int)$id).'.'); + break; + default: + return('Rollback of the action "'.$rb['action'].'" is not yet supported.'); + break; + } + break; + case "security": + case "login": + return('A '.$rb['log_type'].'-related log entry cannot be rolled back.'); + break; + default: + return('Unknown log entry type: "'.$rb['log_type'].'"'); + } + } + + /** + * Posts a comment. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $name the name of the person posting, defaults to current username/IP + * @param $subject the subject line of the comment + * @param $text the comment text + * @return string javascript code + */ + + function addcomment($page_id, $namespace, $name, $subject, $text, $captcha_code = false, $captcha_id = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $_ob = ''; + if(!$session->get_permissions('post_comments')) + return 'Access denied'; + if(getConfig('comments_need_login') == '2' && !$session->user_logged_in) _die('Access denied to post comments: you need to be logged in first.'); + if(getConfig('comments_need_login') == '1' && !$session->user_logged_in) + { + if(!$captcha_code || !$captcha_id) _die('BUG: PageUtils::addcomment: no CAPTCHA data passed to method'); + $result = $session->get_captcha($captcha_id); + if($captcha_code != $result) _die('The confirmation code you entered was incorrect.'); + } + $text = RenderMan::preprocess_text($text); + $name = $session->user_logged_in ? RenderMan::preprocess_text($session->username) : RenderMan::preprocess_text($name); + $subj = RenderMan::preprocess_text($subject); + if(getConfig('approve_comments')=='1') $appr = '0'; else $appr = '1'; + $q = 'INSERT INTO '.table_prefix.'comments(page_id,namespace,subject,comment_data,name,user_id,approved,time) VALUES(\''.$page_id.'\',\''.$namespace.'\',\''.$subj.'\',\''.$text.'\',\''.$name.'\','.$session->user_id.','.$appr.','.time().')'; + $e = $db->sql_query($q); + if(!$e) die('alert(unescape(\''.rawurlencode('Error inserting comment data: '.mysql_error().'\n\nQuery:\n'.$q).'\'))'); + else $_ob .= '
Your comment has been posted.
'; + return PageUtils::comments($page_id, $namespace, false, Array(), $_ob); + } + + /** + * Generates partly-compiled HTML/Javascript code to be eval'ed by the user's browser to display comments + * @param $page_id the page ID + * @param $namespace the namespace + * @param $action administrative action to perform, default is false + * @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc. + * @param $_ob text to prepend to output, used by PageUtils::addcomment + * @return array + * @access private + */ + + function comments_raw($page_id, $namespace, $action = false, $flags = Array(), $_ob = '') + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $pname = $paths->nslist[$namespace] . $page_id; + + ob_start(); + + if($action && $session->get_permissions('mod_comments')) // Nip hacking attempts in the bud + { + switch($action) { + case "delete": + if(isset($flags['id'])) + { + $q = 'DELETE FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND comment_id='.intval($flags['id']).' LIMIT 1;'; + } else { + $n = $db->escape($flags['name']); + $s = $db->escape($flags['subj']); + $t = $db->escape($flags['text']); + $q = 'DELETE FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND name=\''.$n.'\' AND subject=\''.$s.'\' AND comment_data=\''.$t.'\' LIMIT 1;'; + } + $e=$db->sql_query($q); + if(!$e) die('alert(unesape(\''.rawurlencode('Error during query: '.mysql_error().'\n\nQuery:\n'.$q).'\'));'); + break; + case "approve": + if(isset($flags['id'])) + { + $where = 'comment_id='.intval($flags['id']); + } else { + $n = $db->escape($flags['name']); + $s = $db->escape($flags['subj']); + $t = $db->escape($flags['text']); + $where = 'name=\''.$n.'\' AND subject=\''.$s.'\' AND comment_data=\''.$t.'\''; + } + $q = 'SELECT approved FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND '.$where.' LIMIT 1;'; + $e = $db->sql_query($q); + if(!$e) die('alert(unesape(\''.rawurlencode('Error selecting approval status: '.mysql_error().'\n\nQuery:\n'.$q).'\'));'); + $r = $db->fetchrow(); + $db->free_result(); + $a = ( $r['approved'] ) ? '0' : '1'; + $q = 'UPDATE '.table_prefix.'comments SET approved='.$a.' WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND '.$where.';'; + $e=$db->sql_query($q); + if(!$e) die('alert(unesape(\''.rawurlencode('Error during query: '.mysql_error().'\n\nQuery:\n'.$q).'\'));'); + if($a=='1') $v = 'Unapprove'; + else $v = 'Approve'; + echo 'document.getElementById("mdgApproveLink'.$_GET['id'].'").innerHTML="'.$v.'";'; + break; + } + } + + if(!defined('ENANO_TEMPLATE_LOADED')) + { + $template->load_theme($session->theme, $session->style); + } + + $tpl = $template->makeParser('comment.tpl'); + + $e = $db->sql_query('SELECT * FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND approved=0;'); + if(!$e) $db->_die('The comment text data could not be selected.'); + $num_unapp = $db->numrows(); + $db->free_result(); + $e = $db->sql_query('SELECT * FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND approved=1;'); + if(!$e) $db->_die('The comment text data could not be selected.'); + $num_app = $db->numrows(); + $db->free_result(); + $lq = $db->sql_query('SELECT c.comment_id,c.subject,c.name,c.comment_data,c.approved,c.time,c.user_id,u.user_level,u.signature + FROM '.table_prefix.'comments AS c + LEFT JOIN '.table_prefix.'users AS u + ON c.user_id=u.user_id + WHERE page_id=\''.$page_id.'\' + AND namespace=\''.$namespace.'\' ORDER BY c.time ASC;'); + if(!$lq) _die('The comment text data could not be selected. '.mysql_error()); + $_ob .= '

Article Comments

'; + $n = ( $session->get_permissions('mod_comments')) ? $db->numrows() : $num_app; + if($n==1) $s = 'is '.$n.' comment'; else $s = 'are '.$n.' comments'; + if($n < 1) + { + $_ob .= '

There are currently no comments on this '.strtolower($namespace).''; + if($namespace != 'Article') $_ob .= ' page'; + $_ob .= '.

'; + } else $_ob .= '

There '.$s.' on this article.

'; + if($session->get_permissions('mod_comments') && $num_unapp > 0) $_ob .= ' '.$num_unapp.' of those are unapproved.'; + elseif(!$session->get_permissions('mod_comments') && $num_unapp > 0) { $u = ($num_unapp == 1) ? "is $num_unapp comment" : "are $num_unapp comments"; $_ob .= ' However, there ' . $u . ' awating approval.'; } + $list = 'list = { '; + // _die(htmlspecialchars($ttext)); + $i = -1; + while($row = $db->fetchrow($lq)) + { + $i++; + $strings = Array(); + $bool = Array(); + if($session->get_permissions('mod_comments') || $row['approved']) { + $list .= $i . ' : { \'comment\' : unescape(\''.rawurlencode($row['comment_data']).'\'), \'name\' : unescape(\''.rawurlencode($row['name']).'\'), \'subject\' : unescape(\''.rawurlencode($row['subject']).'\'), }, '; + + // Comment ID (used in the Javascript apps) + $strings['ID'] = (string)$i; + + // Determine the name, and whether to link to the user page or not + $name = ''; + if($row['user_id'] > 0) $name .= ''; + $name .= $row['name']; + if($row['user_id'] > 0) $name .= ''; + $strings['NAME'] = $name; unset($name); + + // Subject + $s = $row['subject']; + if(!$row['approved']) $s .= ' (Unapproved)'; + $strings['SUBJECT'] = $s; + + // Date and time + $strings['DATETIME'] = date('F d, Y h:i a', $row['time']); + + // User level + switch($row['user_level']) + { + default: + case USER_LEVEL_GUEST: + $l = 'Guest'; + break; + case USER_LEVEL_MEMBER: + $l = 'Member'; + break; + case USER_LEVEL_MOD: + $l = 'Moderator'; + break; + case USER_LEVEL_ADMIN: + $l = 'Administrator'; + break; + } + $strings['USER_LEVEL'] = $l; unset($l); + + // The actual comment data + $strings['DATA'] = RenderMan::render($row['comment_data']); + + if($session->get_permissions('edit_comments')) + { + // Edit link + $strings['EDIT_LINK'] = 'edit'; + + // Delete link + $strings['DELETE_LINK'] = 'delete'; + } + else + { + // Edit link + $strings['EDIT_LINK'] = ''; + + // Delete link + $strings['DELETE_LINK'] = ''; + } + + // Send PM link + $strings['SEND_PM_LINK'] = ( $session->user_logged_in && $row['user_id'] > 0 ) ? 'Send private message
' : ''; + + // Add Buddy link + $strings['ADD_BUDDY_LINK'] = ( $session->user_logged_in && $row['user_id'] > 0 ) ? 'Add to buddy list' : ''; + + // Mod links + $applink = ''; + $applink .= ''; + if($row['approved']) $applink .= 'Unapprove'; + else $applink .= 'Approve'; + $applink .= ''; + $strings['MOD_APPROVE_LINK'] = $applink; unset($applink); + $strings['MOD_DELETE_LINK'] = 'Delete'; + + // Signature + $strings['SIGNATURE'] = ''; + if($row['signature'] != '') $strings['SIGNATURE'] = RenderMan::render($row['signature']); + + $bool['auth_mod'] = ($session->get_permissions('mod_comments')) ? true : false; + $bool['can_edit'] = ( ( $session->user_logged_in && $row['name'] == $session->username && $session->get_permissions('edit_comments') ) || $session->get_permissions('mod_comments') ) ? true : false; + $bool['signature'] = ( $strings['SIGNATURE'] == '' ) ? false : true; + + // Done processing and compiling, now let's cook it into HTML + $tpl->assign_vars($strings); + $tpl->assign_bool($bool); + $_ob .= $tpl->run(); + } + } + if(getConfig('comments_need_login') != '2' || $session->user_logged_in) + { + if(!$session->get_permissions('post_comments')) + { + $_ob .= '

Got something to say?

Access to post comments on this page is denied.

'; + } + else + { + $_ob .= '

Got something to say?

If you have comments or suggestions on this article, you can shout it out here.'; + if(getConfig('approve_comments')=='1') $_ob .= ' Before your comment will be visible to the public, a moderator will have to approve it.'; + if(getConfig('comments_need_login') == '1' && !$session->user_logged_in) $_ob .= ' Because you are not logged in, you will need to enter a visual confirmation before your comment will be posted.'; + $sn = $session->user_logged_in ? $session->username . '' : ''; + $_ob .= ' +
+

Comment form

+
+ + + '; + if(getConfig('comments_need_login') == '1' && !$session->user_logged_in) + { + $session->kill_captcha(); + $captcha = $session->make_captcha(); + $_ob .= ''; + } + $_ob .= ' + + +
Your name or screen name:'.$sn.'
Comment subject:
Visual confirmation:
Please enter the code you see on the right.
Visual confirmation
Code:
Comment text:
(most HTML will be stripped)
+
+
'; + } + } else { + $_ob .= '

Got something to say?

You need to be logged in to post comments. Log in

'; + } + $list .= '};'; + echo 'document.getElementById(\'ajaxEditContainer\').innerHTML = unescape(\''. rawurlencode($_ob) .'\'); + ' . $list; + echo 'Fat.fade_all(); document.getElementById(\'mdgCommentForm\').style.display = \'none\'; document.getElementById(\'mdgCommentFormLink\').style.display="inline";'; + + $ret = ob_get_contents(); + ob_end_clean(); + return Array($ret, $_ob); + + } + + /** + * Generates ready-to-execute Javascript code to be eval'ed by the user's browser to display comments + * @param $page_id the page ID + * @param $namespace the namespace + * @param $action administrative action to perform, default is false + * @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc. + * @param $_ob text to prepend to output, used by PageUtils::addcomment + * @return string + */ + + function comments($page_id, $namespace, $action = false, $id = -1, $_ob = '') + { + global $db, $session, $paths, $template, $plugins; // Common objects + $r = PageUtils::comments_raw($page_id, $namespace, $action, $id, $_ob); + return $r[0]; + } + + /** + * Generates HTML code for comments - used in browser compatibility mode + * @param $page_id the page ID + * @param $namespace the namespace + * @param $action administrative action to perform, default is false + * @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc. + * @param $_ob text to prepend to output, used by PageUtils::addcomment + * @return string + */ + + function comments_html($page_id, $namespace, $action = false, $id = -1, $_ob = '') + { + global $db, $session, $paths, $template, $plugins; // Common objects + $r = PageUtils::comments_raw($page_id, $namespace, $action, $id, $_ob); + return $r[1]; + } + + /** + * Updates comment data. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $subject new subject + * @param $text new text + * @param $old_subject the old subject, unprocessed and identical to the value in the DB + * @param $old_text the old text, unprocessed and identical to the value in the DB + * @param $id the javascript list ID, used internally by the client-side app + * @return string + */ + + function savecomment($page_id, $namespace, $subject, $text, $old_subject, $old_text, $id = -1) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('edit_comments')) + return 'result="BAD";error="Access denied"'; + // Avoid SQL injection + $old_text = $db->escape($old_text); + $old_subject = $db->escape($old_subject); + // Safety check - username/login + if(!$session->get_permissions('mod_comments')) // allow mods to edit comments + { + if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.

Please log in and try again.'); + $q = 'SELECT c.name FROM '.table_prefix.'comments c, '.table_prefix.'users u WHERE comment_data=\''.$old_text.'\' AND subject=\''.$old_subject.'\' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND u.user_id=c.user_id;'; + $s = $db->sql_query($q); + if(!$s) _die('SQL error during safety check: '.mysql_error().'

Attempted SQL:
'.htmlspecialchars($q).'
'); + $r = $db->fetchrow($s); + $db->free_result(); + if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); + } + $s = RenderMan::preprocess_text($subject); + $t = RenderMan::preprocess_text($text); + $sql = 'UPDATE '.table_prefix.'comments SET subject=\''.$s.'\',comment_data=\''.$t.'\' WHERE comment_data=\''.$old_text.'\' AND subject=\''.$old_subject.'\' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\''; + $result = $db->sql_query($sql); + if($result) + { + return 'result="GOOD"; + list['.$id.'][\'subject\'] = unescape(\''.str_replace('%5Cn', '%0A', rawurlencode(str_replace('{{EnAnO:Newline}}', '\\n', stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $s))))).'\'); + list['.$id.'][\'comment\'] = unescape(\''.str_replace('%5Cn', '%0A', rawurlencode(str_replace('{{EnAnO:Newline}}', '\\n', stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $t))))).'\'); id = '.$id.'; + s = unescape(\''.rawurlencode($s).'\'); + t = unescape(\''.str_replace('%5Cn', '
', rawurlencode(RenderMan::render(str_replace('{{EnAnO:Newline}}', "\n", stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $t)))))).'\');'; + } + else + { + return 'result="BAD"; error=unescape("'.rawurlencode('Enano encountered a problem whilst saving the comment. + Performed SQL: + '.$sql.' + + Error returned by MySQL: '.mysql_error()).'");'; + } + } + + /** + * Updates comment data using the comment_id column instead of the old, messy way + * @param $page_id the page ID + * @param $namespace the namespace + * @param $subject new subject + * @param $text new text + * @param $id the comment ID (primary key in enano_comments table) + * @return string + */ + + function savecomment_neater($page_id, $namespace, $subject, $text, $id) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!is_int($id)) die('PageUtils::savecomment: $id is not an integer, aborting for safety'); + if(!$session->get_permissions('edit_comments')) + return 'Access denied'; + // Safety check - username/login + if(!$session->get_permissions('mod_comments')) // allow mods to edit comments + { + if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.

Please log in and try again.'); + $q = 'SELECT c.name FROM '.table_prefix.'comments c, '.table_prefix.'users u WHERE comment_id='.$id.' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND u.user_id=c.user_id;'; + $s = $db->sql_query($q); + if(!$s) _die('SQL error during safety check: '.mysql_error().'

Attempted SQL:
'.htmlspecialchars($q).'
'); + $r = $db->fetchrow($s); + if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); + $db->free_result(); + } + $s = RenderMan::preprocess_text($subject); + $t = RenderMan::preprocess_text($text); + $sql = 'UPDATE '.table_prefix.'comments SET subject=\''.$s.'\',comment_data=\''.$t.'\' WHERE comment_id='.$id.' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\''; + $result = $db->sql_query($sql); + if($result) + return 'good'; + else return 'Enano encountered a problem whilst saving the comment. + Performed SQL: + '.$sql.' + + Error returned by MySQL: '.mysql_error(); + } + + /** + * Deletes a comment. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $name the name the user posted under + * @param $subj the subject of the comment to be deleted + * @param $text the text of the comment to be deleted + * @param $id the javascript list ID, used internally by the client-side app + * @return string + */ + + function deletecomment($page_id, $namespace, $name, $subj, $text, $id) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + if(!$session->get_permissions('edit_comments')) + return 'alert("Access to delete/edit comments is denied");'; + + if(!preg_match('#^([0-9]+)$#', (string)$id)) die('$_GET[id] is improperly formed.'); + $n = $db->escape($name); + $s = $db->escape($subj); + $t = $db->escape($text); + + // Safety check - username/login + if(!$session->get_permissions('mod_comments')) // allows mods to delete comments + { + if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.

Please log in and try again.'); + $q = 'SELECT c.name FROM '.table_prefix.'comments c, '.table_prefix.'users u WHERE comment_data=\''.$t.'\' AND subject=\''.$s.'\' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND u.user_id=c.user_id;'; + $s = $db->sql_query($q); + if(!$s) _die('SQL error during safety check: '.mysql_error().'

Attempted SQL:
'.htmlspecialchars($q).'
'); + $r = $db->fetchrow($s); + if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); + $db->free_result(); + } + $q = 'DELETE FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND name=\''.$n.'\' AND subject=\''.$s.'\' AND comment_data=\''.$t.'\' LIMIT 1;'; + $e=$db->sql_query($q); + if(!$e) return('alert(unesape(\''.rawurlencode('Error during query: '.mysql_error().'\n\nQuery:\n'.$q).'\'));'); + return('good'); + } + + /** + * Deletes a comment in a cleaner fashion. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $id the comment ID (primary key) + * @return string + */ + + function deletecomment_neater($page_id, $namespace, $id) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + if(!preg_match('#^([0-9]+)$#', (string)$id)) die('$_GET[id] is improperly formed.'); + + if(!$session->get_permissions('edit_comments')) + return 'alert("Access to delete/edit comments is denied");'; + + // Safety check - username/login + if(!$session->get_permissions('mod_comments')) // allows mods to delete comments + { + if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.

Please log in and try again.'); + $q = 'SELECT c.name FROM '.table_prefix.'comments c, '.table_prefix.'users u WHERE comment_id='.$id.' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND u.user_id=c.user_id;'; + $s = $db->sql_query($q); + if(!$s) _die('SQL error during safety check: '.mysql_error().'

Attempted SQL:
'.htmlspecialchars($q).'
'); + $r = $db->fetchrow($s); + if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); + $db->free_result(); + } + $q = 'DELETE FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND comment_id='.$id.' LIMIT 1;'; + $e=$db->sql_query($q); + if(!$e) return('alert(unesape(\''.rawurlencode('Error during query: '.mysql_error().'\n\nQuery:\n'.$q).'\'));'); + return('good'); + } + + /** + * Renames a page. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $name the new name for the page + * @return string error string or success message + */ + + function rename($page_id, $namespace, $name) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $pname = $paths->nslist[$namespace] . $page_id; + + $prot = ( ( $paths->pages[$pname]['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $paths->pages[$pname]['protected'] == 1) ? true : false; + $wiki = ( ( $paths->pages[$pname]['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $paths->pages[$pname]['wiki_mode'] == 1) ? true : false; + + if( empty($name)) die('Name is too short'); + if( ( $session->get_permissions('rename') && ( ( $prot && $session->get_permissions('even_when_protected') ) || !$prot ) ) && ( $paths->namespace != 'Special' && $paths->namespace != 'Admin' )) { + $e = $db->sql_query('INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,page_id,namespace,author,edit_summary) VALUES('.time().', \''.date('d M Y h:i a').'\', \'page\', \'rename\', \''.$paths->cpage['urlname_nons'].'\', \''.$paths->namespace.'\', \''.$session->username.'\', \''.$paths->cpage['name'].'\')'); + if(!$e) $db->_die('The page title could not be updated.'); + $e = $db->sql_query('UPDATE '.table_prefix.'pages SET name=\''.$db->escape($name).'\' WHERE urlname=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$e) $db->_die('The page title could not be updated.'); + else return('The page "'.$paths->pages[$pname]['name'].'" has been renamed to "'.$name.'". You are encouraged to leave a comment explaining your action. + +You will see the change take effect the next time you reload this page.'); + } else { + return('Access is denied.'); + } + } + + /** + * Flushes (clears) the action logs for a given page + * @param $page_id the page ID + * @param $namespace the namespace + * @return string error/success string + */ + + function flushlogs($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('clear_logs')) die('Administrative privileges are required to flush logs, you loser.'); + $e = $db->sql_query('DELETE FROM '.table_prefix.'logs WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$e) $db->_die('The log entries could not be deleted.'); + $e = $db->sql_query('SELECT page_text,char_tag FROM '.table_prefix.'page_text WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$e) $db->_die('The current page text could not be selected; as a result, creating the backup of the page failed. Please make a backup copy of the page by clicking Edit this page and then clicking Save Changes.'); + $row = $db->fetchrow(); + $db->free_result(); + $q='INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,page_id,namespace,page_text,char_tag,author,edit_summary,minor_edit) VALUES(\'page\', \'edit\', '.time().', \''.date('d M Y h:i a').'\', \''.$page_id.'\', \''.$namespace.'\', \''.$db->escape($row['page_text']).'\', \''.$row['char_tag'].'\', \''.$session->username.'\', \''."Automatic backup created when logs were purged".'\', '.'false'.');'; + if(!$db->sql_query($q)) $db->_die('The history (log) entry could not be inserted into the logs table.'); + return('The logs for this page have been cleared. A backup of this page has been added to the logs table so that this page can be restored in case of vandalism or spam later.'); + } + + /** + * Deletes a page. + * @param $page_id the condemned page ID + * @param $namespace the condemned namespace + * @return string + */ + + function deletepage($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $perms = $session->fetch_page_acl($page_id, $namespace); + if(!$perms->get_permissions('delete_page')) die('Administrative privileges are required to delete pages, you loser.'); + $e = $db->sql_query('INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,page_id,namespace,author) VALUES('.time().', \''.date('d M Y h:i a').'\', \'page\', \'delete\', \''.$page_id.'\', \''.$namespace.'\', \''.$session->username.'\')'); + if(!$e) $db->_die('The page log entry could not be inserted.'); + $e = $db->sql_query('DELETE FROM '.table_prefix.'categories WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\''); + if(!$e) $db->_die('The page categorization entries could not be deleted.'); + $e = $db->sql_query('DELETE FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\''); + if(!$e) $db->_die('The page comments could not be deleted.'); + $e = $db->sql_query('DELETE FROM '.table_prefix.'page_text WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\''); + if(!$e) $db->_die('The page text entry could not be deleted.'); + $e = $db->sql_query('DELETE FROM '.table_prefix.'pages WHERE urlname=\''.$page_id.'\' AND namespace=\''.$namespace.'\''); + if(!$e) $db->_die('The page entry could not be deleted.'); + $e = $db->sql_query('DELETE FROM '.table_prefix.'files WHERE page_id=\''.$page_id.'\''); + if(!$e) $db->_die('The file entry could not be deleted.'); + return('This page has been deleted. Note that there is still a log of edits and actions in the database, and anyone with admin rights can raise this page from the dead unless the log is cleared. If the deleted file is an image, there may still be cached thumbnails of it in the cache/ directory, which is inaccessible to users.'); + } + + /** + * Increments the deletion votes for a page by 1, and adds the current username/IP to the list of users that have voted for the page to prevent dual-voting + * @param $page_id the page ID + * @param $namespace the namespace + * @return string + */ + + function delvote($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('vote_delete')) + return 'Access denied'; + $pname = $paths->nslist[$namespace] . $page_id; + $cv = $paths->pages[$pname]['delvotes']; + $ips = $paths->pages[$pname]['delvote_ips']; + $ips = explode('|', $ips); + if(in_array($_SERVER['REMOTE_ADDR'], $ips)) return('It appears that you have already voted to have this page deleted.'); + if($session->user_logged_in) + if(in_array($session->username, $ips)) + return('It appears that you have already voted to have this page deleted.'); + $ips[] = $_SERVER['REMOTE_ADDR']; + if($session->user_logged_in) $ips[] = $session->username; + $ips = implode('|', $ips); + $ips = substr($ips, 1, strlen($ips)); + $cv++; + $q = 'UPDATE '.table_prefix.'pages SET delvotes='.$cv.',delvote_ips=\''.$ips.'\' WHERE urlname=\''.$page_id.'\' AND namespace=\''.$namespace.'\''; + $w = $db->sql_query($q); + if(!$w) return("Error updating pages table: ".mysql_error()."\n\nAttemped SQL:\n".$q); + return('Your vote to have this page deleted has been cast.'."\nYou are encouraged to leave a comment explaining the reason for your vote."); + } + + /** + * Resets the number of votes against a page to 0. + * @param $page_id the page ID + * @param $namespace the namespace + * @return string + */ + + function resetdelvotes($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('vote_reset')) die('You need moderator rights in order to do this, stinkin\' hacker.'); + $q = 'UPDATE '.table_prefix.'pages SET delvotes=0,delvote_ips=\'\' WHERE urlname=\''.$page_id.'\' AND namespace=\''.$namespace.'\''; + $e = $db->sql_query($q); + if(!$e) $db->_die('The number of delete votes was not reset.'); + else return('The number of votes for having this page deleted has been reset to zero.'); + } + + /** + * Gets a list of styles for a given theme name. + * @param $id the name of the directory for the theme + * @return string Javascript code + */ + + function getstyles() + { + $dir = './themes/'.$_GET['id'].'/css/'; + $list = Array(); + // Open a known directory, and proceed to read its contents + if (is_dir($dir)) { + if ($dh = opendir($dir)) { + while (($file = readdir($dh)) !== false) { + if(preg_match('#^(.*?)\.css$#is', $file) && $file != '_printable.css') { // _printable.css should be included with every theme + // it should be a copy of the original style, but + // mostly black and white + // Note to self: document this + $list[] = substr($file, 0, strlen($file)-4); + } + } + closedir($dh); + } + } else return($dir.' is not a dir'); + $l = 'var list = new Array();'; + $i = -1; + foreach($list as $li) { + $i++; + $l .= "list[$i] = '$li';"; + } + return $l; + } + + /** + * Assembles a Javascript app with category information + * @param $page_id the page ID + * @param $namespace the namespace + * @return string Javascript code + */ + + function catedit($page_id, $namespace) + { + $d = PageUtils::catedit_raw($page_id, $namespace); + return $d[0] . ' /* BEGIN CONTENT */ document.getElementById("ajaxEditContainer").innerHTML = unescape(\''.rawurlencode($d[1]).'\');'; + } + + /** + * Does the actual HTML/javascript generation for cat editing, but returns an array + * @access private + */ + + function catedit_raw($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + ob_start(); + $_ob = ''; + $e = $db->sql_query('SELECT category_id FROM '.table_prefix.'categories WHERE page_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\''.$paths->namespace.'\''); + if(!$e) jsdie('Error selecting category information for current page: '.mysql_error()); + $cat_current = Array(); + while($r = $db->fetchrow()) + { + $cat_current[] = $r; + } + $db->free_result(); + $cat_all = Array(); + for($i=0;$ipages)/2;$i++) + { + if($paths->pages[$i]['namespace']=='Category') $cat_all[] = $paths->pages[$i]; + } + + // Make $cat_all an associative array, like $paths->pages + $sz = sizeof($cat_all); + for($i=0;$i<$sz;$i++) + { + $cat_all[$cat_all[$i]['urlname_nons']] = $cat_all[$i]; + } + // Now, the "zipper" function - join the list of categories with the list of cats that this page is a part of + $cat_info = $cat_all; + for($i=0;$i'; + if ( sizeof($cat_info) < 1 ) + { + $_ob .= '

There are no categories on this site yet.

'; + } + for ( $i = 0; $i < sizeof($cat_info) / 2; $i++ ) + { + // Protection code added 1/3/07 + // Updated 3/4/07 + $is_prot = false; + $perms = $session->fetch_page_acl($cat_info[$i]['urlname_nons'], 'Category'); + if ( !$session->get_permissions('edit_cat') || !$perms->get_permissions('edit_cat') || + ( $cat_info[$i]['really_protected'] && !$perms->get_permissions('even_when_protected') ) ) + $is_prot = true; + $prot = ( $is_prot ) ? ' disabled="disabled" ' : ''; + $prottext = ( $is_prot ) ? ' (protected)' : ''; + echo 'catlist['.$i.'] = \''.$cat_info[$i]['urlname_nons'].'\';'; + $_ob .= ''.$cat_info[$i]['name'].$prottext.'
'; + } + + $disabled = ( sizeof($cat_info) < 1 ) ? 'disabled="disabled"' : ''; + + $_ob .= '
'; + + $cont = ob_get_contents(); + ob_end_clean(); + return Array($cont, $_ob); + } + + /** + * Saves category information + * WARNING: If $which_cats is empty, all the category information for the selected page will be nuked! + * @param $page_id string the page ID + * @param $namespace string the namespace + * @param $which_cats array associative array of categories to put the page in + * @return string "GOOD" on success, error string on failure + */ + + function catsave($page_id, $namespace, $which_cats) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('edit_cat')) return('Insufficient privileges to change category information'); + + $page_perms = $session->fetch_page_acl($page_id, $namespace); + $page_data =& $paths->pages[$paths->nslist[$namespace].$page_id]; + + $cat_all = Array(); + for($i=0;$ipages)/2;$i++) + { + if($paths->pages[$i]['namespace']=='Category') $cat_all[] = $paths->pages[$i]; + } + + // Make $cat_all an associative array, like $paths->pages + $sz = sizeof($cat_all); + for($i=0;$i<$sz;$i++) + { + $cat_all[$cat_all[$i]['urlname_nons']] = $cat_all[$i]; + } + + $rowlist = Array(); + + for($i=0;$ifetch_page_acl($cat_all[$i]['urlname_nons'], 'Category'); + if ( !$session->get_permissions('edit_cat') || !$perms->get_permissions('edit_cat') || + ( $cat_all[$i]['really_protected'] && !$perms->get_permissions('even_when_protected') ) || + ( !$page_perms->get_permissions('even_when_protected') && $page_data['protected'] == '1' ) ) + $auth = false; + if(!$auth) + { + // Find out if the page is currently in the category + $q = $db->sql_query('SELECT * FROM '.table_prefix.'categories WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$q) + return 'MySQL error: '.$db->get_error(); + if($db->numrows() > 0) + { + $auth = true; + $which_cats[$cat_all[$i]['urlname_nons']] = true; // Force the category to stay in its current state + } + $db->free_result(); + } + if(isset($which_cats[$cat_all[$i]['urlname_nons']]) && $which_cats[$cat_all[$i]['urlname_nons']] == true /* for clarity ;-) */ && $auth ) $rowlist[] = '(\''.$page_id.'\', \''.$namespace.'\', \''.$cat_all[$i]['urlname_nons'].'\')'; + } + if(sizeof($rowlist) > 0) + { + $val = implode(',', $rowlist); + $q = 'INSERT INTO '.table_prefix.'categories(page_id,namespace,category_id) VALUES' . $val . ';'; + $e = $db->sql_query('DELETE FROM '.table_prefix.'categories WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$e) $db->_die('The old category data could not be deleted.'); + $e = $db->sql_query($q); + if(!$e) $db->_die('The new category data could not be inserted.'); + return('GOOD'); + } + else + { + $e = $db->sql_query('DELETE FROM '.table_prefix.'categories WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$e) $db->_die('The old category data could not be deleted.'); + return('GOOD'); + } + } + + /** + * Sets the wiki mode level for a page. + * @param $page_id string the page ID + * @param $namespace string the namespace + * @param $level int 0 for off, 1 for on, 2 for use global setting + * @return string "GOOD" on success, error string on failure + */ + + function setwikimode($page_id, $namespace, $level) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('set_wiki_mode')) return('Insufficient access rights'); + if(!isset($level) || (isset($level) && !preg_match('#^([0-2]){1}$#', (string)$level))) return('Invalid mode string'); + $q = $db->sql_query('UPDATE '.table_prefix.'pages SET wiki_mode='.$level.' WHERE urlname=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$q) return('Error during update query: '.mysql_error()."\n\nSQL Backtrace:\n".$db->sql_backtrace()); + return('GOOD'); + } + + /** + * Sets the access password for a page. + * @param $page_id string the page ID + * @param $namespace string the namespace + * @param $pass string the SHA1 hash of the password - if the password doesn't match the regex ^([0-9a-f]*){40,40}$ it will be sha1'ed + * @return string + */ + + function setpass($page_id, $namespace, $pass) + { + global $db, $session, $paths, $template, $plugins; // Common objects + // Determine permissions + if($paths->pages[$paths->nslist[$namespace].$page_id]['password'] != '') + $a = $session->get_permissions('password_reset'); + else + $a = $session->get_permissions('password_set'); + if(!$a) + return 'Access is denied'; + if(!isset($pass)) return('Password was not set on URL'); + $p = $pass; + if(!preg_match('#([0-9a-f]){40,40}#', $p)) $p = sha1($p); + if($p=='da39a3ee5e6b4b0d3255bfef95601890afd80709') $p = ''; + $e = $db->sql_query('UPDATE '.table_prefix.'pages SET password=\''.$p.'\' WHERE urlname=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$e) die('PageUtils::setpass(): Error during update query: '.mysql_error()."\n\nSQL Backtrace:\n".$db->sql_backtrace()); + if($p=='') return('The password for this page has been disabled.'); + else return('The password for this page has been set.'); + } + + /** + * Generates some preview HTML + * @param $text string the wikitext to use + * @return string + */ + + function genPreview($text) + { + return '
Reminder: This is only a preview - your changes to this page have not yet been saved.
'.RenderMan::render(RenderMan::preprocess_text($text, false, false)).'
'; + } + + /** + * Makes a scrollable box + * @param string $text the inner HTML + * @param int $height Optional - the maximum height. Defaults to 250. + * @return string + */ + + function scrollBox($text, $height = 250) + { + return '
'.$text.'
'; + } + + /** + * Generates a diff summary between two page revisions. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $id1 the time ID of the first revision + * @param $id2 the time ID of the second revision + * @return string XHTML-formatted diff + */ + + function pagediff($page_id, $namespace, $id1, $id2) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('history_view')) + return 'Access denied'; + if(!preg_match('#^([0-9]+)$#', (string)$id1) || + !preg_match('#^([0-9]+)$#', (string)$id2 )) return 'SQL injection attempt'; + // OK we made it through security + // Safest way to make sure we don't end up with the revisions in wrong columns is to make 2 queries + if(!$q1 = $db->sql_query('SELECT page_text,char_tag,author,edit_summary FROM '.table_prefix.'logs WHERE time_id='.$id1.' AND log_type=\'page\' AND action=\'edit\' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';')) return 'MySQL error: '.mysql_error(); + if(!$q2 = $db->sql_query('SELECT page_text,char_tag,author,edit_summary FROM '.table_prefix.'logs WHERE time_id='.$id2.' AND log_type=\'page\' AND action=\'edit\' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';')) return 'MySQL error: '.mysql_error(); + $row1 = $db->fetchrow($q1); + $db->free_result($q1); + $row2 = $db->fetchrow($q2); + $db->free_result($q2); + if(sizeof($row1) < 1 || sizeof($row2) < 2) return 'Couldn\'t find any rows that matched the query. The time ID probably doesn\'t exist in the logs table.'; + $text1 = $row1['page_text']; + $text2 = $row2['page_text']; + $time1 = date('F d, Y h:i a', $id1); + $time2 = date('F d, Y h:i a', $id2); + $_ob = " +

Comparing revisions: {$time1} → {$time2}

+ "; + // Free some memory + unset($row1, $row2, $q1, $q2); + + $_ob .= RenderMan::diff($text1, $text2); + return $_ob; + } + + /** + * Gets ACL information about the selected page for target type X and target ID Y. + * @param string $page_id The page ID + * @param string $namespace The namespace + * @param array $parms What to select. This is an array purely for JSON compatibility. It should be an associative array with keys target_type and target_id. + * @return array + */ + + function acl_editor($parms = Array()) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('edit_acl') && $session->user_level < USER_LEVEL_ADMIN) + return 'Access is denied.'; + $parms['page_id'] = ( isset($parms['page_id']) ) ? $parms['page_id'] : false; + $parms['namespace'] = ( isset($parms['namespace']) ) ? $parms['namespace'] : false; + $page_id =& $parms['page_id']; + $namespace =& $parms['namespace']; + $page_where_clause = ( empty($page_id) || empty($namespace) ) ? 'AND a.page_id IS NULL AND a.namespace IS NULL' : 'AND a.page_id=\''.$db->escape($page_id).'\' AND a.namespace=\''.$db->escape($namespace).'\''; + $page_where_clause_lite = ( empty($page_id) || empty($namespace) ) ? 'AND page_id IS NULL AND namespace IS NULL' : 'AND page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\''; + //die(print_r($page_id,true)); + $template->load_theme(); + // $perms_obj = $session->fetch_page_acl($page_id, $namespace); + $perms_obj =& $session; + $return = Array(); + if ( !file_exists(ENANO_ROOT . '/themes/' . $session->theme . '/acledit.tpl') ) + { + return Array( + 'mode' => 'error', + 'error' => 'It seems that (a) the file acledit.tpl is missing from these theme, and (b) the JSON response is working.', + ); + } + $return['template'] = $template->extract_vars('acledit.tpl'); + $return['page_id'] = $page_id; + $return['namespace'] = $namespace; + if(isset($parms['mode'])) + { + switch($parms['mode']) + { + case 'listgroups': + $return['groups'] = Array(); + $q = $db->sql_query('SELECT group_id,group_name FROM '.table_prefix.'groups ORDER BY group_name ASC;'); + while($row = $db->fetchrow()) + { + $return['groups'][] = Array( + 'id' => $row['group_id'], + 'name' => $row['group_name'], + ); + } + $db->free_result(); + break; + case 'seltarget': + $return['mode'] = 'seltarget'; + $return['acl_types'] = $perms_obj->acl_types; + $return['acl_deps'] = $perms_obj->acl_deps; + $return['acl_descs'] = $perms_obj->acl_descs; + $return['target_type'] = $parms['target_type']; + $return['target_id'] = $parms['target_id']; + switch($parms['target_type']) + { + case ACL_TYPE_USER: + $q = $db->sql_query('SELECT a.rules,u.user_id FROM '.table_prefix.'users AS u + LEFT JOIN '.table_prefix.'acl AS a + ON a.target_id=u.user_id + WHERE a.target_type='.ACL_TYPE_USER.' + AND u.username=\''.$db->escape($parms['target_id']).'\' + '.$page_where_clause.';'); + if(!$q) + return(Array('mode'=>'error','error'=>mysql_error())); + if($db->numrows() < 1) + { + $return['type'] = 'new'; + $q = $db->sql_query('SELECT user_id FROM '.table_prefix.'users WHERE username=\''.$db->escape($parms['target_id']).'\';'); + if(!$q) + return(Array('mode'=>'error','error'=>mysql_error())); + if($db->numrows() < 1) + return Array('mode'=>'error','error'=>'The username you entered was not found.'); + $row = $db->fetchrow(); + $return['target_name'] = $return['target_id']; + $return['target_id'] = intval($row['user_id']); + $return['current_perms'] = $session->acl_types; + } + else + { + $return['type'] = 'edit'; + $row = $db->fetchrow(); + $return['target_name'] = $return['target_id']; + $return['target_id'] = intval($row['user_id']); + $return['current_perms'] = $session->acl_merge($perms_obj->acl_types, $session->string_to_perm($row['rules'])); + } + $db->free_result(); + // Eliminate types that don't apply to this namespace + if ( $namespace ) + { + foreach ( $return['current_perms'] AS $i => $perm ) + { + if ( ( $page_id != null && $namespace != null ) && ( !in_array ( $namespace, $session->acl_scope[$i] ) && !in_array('All', $session->acl_scope[$i]) ) ) + { + // echo "// SCOPE CONTROL: eliminating: $i\n"; + unset($return['current_perms'][$i]); + unset($return['acl_types'][$i]); + unset($return['acl_descs'][$i]); + unset($return['acl_deps'][$i]); + } + } + } + break; + case ACL_TYPE_GROUP: + $q = $db->sql_query('SELECT a.rules,g.group_name,g.group_id FROM '.table_prefix.'groups AS g + LEFT JOIN '.table_prefix.'acl AS a + ON a.target_id=g.group_id + WHERE a.target_type='.ACL_TYPE_GROUP.' + AND g.group_id=\''.intval($parms['target_id']).'\' + '.$page_where_clause.';'); + if(!$q) + return(Array('mode'=>'error','error'=>mysql_error())); + if($db->numrows() < 1) + { + $return['type'] = 'new'; + $q = $db->sql_query('SELECT group_id,group_name FROM '.table_prefix.'groups WHERE group_id=\''.intval($parms['target_id']).'\';'); + if(!$q) + return(Array('mode'=>'error','error'=>mysql_error())); + if($db->numrows() < 1) + return Array('mode'=>'error','error'=>'The group ID you submitted is not valid.'); + $row = $db->fetchrow(); + $return['target_name'] = $row['group_name']; + $return['target_id'] = intval($row['group_id']); + $return['current_perms'] = $session->acl_types; + } + else + { + $return['type'] = 'edit'; + $row = $db->fetchrow(); + $return['target_name'] = $row['group_name']; + $return['target_id'] = intval($row['group_id']); + $return['current_perms'] = $session->acl_merge($session->acl_types, $session->string_to_perm($row['rules'])); + } + $db->free_result(); + // Eliminate types that don't apply to this namespace + if ( $namespace ) + { + foreach ( $return['current_perms'] AS $i => $perm ) + { + if ( ( $page_id != false && $namespace != false ) && ( !in_array ( $namespace, $session->acl_scope[$i] ) && !in_array('All', $session->acl_scope[$i]) ) ) + { + // echo "// SCOPE CONTROL: eliminating: $i\n"; //; ".print_r($namespace,true).":".print_r($page_id,true)."\n"; + unset($return['current_perms'][$i]); + unset($return['acl_types'][$i]); + unset($return['acl_descs'][$i]); + unset($return['acl_deps'][$i]); + } + } + } + //return Array('mode'=>'debug','text'=>print_r($return, true)); + break; + default: + return Array('mode'=>'error','error','Invalid ACL type ID'); + break; + } + return $return; + break; + case 'save_new': + case 'save_edit': + $q = $db->sql_query('DELETE FROM '.table_prefix.'acl WHERE target_type='.intval($parms['target_type']).' AND target_id='.intval($parms['target_id']).' + '.$page_where_clause_lite.';'); + if(!$q) + return Array('mode'=>'error','error'=>mysql_error()); + $rules = $session->perm_to_string($parms['perms']); + if ( sizeof ( $rules ) < 1 ) + { + return array( + 'mode' => 'error', + 'error' => 'Supplied rule list has a length of zero' + ); + } + $q = ($page_id && $namespace) ? 'INSERT INTO '.table_prefix.'acl ( target_type, target_id, page_id, namespace, rules ) + VALUES( '.intval($parms['target_type']).', '.intval($parms['target_id']).', \''.$db->escape($page_id).'\', \''.$db->escape($namespace).'\', \''.$db->escape($rules).'\' )' : + 'INSERT INTO '.table_prefix.'acl ( target_type, target_id, rules ) + VALUES( '.intval($parms['target_type']).', '.intval($parms['target_id']).', \''.$db->escape($rules).'\' )'; + if(!$db->sql_query($q)) return Array('mode'=>'error','error'=>mysql_error()); + return Array( + 'mode' => 'success', + 'target_type' => $parms['target_type'], + 'target_id' => $parms['target_id'], + 'target_name' => $parms['target_name'], + 'page_id' => $page_id, + 'namespace' => $namespace, + ); + break; + case 'delete': + $q = $db->sql_query('DELETE FROM '.table_prefix.'acl WHERE target_type='.intval($parms['target_type']).' AND target_id='.intval($parms['target_id']).' + '.$page_where_clause_lite.';'); + if(!$q) + return Array('mode'=>'error','error'=>mysql_error()); + return Array( + 'mode' => 'delete', + 'target_type' => $parms['target_type'], + 'target_id' => $parms['target_id'], + 'target_name' => $parms['target_name'], + 'page_id' => $page_id, + 'namespace' => $namespace, + ); + break; + default: + return Array('mode'=>'error','error'=>'Hacking attempt'); + break; + } + } + return $return; + } + + /** + * Same as PageUtils::acl_editor(), but the parms are a JSON string instead of an array. This also returns a JSON string. + * @param string $parms Same as PageUtils::acl_editor/$parms, but should be a valid JSON string. + * @return string + */ + + function acl_json($parms = '{ }') + { + global $db, $session, $paths, $template, $plugins; // Common objects + $json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE); + $parms = $json->decode($parms); + $ret = PageUtils::acl_editor($parms); + $ret = $json->encode($ret); + return $ret; + } + + /** + * A non-Javascript frontend for the ACL API. + * @param array The request data, if any, this should be in the format required by PageUtils::acl_editor() + */ + + function aclmanager($parms) + { + global $db, $session, $paths, $template, $plugins; // Common objects + ob_start(); + // Convenience + $formstart = '
'; + $formend = '
'; + $parms = PageUtils::acl_preprocess($parms); + $response = PageUtils::acl_editor($parms); + $response = PageUtils::acl_postprocess($response); + + //die('
' . htmlspecialchars(print_r($response, true)) . '
'); + + switch($response['mode']) + { + case 'debug': + echo '
' . htmlspecialchars($response['text']) . '
'; + break; + case 'stage1': + echo '

Manage page access

+

Please select who should be affected by this access rule.

'; + echo $formstart; + echo '

+

+

+

' . $template->username_field('data[target_id_user]') . '

+

What should this access rule control?

+

+

+
+ + + + +
'; + echo $formend; + break; + case 'success': + echo '
+ Permissions updated
+ The permissions for ' . $response['target_name'] . ' on this page have been updated successfully.
+ ' . $formstart . ' + + + + + + + + + ' . $formend . ' +
'; + break; + case 'delete': + echo '
+ Rule deleted
+ The selected access rule has been successfully deleted.
+ ' . $formstart . ' + + + + + + + + + ' . $formend . ' +
'; + break; + case 'seltarget': + if ( $response['type'] == 'edit' ) + { + echo '

Editing permissions

'; + } + else + { + echo '

Create new rule

'; + } + $type = ( $response['target_type'] == ACL_TYPE_GROUP ) ? 'group' : 'user'; + $scope = ( $response['page_id'] ) ? 'this page' : 'this entire site'; + echo 'This panel allows you to edit what the '.$type.' "'.$response['target_name'].'" can do on '.$scope.'. Unless you set a permission to "Deny", these permissions may be overridden by other rules.'; + echo $formstart; + $parser = $template->makeParserText( $response['template']['acl_field_begin'] ); + echo $parser->run(); + $parser = $template->makeParserText( $response['template']['acl_field_item'] ); + $cls = 'row2'; + foreach ( $response['acl_types'] as $acl_type => $value ) + { + $vars = Array( + 'FIELD_DENY_CHECKED' => '', + 'FIELD_DISALLOW_CHECKED' => '', + 'FIELD_WIKIMODE_CHECKED' => '', + 'FIELD_ALLOW_CHECKED' => '', + ); + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $vars['ROW_CLASS'] = $cls; + + switch ( $response['current_perms'][$acl_type] ) + { + case AUTH_ALLOW: + $vars['FIELD_ALLOW_CHECKED'] = 'checked="checked"'; + break; + case AUTH_WIKIMODE: + $vars['FIELD_WIKIMODE_CHECKED'] = 'checked="checked"'; + break; + case AUTH_DISALLOW: + default: + $vars['FIELD_DISALLOW_CHECKED'] = 'checked="checked"'; + break; + case AUTH_DENY: + $vars['FIELD_DENY_CHECKED'] = 'checked="checked"'; + break; + } + $vars['FIELD_NAME'] = 'data[perms][' . $acl_type . ']'; + $vars['FIELD_DESC'] = $response['acl_descs'][$acl_type]; + $parser->assign_vars($vars); + echo $parser->run(); + } + $parser = $template->makeParserText( $response['template']['acl_field_end'] ); + echo $parser->run(); + echo '
+ + + + + + +    +
'; + echo $formend; + break; + case 'error': + ob_end_clean(); + die_friendly('Error occurred', '

Error returned by permissions API:

' . htmlspecialchars($response['error']) . '
'); + break; + } + $ret = ob_get_contents(); + ob_end_clean(); + echo + $template->getHeader() . + $ret . + $template->getFooter(); + } + + /** + * Preprocessor to turn the form-submitted data from the ACL editor into something the backend can handle + * @param array The posted data + * @return array + * @access private + */ + + function acl_preprocess($parms) + { + if ( !isset($parms['mode']) ) + // Nothing to do + return $parms; + switch ( $parms['mode'] ) + { + case 'seltarget': + + // Who's affected? + $parms['target_type'] = intval( $parms['target_type'] ); + $parms['target_id'] = ( $parms['target_type'] == ACL_TYPE_GROUP ) ? $parms['target_id_grp'] : $parms['target_id_user']; + + case 'save_edit': + case 'save_new': + if ( isset($parms['act_delete_rule']) ) + { + $parms['mode'] = 'delete'; + } + + // Scope (just this page or entire site?) + if ( $parms['scope'] == 'entire_site' || ( $parms['page_id'] == 'false' && $parms['namespace'] == 'false' ) ) + { + $parms['page_id'] = false; + $parms['namespace'] = false; + } + + break; + } + + if ( isset($parms['act_go_stage1']) ) + { + $parms = array( + 'mode' => 'listgroups' + ); + } + + return $parms; + } + + function acl_postprocess($response) + { + if(!isset($response['mode'])) + { + if ( isset($response['groups']) ) + $response['mode'] = 'stage1'; + else + $response = Array( + 'mode' => 'error', + 'error' => 'Invalid action passed by API backend.', + ); + } + return $response; + } + +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/pageutils.php~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/pageutils.php~ Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,2059 @@ +sql_query('SELECT username FROM '.table_prefix.'users WHERE username=\''.$db->escape(rawurldecode($name)).'\''); + if(!$q) die(mysql_error()); + if($db->numrows() < 1) { $db->free_result(); return('good'); } + else { $db->free_result(); return('bad'); } + } + + /** + * Get the wiki formatting source for a page + * @param $page the full page id (Namespace:Pagename) + * @return string + * @todo (DONE) Make it require a password (just for security purposes) + */ + + function getsource($page, $password = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!isset($paths->pages[$page])) + { + return ''; + } + + if(strlen($paths->pages[$page]['password']) == 40) + { + if(!$password || ( $password != $paths->pages[$page]['password'])) + { + return 'invalid_password'; + } + } + + if(!$session->get_permissions('view_source')) // Dependencies handle this for us - this also checks for read privileges + return 'access_denied'; + $pid = RenderMan::strToPageID($page); + if($pid[1] == 'Special' || $pid[1] == 'Admin') + { + die('This type of page ('.$paths->nslist[$pid[1]].') cannot be edited because the page source code is not stored in the database.'); + } + + $e = $db->sql_query('SELECT page_text,char_tag FROM '.table_prefix.'page_text WHERE page_id=\''.$pid[0].'\' AND namespace=\''.$pid[1].'\''); + if ( !$e ) + { + $db->_die('The page text could not be selected.'); + } + if( $db->numrows() < 1 ) + { + return ''; //$db->_die('There were no rows in the text table that matched the page text query.'); + } + + $r = $db->fetchrow(); + $db->free_result(); + $message = $r['page_text']; + + return htmlspecialchars($message); + } + + /** + * Basically a frontend to RenderMan::getPage(), with the ability to send valid data for nonexistent pages + * @param $page the full page id (Namespace:Pagename) + * @param $send_headers true if the theme headers should be sent (still dependent on current page settings), false otherwise + * @return string + */ + + function getpage($page, $send_headers = false, $hist_id = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + ob_start(); + $pid = RenderMan::strToPageID($page); + //die('
'.print_r($pid, true).'
'); + if(isset($paths->pages[$page]['password']) && strlen($paths->pages[$page]['password']) == 40) + { + password_prompt($page); + } + if(isset($paths->pages[$page])) + { + doStats($pid[0], $pid[1]); + } + if($paths->custom_page || $pid[1] == 'Special') + { + // If we don't have access to the page, get out and quick! + if(!$session->get_permissions('read') && $pid[0] != 'Login' && $pid[0] != 'Register') + { + $template->tpl_strings['PAGE_NAME'] = 'Access denied'; + + if ( $send_headers ) + { + $template->header(); + } + + echo '
Access to this page is denied.
This may be because you are not logged in or you have not met certain criteria for viewing this page.
'; + + if ( $send_headers ) + { + $template->footer(); + } + + $r = ob_get_contents(); + ob_end_clean(); + return $r; + } + + $fname = 'page_'.$pid[1].'_'.$paths->pages[$page]['urlname_nons']; + @call_user_func($fname); + + } + else if ( $pid[1] == 'Admin' ) + { + // If we don't have access to the page, get out and quick! + if(!$session->get_permissions('read')) + { + $template->tpl_strings['PAGE_NAME'] = 'Access denied'; + if ( $send_headers ) + { + $template->header(); + } + echo '
Access to this page is denied.
This may be because you are not logged in or you have not met certain criteria for viewing this page.
'; + if ( $send_headers ) + { + $template->footer(); + } + $r = ob_get_contents(); + ob_end_clean(); + return $r; + } + + $fname = 'page_'.$pid[1].'_'.$pid[0]; + if ( !function_exists($fname) ) + { + $title = 'Page backend not found'; + $message = "The administration page you are looking for was properly registered using the page API, but the backend function + ($fname) was not found. If this is a plugin page, then this is almost certainly a bug with the plugin."; + if ( $send_headers ) + { + die_friendly($title, "

$message

"); + } + else + { + echo "

$title

\n

$message

"; + } + } + @call_user_func($fname); + } + else if ( !isset( $paths->pages[$page] ) ) + { + ob_start(); + $code = $plugins->setHook('page_not_found'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + $text = ob_get_contents(); + if ( $text != '' ) + { + ob_end_clean(); + return $text; + } + $template->header(); + if($m = $paths->sysmsg('Page_not_found')) + { + eval('?>'.RenderMan::render($m)); + } + else + { + header('HTTP/1.1 404 Not Found'); + echo '

There is no page with this title yet.

+

You have requested a page that doesn\'t exist yet.'; + if($session->get_permissions('create_page')) echo ' You can create this page, or return to the homepage.'; + else echo ' Return to the homepage.

'; + if($session->get_permissions('history_rollback')) { + $e = $db->sql_query('SELECT * FROM '.table_prefix.'logs WHERE action=\'delete\' AND page_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\''.$pid[1].'\' ORDER BY time_id DESC;'); + if(!$e) $db->_die('The deletion log could not be selected.'); + if($db->numrows() > 0) { + $r = $db->fetchrow(); + echo '

This page also appears to have some log entries in the database - it seems that it was deleted on '.$r['date_string'].'. You can probably roll back the deletion.

'; + } + $db->free_result(); + } + echo '

+ HTTP Error: 404 Not Found +

'; + } + $template->footer(); + } + else + { + + // If we don't have access to the page, get out and quick! + if(!$session->get_permissions('read')) + { + $template->tpl_strings['PAGE_NAME'] = 'Access denied'; + if($send_headers) $template->header(); + echo '
Access to this page is denied.
This may be because you are not logged in or you have not met certain criteria for viewing this page.
'; + if($send_headers) $template->footer(); + $r = ob_get_contents(); + ob_end_clean(); + return $r; + } + + ob_start(); + $code = $plugins->setHook('page_custom_handler'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + $text = ob_get_contents(); + if ( $text != '' ) + { + ob_end_clean(); + return $text; + } + + if($hist_id) { + $e = $db->sql_query('SELECT page_text,date_string,char_tag FROM '.table_prefix.'logs WHERE page_id=\''.$paths->pages[$page]['urlname_nons'].'\' AND namespace=\''.$pid[1].'\' AND log_type=\'page\' AND action=\'edit\' AND time_id='.$db->escape($hist_id).''); + if($db->numrows() < 1) + { + $db->_die('There were no rows in the text table that matched the page text query.'); + } + $r = $db->fetchrow(); + $db->free_result(); + $message = '
Notice:
The page you are viewing was archived on '.$r['date_string'].'.
View current version | Restore this version

'.RenderMan::render($r['page_text']); + + if( !$paths->pages[$page]['special'] ) + { + if($send_headers) + { + $template->header(); + } + display_page_headers(); + } + + eval('?>'.$message); + + if( !$paths->pages[$page]['special'] ) + { + display_page_footers(); + if($send_headers) + { + $template->footer(); + } + } + + } else { + if(!$paths->pages[$page]['special']) + { + $message = RenderMan::getPage($paths->pages[$page]['urlname_nons'], $pid[1]); + } + else + { + $message = RenderMan::getPage($paths->pages[$page]['urlname_nons'], $pid[1], 0, false, false, false, false); + } + // This line is used to debug wikiformatted code + // die('
'.htmlspecialchars($message).'
'); + + if( !$paths->pages[$page]['special'] ) + { + if($send_headers) + { + $template->header(); + } + display_page_headers(); + } + + // This is it, this is what all of Enano has been working up to... + + eval('?>'.$message); + + if( !$paths->pages[$page]['special'] ) + { + display_page_footers(); + if($send_headers) + { + $template->footer(); + } + } + } + } + $ret = ob_get_contents(); + ob_end_clean(); + return $ret; + } + + /** + * Writes page data to the database, after verifying permissions and running the XSS filter + * @param $page_id the page ID + * @param $namespace the namespace + * @param $message the text to save + * @return string + */ + + function savepage($page_id, $namespace, $message, $summary = 'No edit summary given', $minor = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $uid = sha1(microtime()); + $pname = $paths->nslist[$namespace] . $page_id; + + if(!$session->get_permissions('edit_page')) + return 'Access to edit pages is denied.'; + + if(!isset($paths->pages[$pname])) + { + if(!PageUtils::createPage($page_id, $namespace)) + return 'The page did not exist, and I was not able to create it. Permissions problem?'; + } + + $prot = ( ( $paths->pages[$pname]['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $paths->pages[$pname]['protected'] == 1) ? true : false; + $wiki = ( ( $paths->pages[$pname]['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $paths->pages[$pname]['wiki_mode'] == 1) ? true : false; + if(($prot || !$wiki) && $session->user_level < USER_LEVEL_ADMIN ) return('You are not authorized to edit this page.'); + + // Strip potentially harmful tags and PHP from the message, if we are in wiki mode and the user is not an administrator + $message = RenderMan::preprocess_text($message, false, false); + + $msg=$db->escape($message); + + $minor = $minor ? 'true' : 'false'; + $q='INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,page_id,namespace,page_text,char_tag,author,edit_summary,minor_edit) VALUES(\'page\', \'edit\', '.time().', \''.date('d M Y h:i a').'\', \''.$paths->cpage['urlname_nons'].'\', \''.$paths->namespace.'\', \''.$msg.'\', \''.$uid.'\', \''.$session->username.'\', \''.$db->escape(htmlspecialchars($summary)).'\', '.$minor.');'; + if(!$db->sql_query($q)) $db->_die('The history (log) entry could not be inserted into the logs table.'); + + $q = 'UPDATE '.table_prefix.'page_text SET page_text=\''.$msg.'\',char_tag=\''.$uid.'\' WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'; + $e = $db->sql_query($q); + if(!$e) $db->_die('Enano was unable to save the page contents. Your changes have been lost :\'(.'); + + $paths->rebuild_page_index($page_id, $namespace); + + return 'good'; + } + + /** + * Creates a page, both in memory and in the database. + * @param string $page_id + * @param string $namespace + * @return bool true on success, false on failure + */ + + function createPage($page_id, $namespace, $name = false, $visible = 1) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(in_array($namespace, Array('Special', 'Admin'))) + { + // echo 'Notice: PageUtils::createPage: You can\'t create a special page in the database
'; + return false; // Can't create a special page + } + + if(!isset($paths->nslist[$namespace])) + { + // echo 'Notice: PageUtils::createPage: Couldn\'t look up the namespace
'; + return false; // Couldn't look up namespace + } + + $pname = $paths->nslist[$namespace] . $page_id; + if(isset($paths->pages[$pname])) + { + // echo 'Notice: PageUtils::createPage: Page already exists
'; + return false; // Page already exists + } + + if(!$session->get_permissions('create_page')) + { + // echo 'Notice: PageUtils::createPage: Not authorized to create pages
'; + return false; // Access denied + } + + if($session->user_level < USER_LEVEL_ADMIN && $namespace == 'System') + { + // echo 'Notice: PageUtils::createPage: Not authorized to create system messages
'; + return false; // Not authorized to create system messages + } + + if ( !$name ) + $name = str_replace('_', ' ', $page_id); + $page = str_replace(' ', '_', $page_id); + $regex = '#^([A-z0-9 _\-\.\/\!\@\(\)]*)$#is'; + if(!preg_match($regex, $page)) + { + //echo 'Notice: PageUtils::createPage: Name contains invalid characters
'; + return false; // Name contains invalid characters + } + + $prot = ( $namespace == 'System' ) ? 1 : 0; + + $paths->add_page(Array( + 'name'=>$name, + 'urlname'=>$page, + 'namespace'=>$namespace, + 'special'=>0,'visible'=>1,'comments_on'=>0,'protected'=>$prot,'delvotes'=>0,'delvote_ips'=>'','wiki_mode'=>2, + )); + + $qa = $db->sql_query('INSERT INTO '.table_prefix.'pages(name,urlname,namespace,visible,protected) VALUES(\''.$db->escape($name).'\', \''.$db->escape($page).'\', \''.$namespace.'\', '. ( $visible ? '1' : '0' ) .', '.$prot.');'); + $qb = $db->sql_query('INSERT INTO '.table_prefix.'page_text(page_id,namespace) VALUES(\''.$db->escape($page).'\', \''.$namespace.'\');'); + $qc = $db->sql_query('INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,author,page_id,namespace) VALUES('.time().', \''.date('d M Y h:i a').'\', \'page\', \'create\', \''.$session->username.'\', \''.$db->escape($page).'\', \''.$namespace.'\');'); + + if($qa && $qb && $qc) + return true; + else + { + echo $db->get_error(); + return false; + } + } + + /** + * Sets the protection level on a page. + * @param $page_id string the page ID + * @param $namespace string the namespace + * @param $level int level of protection - 0 is off, 1 is full, 2 is semi + * @param $reason string why the page is being (un)protected + * @return string - "good" on success, in all other cases, an error string (on query failure, calls $db->_die() ) + */ + function protect($page_id, $namespace, $level, $reason) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $pname = $paths->nslist[$namespace] . $page_id; + $wiki = ( ( $paths->pages[$pname]['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $paths->pages[$pname]['wiki_mode'] == 1) ? true : false; + $prot = ( ( $paths->pages[$pname]['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $paths->pages[$pname]['protected'] == 1) ? true : false; + + if(!$session->get_permissions('protect')) return('Insufficient access rights'); + if(!$wiki) return('Page protection only has an effect when Wiki Mode is enabled.'); + if(!preg_match('#^([0-9]+){1}$#', (string)$level)) return('Invalid $level parameter.'); + + if($reason!='NO_REASON') { + switch($level) + { + case 0: + $q = 'INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,author,page_id,namespace,edit_summary) VALUES('.time().', \''.date('d M Y h:i a').'\', \'page\', \'unprot\', \''.$session->username.'\', \''.$page_id.'\', \''.$namespace.'\', \''.$db->escape(htmlspecialchars($reason)).'\');'; + break; + case 1: + $q = 'INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,author,page_id,namespace,edit_summary) VALUES('.time().', \''.date('d M Y h:i a').'\', \'page\', \'prot\', \''.$session->username.'\', \''.$page_id.'\', \''.$namespace.'\', \''.$db->escape(htmlspecialchars($reason)).'\');'; + break; + case 2: + $q = 'INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,author,page_id,namespace,edit_summary) VALUES('.time().', \''.date('d M Y h:i a').'\', \'page\', \'semiprot\', \''.$session->username.'\', \''.$page_id.'\', \''.$namespace.'\', \''.$db->escape(htmlspecialchars($reason)).'\');'; + break; + default: + return 'PageUtils::protect(): Invalid value for $level'; + break; + } + if(!$db->sql_query($q)) $db->_die('The log entry for the page protection could not be inserted.'); + } + + $q = $db->sql_query('UPDATE '.table_prefix.'pages SET protected='.$_POST['level'].' WHERE urlname=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$q) $db->_die('The pages table was not updated.'); + + return('good'); + } + + /** + * Generates an HTML table with history information in it. + * @param $page_id the page ID + * @param $namespace the namespace + * @return string + */ + + function histlist($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + if(!$session->get_permissions('history_view')) + return 'Access denied'; + + ob_start(); + + $pname = $paths->nslist[$namespace] . $page_id; + $wiki = ( ( $paths->pages[$pname]['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $paths->pages[$pname]['wiki_mode'] == 1) ? true : false; + $prot = ( ( $paths->pages[$pname]['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $paths->pages[$pname]['protected'] == 1) ? true : false; + + $q = 'SELECT time_id,date_string,page_id,namespace,author,edit_summary,minor_edit FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' ORDER BY time_id DESC;'; + if(!$db->sql_query($q)) $db->_die('The history data for the page "'.$paths->cpage['name'].'" could not be selected.'); + echo 'History of edits and actions

Edits:

'; + $numrows = $db->numrows(); + if($numrows < 1) echo 'No history entries in this category.'; + else + { + + echo '
+ +
  +
+ + + + + + + + + '."\n"."\n"; + $cls = 'row2'; + $ticker = 0; + + while($r = $db->fetchrow()) { + + $ticker++; + + if($cls == 'row2') $cls = 'row1'; + else $cls = 'row2'; + + echo ''."\n"; + + // Diff selection + if($ticker == 1) + { + $s1 = ''; + $s2 = 'checked="checked" '; + } + elseif($ticker == 2) + { + $s1 = 'checked="checked" '; + $s2 = ''; + } + else + { + $s1 = ''; + $s2 = ''; + } + if($ticker > 1) echo ''."\n"; else echo ''; + if($ticker < $numrows) echo ''."\n"; else echo ''; + + // Date and time + echo ''."\n"; + + // User + if($session->get_permissions('mod_misc') && preg_match('#^([0-9]*){1,3}\.([0-9]*){1,3}\.([0-9]*){1,3}\.([0-9]*){1,3}$#', $r['author'])) $rc = ' style="cursor: pointer;" title="Click cell background for reverse DNS info" onclick="ajaxReverseDNS(this, \''.$r['author'].'\');"'; + else $rc = ''; + echo ''."\n"; + + // Edit summary + echo ''."\n"; + + // Minor edit + echo ''."\n"; + + // Actions! + echo ''."\n"; + echo ''."\n"; + echo ''."\n"; + + echo ''."\n"."\n"; + + } + echo '
DiffDate/timeUserEdit summaryMinorActions
'.$r['date_string'].'nslist['User'] . $r['author'])) echo 'class="wikilink-nonexistent"'; + echo '>'.$r['author'].''.$r['edit_summary'].''. (( $r['minor_edit'] ) ? 'M' : '' ) .'View revisionView user contribsRevert to this revision
+
+
+ + +
+ '; + } + $db->free_result(); + echo '

Other changes:

'; + $q = 'SELECT time_id,action,date_string,page_id,namespace,author,edit_summary,minor_edit FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action!=\'edit\' AND page_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\''.$paths->namespace.'\' ORDER BY time_id DESC;'; + if(!$db->sql_query($q)) $db->_die('The history data for the page "'.$paths->cpage['name'].'" could not be selected.'); + if($db->numrows() < 1) echo 'No history entries in this category.'; + else { + + echo '
'; + $cls = 'row2'; + while($r = $db->fetchrow()) { + + if($cls == 'row2') $cls = 'row1'; + else $cls = 'row2'; + + echo ''; + + // Date and time + echo ''; + + // User + echo ''; + + + // Minor edit + echo ''; + + // Action taken + echo ''; + + // Actions! + echo ''; + echo ''; + + //echo '(rollback) '.$r['date_string'].' '.$r['author'].' (Userpage, Contrib): '; + + if($r['minor_edit']) echo ' - minor edit'; + echo '
'; + + echo ''; + } + echo '
Date/timeUserMinorAction takenExtra info
'.$r['date_string'].'nslist['User'] . $r['author'])) echo 'class="wikilink-nonexistent"'; + echo '>'.$r['author'].''. (( $r['minor_edit'] ) ? 'M' : '' ) .''; + if ($r['action']=='prot') echo 'Protected pageReason: '.$r['edit_summary']; + elseif($r['action']=='unprot') echo 'Unprotected pageReason: '.$r['edit_summary']; + elseif($r['action']=='semiprot') echo 'Semi-protected pageReason: '.$r['edit_summary']; + elseif($r['action']=='rename') echo 'Renamed pageOld title: '.$r['edit_summary']; + elseif($r['action']=='create') echo 'Created page'; + elseif($r['action']=='delete') echo 'Deleted page'; + elseif($r['action']=='reupload') echo 'Uploaded new file versionReason: '.$r['edit_summary']; + echo 'View user contribsRevert action
'; + } + $db->free_result(); + $ret = ob_get_contents(); + ob_end_clean(); + return $ret; + } + + /** + * Rolls back a logged action + * @param $id the time ID, a.k.a. the primary key in the logs table + * @return string + */ + + function rollback($id) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('history_rollback')) return('You are not authorized to perform rollbacks.'); + if(!preg_match('#^([0-9]+)$#', (string)$id)) return('The value "id" on the query string must be an integer.'); + $e = $db->sql_query('SELECT log_type,action,date_string,page_id,namespace,page_text,char_tag,author,edit_summary FROM '.table_prefix.'logs WHERE time_id='.$id.';'); + if(!$e) $db->_die('The rollback data could not be selected.'); + $rb = $db->fetchrow(); + $db->free_result(); + switch($rb['log_type']) { + case "page": + switch($rb['action']) { + case "edit": + $t = $db->escape($rb['page_text']); + $e = $db->sql_query('UPDATE '.table_prefix.'page_text SET page_text=\''.$t.'\',char_tag=\''.$rb['char_tag'].'\' WHERE page_id=\''.$rb['page_id'].'\' AND namespace=\''.$rb['namespace'].'\''); + if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + else return('The page "'.$paths->pages[$paths->nslist[$rb['namespace']].$rb['page_id']]['name'].'" has been rolled back to the state it was in on '.$rb['date_string'].'.'); + break; + case "rename": + $t = $db->escape($rb['edit_summary']); + $e = $db->sql_query('UPDATE '.table_prefix.'pages SET name=\''.$t.'\' WHERE urlname=\''.$rb['page_id'].'\' AND namespace=\''.$rb['namespace'].'\''); + if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + else return('The page "'.$paths->pages[$paths->nslist[$rb['namespace']].$rb['page_id']]['name'].'" has been rolled back to the name it had ("'.$rb['edit_summary'].'") before '.$rb['date_string'].'.'); + break; + case "prot": + $e = $db->sql_query('UPDATE '.table_prefix.'pages SET protected=0 WHERE urlname=\''.$rb['page_id'].'\' AND namespace=\''.$rb['namespace'].'\''); + if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + else return('The page "'.$paths->pages[$paths->nslist[$rb['namespace']].$rb['page_id']]['name'].'" has been unprotected according to the log created at '.$rb['date_string'].'.'); + break; + case "semiprot": + $e = $db->sql_query('UPDATE '.table_prefix.'pages SET protected=0 WHERE urlname=\''.$rb['page_id'].'\' AND namespace=\''.$rb['namespace'].'\''); + if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + else return('The page "'.$paths->pages[$paths->nslist[$rb['namespace']].$rb['page_id']]['name'].'" has been unprotected according to the log created at '.$rb['date_string'].'.'); + break; + case "unprot": + $e = $db->sql_query('UPDATE '.table_prefix.'pages SET protected=1 WHERE urlname=\''.$rb['page_id'].'\' AND namespace=\''.$rb['namespace'].'\''); + if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + else return('The page "'.$paths->pages[$paths->nslist[$rb['namespace']].$rb['page_id']]['name'].'" has been protected according to the log created at '.$rb['date_string'].'.'); + break; + case "delete": + if(!$session->get_permissions('history_rollback_extra')) return('Administrative privileges are required for page undeletion.'); + if(isset($paths->pages[$paths->cpage['urlname']])) return('You cannot raise a dead page that is alive.'); + $name = str_replace('_', ' ', $rb['page_id']); + $e = $db->sql_query('INSERT INTO '.table_prefix.'pages(name,urlname,namespace) VALUES( \''.$name.'\', \''.$rb['page_id'].'\',\''.$rb['namespace'].'\' )');if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + $e = $db->sql_query('SELECT page_text,char_tag FROM '.table_prefix.'logs WHERE page_id=\''.$rb['page_id'].'\' AND namespace=\''.$rb['namespace'].'\' AND log_type=\'page\' AND action=\'edit\' ORDER BY time_id DESC;'); if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + $r = $db->fetchrow(); + $e = $db->sql_query('INSERT INTO '.table_prefix.'page_text(page_id,namespace,page_text,char_tag) VALUES(\''.$rb['page_id'].'\',\''.$rb['namespace'].'\',\''.$db->escape($r['page_text']).'\',\''.$r['char_tag'].'\')'); if(!$e) return("An error occurred during the rollback operation.\nMySQL said: ".mysql_error()."\n\nSQL backtrace:\n".$db->sql_backtrace()); + return('The page "'.$name.'" has been undeleted according to the log created at '.$rb['date_string'].'.'); + break; + case "reupload": + if(!$session->get_permissions('history_rollbacks_extra')) return('Administrative privileges are required for file rollbacks.'); + $newtime = time(); + $newdate = date('d M Y h:i a'); + if(!$db->sql_query('UPDATE '.table_prefix.'logs SET time_id='.$newtime.',date_string=\''.$newdate.'\' WHERE time_id='.$id)) return('Error during query: '.mysql_error()); + if(!$db->sql_query('UPDATE '.table_prefix.'files SET time_id='.$newtime.' WHERE time_id='.$id)) return('Error during query: '.mysql_error()); + return('The file has been rolled back to the version uploaded on '.date('d M Y h:i a', (int)$id).'.'); + break; + default: + return('Rollback of the action "'.$rb['action'].'" is not yet supported.'); + break; + } + break; + case "security": + case "login": + return('A '.$rb['log_type'].'-related log entry cannot be rolled back.'); + break; + default: + return('Unknown log entry type: "'.$rb['log_type'].'"'); + } + } + + /** + * Posts a comment. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $name the name of the person posting, defaults to current username/IP + * @param $subject the subject line of the comment + * @param $text the comment text + * @return string javascript code + */ + + function addcomment($page_id, $namespace, $name, $subject, $text, $captcha_code = false, $captcha_id = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $_ob = ''; + if(!$session->get_permissions('post_comments')) + return 'Access denied'; + if(getConfig('comments_need_login') == '2' && !$session->user_logged_in) _die('Access denied to post comments: you need to be logged in first.'); + if(getConfig('comments_need_login') == '1' && !$session->user_logged_in) + { + if(!$captcha_code || !$captcha_id) _die('BUG: PageUtils::addcomment: no CAPTCHA data passed to method'); + $result = $session->get_captcha($captcha_id); + if($captcha_code != $result) _die('The confirmation code you entered was incorrect.'); + } + $text = RenderMan::preprocess_text($text); + $name = $session->user_logged_in ? RenderMan::preprocess_text($session->username) : RenderMan::preprocess_text($name); + $subj = RenderMan::preprocess_text($subject); + if(getConfig('approve_comments')=='1') $appr = '0'; else $appr = '1'; + $q = 'INSERT INTO '.table_prefix.'comments(page_id,namespace,subject,comment_data,name,user_id,approved,time) VALUES(\''.$page_id.'\',\''.$namespace.'\',\''.$subj.'\',\''.$text.'\',\''.$name.'\','.$session->user_id.','.$appr.','.time().')'; + $e = $db->sql_query($q); + if(!$e) die('alert(unescape(\''.rawurlencode('Error inserting comment data: '.mysql_error().'\n\nQuery:\n'.$q).'\'))'); + else $_ob .= '
Your comment has been posted.
'; + return PageUtils::comments($page_id, $namespace, false, Array(), $_ob); + } + + /** + * Generates partly-compiled HTML/Javascript code to be eval'ed by the user's browser to display comments + * @param $page_id the page ID + * @param $namespace the namespace + * @param $action administrative action to perform, default is false + * @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc. + * @param $_ob text to prepend to output, used by PageUtils::addcomment + * @return array + * @access private + */ + + function comments_raw($page_id, $namespace, $action = false, $flags = Array(), $_ob = '') + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $pname = $paths->nslist[$namespace] . $page_id; + + ob_start(); + + if($action && $session->get_permissions('mod_comments')) // Nip hacking attempts in the bud + { + switch($action) { + case "delete": + if(isset($flags['id'])) + { + $q = 'DELETE FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND comment_id='.intval($flags['id']).' LIMIT 1;'; + } else { + $n = $db->escape($flags['name']); + $s = $db->escape($flags['subj']); + $t = $db->escape($flags['text']); + $q = 'DELETE FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND name=\''.$n.'\' AND subject=\''.$s.'\' AND comment_data=\''.$t.'\' LIMIT 1;'; + } + $e=$db->sql_query($q); + if(!$e) die('alert(unesape(\''.rawurlencode('Error during query: '.mysql_error().'\n\nQuery:\n'.$q).'\'));'); + break; + case "approve": + if(isset($flags['id'])) + { + $where = 'comment_id='.intval($flags['id']); + } else { + $n = $db->escape($flags['name']); + $s = $db->escape($flags['subj']); + $t = $db->escape($flags['text']); + $where = 'name=\''.$n.'\' AND subject=\''.$s.'\' AND comment_data=\''.$t.'\''; + } + $q = 'SELECT approved FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND '.$where.' LIMIT 1;'; + $e = $db->sql_query($q); + if(!$e) die('alert(unesape(\''.rawurlencode('Error selecting approval status: '.mysql_error().'\n\nQuery:\n'.$q).'\'));'); + $r = $db->fetchrow(); + $db->free_result(); + $a = ( $r['approved'] ) ? '0' : '1'; + $q = 'UPDATE '.table_prefix.'comments SET approved='.$a.' WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND '.$where.';'; + $e=$db->sql_query($q); + if(!$e) die('alert(unesape(\''.rawurlencode('Error during query: '.mysql_error().'\n\nQuery:\n'.$q).'\'));'); + if($a=='1') $v = 'Unapprove'; + else $v = 'Approve'; + echo 'document.getElementById("mdgApproveLink'.$_GET['id'].'").innerHTML="'.$v.'";'; + break; + } + } + + if(!defined('ENANO_TEMPLATE_LOADED')) + { + $template->load_theme($session->theme, $session->style); + } + + $tpl = $template->makeParser('comment.tpl'); + + $e = $db->sql_query('SELECT * FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND approved=0;'); + if(!$e) $db->_die('The comment text data could not be selected.'); + $num_unapp = $db->numrows(); + $db->free_result(); + $e = $db->sql_query('SELECT * FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND approved=1;'); + if(!$e) $db->_die('The comment text data could not be selected.'); + $num_app = $db->numrows(); + $db->free_result(); + $lq = $db->sql_query('SELECT c.comment_id,c.subject,c.name,c.comment_data,c.approved,c.time,c.user_id,u.user_level,u.signature + FROM '.table_prefix.'comments AS c + LEFT JOIN '.table_prefix.'users AS u + ON c.user_id=u.user_id + WHERE page_id=\''.$page_id.'\' + AND namespace=\''.$namespace.'\' ORDER BY c.time ASC;'); + if(!$lq) _die('The comment text data could not be selected. '.mysql_error()); + $_ob .= '

Article Comments

'; + $n = ( $session->get_permissions('mod_comments')) ? $db->numrows() : $num_app; + if($n==1) $s = 'is '.$n.' comment'; else $s = 'are '.$n.' comments'; + if($n < 1) + { + $_ob .= '

There are currently no comments on this '.strtolower($namespace).''; + if($namespace != 'Article') $_ob .= ' page'; + $_ob .= '.

'; + } else $_ob .= '

There '.$s.' on this article.

'; + if($session->get_permissions('mod_comments') && $num_unapp > 0) $_ob .= ' '.$num_unapp.' of those are unapproved.'; + elseif(!$session->get_permissions('mod_comments') && $num_unapp > 0) { $u = ($num_unapp == 1) ? "is $num_unapp comment" : "are $num_unapp comments"; $_ob .= ' However, there ' . $u . ' awating approval.'; } + $list = 'list = { '; + // _die(htmlspecialchars($ttext)); + $i = -1; + while($row = $db->fetchrow($lq)) + { + $i++; + $strings = Array(); + $bool = Array(); + if($session->get_permissions('mod_comments') || $row['approved']) { + $list .= $i . ' : { \'comment\' : unescape(\''.rawurlencode($row['comment_data']).'\'), \'name\' : unescape(\''.rawurlencode($row['name']).'\'), \'subject\' : unescape(\''.rawurlencode($row['subject']).'\'), }, '; + + // Comment ID (used in the Javascript apps) + $strings['ID'] = (string)$i; + + // Determine the name, and whether to link to the user page or not + $name = ''; + if($row['user_id'] > 0) $name .= ''; + $name .= $row['name']; + if($row['user_id'] > 0) $name .= ''; + $strings['NAME'] = $name; unset($name); + + // Subject + $s = $row['subject']; + if(!$row['approved']) $s .= ' (Unapproved)'; + $strings['SUBJECT'] = $s; + + // Date and time + $strings['DATETIME'] = date('F d, Y h:i a', $row['time']); + + // User level + switch($row['user_level']) + { + default: + case USER_LEVEL_GUEST: + $l = 'Guest'; + break; + case USER_LEVEL_MEMBER: + $l = 'Member'; + break; + case USER_LEVEL_MOD: + $l = 'Moderator'; + break; + case USER_LEVEL_ADMIN: + $l = 'Administrator'; + break; + } + $strings['USER_LEVEL'] = $l; unset($l); + + // The actual comment data + $strings['DATA'] = RenderMan::render($row['comment_data']); + + if($session->get_permissions('edit_comments')) + { + // Edit link + $strings['EDIT_LINK'] = 'edit'; + + // Delete link + $strings['DELETE_LINK'] = 'delete'; + } + else + { + // Edit link + $strings['EDIT_LINK'] = ''; + + // Delete link + $strings['DELETE_LINK'] = ''; + } + + // Send PM link + $strings['SEND_PM_LINK'] = ( $session->user_logged_in && $row['user_id'] > 0 ) ? 'Send private message
' : ''; + + // Add Buddy link + $strings['ADD_BUDDY_LINK'] = ( $session->user_logged_in && $row['user_id'] > 0 ) ? 'Add to buddy list' : ''; + + // Mod links + $applink = ''; + $applink .= ''; + if($row['approved']) $applink .= 'Unapprove'; + else $applink .= 'Approve'; + $applink .= ''; + $strings['MOD_APPROVE_LINK'] = $applink; unset($applink); + $strings['MOD_DELETE_LINK'] = 'Delete'; + + // Signature + $strings['SIGNATURE'] = ''; + if($row['signature'] != '') $strings['SIGNATURE'] = RenderMan::render($row['signature']); + + $bool['auth_mod'] = ($session->get_permissions('mod_comments')) ? true : false; + $bool['can_edit'] = ( ( $session->user_logged_in && $row['name'] == $session->username && $session->get_permissions('edit_comments') ) || $session->get_permissions('mod_comments') ) ? true : false; + $bool['signature'] = ( $strings['SIGNATURE'] == '' ) ? false : true; + + // Done processing and compiling, now let's cook it into HTML + $tpl->assign_vars($strings); + $tpl->assign_bool($bool); + $_ob .= $tpl->run(); + } + } + if(getConfig('comments_need_login') != '2' || $session->user_logged_in) + { + if(!$session->get_permissions('post_comments')) + { + $_ob .= '

Got something to say?

Access to post comments on this page is denied.

'; + } + else + { + $_ob .= '

Got something to say?

If you have comments or suggestions on this article, you can shout it out here.'; + if(getConfig('approve_comments')=='1') $_ob .= ' Before your comment will be visible to the public, a moderator will have to approve it.'; + if(getConfig('comments_need_login') == '1' && !$session->user_logged_in) $_ob .= ' Because you are not logged in, you will need to enter a visual confirmation before your comment will be posted.'; + $sn = $session->user_logged_in ? $session->username . '' : ''; + $_ob .= ' +
+

Comment form

+
+ + + '; + if(getConfig('comments_need_login') == '1' && !$session->user_logged_in) + { + $session->kill_captcha(); + $captcha = $session->make_captcha(); + $_ob .= ''; + } + $_ob .= ' + + +
Your name or screen name:'.$sn.'
Comment subject:
Visual confirmation:
Please enter the code you see on the right.
Visual confirmation
Code:
Comment text:
(most HTML will be stripped)
+
+
'; + } + } else { + $_ob .= '

Got something to say?

You need to be logged in to post comments. Log in

'; + } + $list .= '};'; + echo 'document.getElementById(\'ajaxEditContainer\').innerHTML = unescape(\''. rawurlencode($_ob) .'\'); + ' . $list; + echo 'Fat.fade_all(); document.getElementById(\'mdgCommentForm\').style.display = \'none\'; document.getElementById(\'mdgCommentFormLink\').style.display="inline";'; + + $ret = ob_get_contents(); + ob_end_clean(); + return Array($ret, $_ob); + + } + + /** + * Generates ready-to-execute Javascript code to be eval'ed by the user's browser to display comments + * @param $page_id the page ID + * @param $namespace the namespace + * @param $action administrative action to perform, default is false + * @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc. + * @param $_ob text to prepend to output, used by PageUtils::addcomment + * @return string + */ + + function comments($page_id, $namespace, $action = false, $id = -1, $_ob = '') + { + global $db, $session, $paths, $template, $plugins; // Common objects + $r = PageUtils::comments_raw($page_id, $namespace, $action, $id, $_ob); + return $r[0]; + } + + /** + * Generates HTML code for comments - used in browser compatibility mode + * @param $page_id the page ID + * @param $namespace the namespace + * @param $action administrative action to perform, default is false + * @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc. + * @param $_ob text to prepend to output, used by PageUtils::addcomment + * @return string + */ + + function comments_html($page_id, $namespace, $action = false, $id = -1, $_ob = '') + { + global $db, $session, $paths, $template, $plugins; // Common objects + $r = PageUtils::comments_raw($page_id, $namespace, $action, $id, $_ob); + return $r[1]; + } + + /** + * Updates comment data. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $subject new subject + * @param $text new text + * @param $old_subject the old subject, unprocessed and identical to the value in the DB + * @param $old_text the old text, unprocessed and identical to the value in the DB + * @param $id the javascript list ID, used internally by the client-side app + * @return string + */ + + function savecomment($page_id, $namespace, $subject, $text, $old_subject, $old_text, $id = -1) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('edit_comments')) + return 'result="BAD";error="Access denied"'; + // Avoid SQL injection + $old_text = $db->escape($old_text); + $old_subject = $db->escape($old_subject); + // Safety check - username/login + if(!$session->get_permissions('mod_comments')) // allow mods to edit comments + { + if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.

Please log in and try again.'); + $q = 'SELECT c.name FROM '.table_prefix.'comments c, '.table_prefix.'users u WHERE comment_data=\''.$old_text.'\' AND subject=\''.$old_subject.'\' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND u.user_id=c.user_id;'; + $s = $db->sql_query($q); + if(!$s) _die('SQL error during safety check: '.mysql_error().'

Attempted SQL:
'.htmlspecialchars($q).'
'); + $r = $db->fetchrow($s); + $db->free_result(); + if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); + } + $s = RenderMan::preprocess_text($subject); + $t = RenderMan::preprocess_text($text); + $sql = 'UPDATE '.table_prefix.'comments SET subject=\''.$s.'\',comment_data=\''.$t.'\' WHERE comment_data=\''.$old_text.'\' AND subject=\''.$old_subject.'\' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\''; + $result = $db->sql_query($sql); + if($result) + { + return 'result="GOOD"; + list['.$id.'][\'subject\'] = unescape(\''.str_replace('%5Cn', '%0A', rawurlencode(str_replace('{{EnAnO:Newline}}', '\\n', stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $s))))).'\'); + list['.$id.'][\'comment\'] = unescape(\''.str_replace('%5Cn', '%0A', rawurlencode(str_replace('{{EnAnO:Newline}}', '\\n', stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $t))))).'\'); id = '.$id.'; + s = unescape(\''.rawurlencode($s).'\'); + t = unescape(\''.str_replace('%5Cn', '
', rawurlencode(RenderMan::render(str_replace('{{EnAnO:Newline}}', "\n", stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $t)))))).'\');'; + } + else + { + return 'result="BAD"; error=unescape("'.rawurlencode('Enano encountered a problem whilst saving the comment. + Performed SQL: + '.$sql.' + + Error returned by MySQL: '.mysql_error()).'");'; + } + } + + /** + * Updates comment data using the comment_id column instead of the old, messy way + * @param $page_id the page ID + * @param $namespace the namespace + * @param $subject new subject + * @param $text new text + * @param $id the comment ID (primary key in enano_comments table) + * @return string + */ + + function savecomment_neater($page_id, $namespace, $subject, $text, $id) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!is_int($id)) die('PageUtils::savecomment: $id is not an integer, aborting for safety'); + if(!$session->get_permissions('edit_comments')) + return 'Access denied'; + // Safety check - username/login + if(!$session->get_permissions('mod_comments')) // allow mods to edit comments + { + if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.

Please log in and try again.'); + $q = 'SELECT c.name FROM '.table_prefix.'comments c, '.table_prefix.'users u WHERE comment_id='.$id.' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND u.user_id=c.user_id;'; + $s = $db->sql_query($q); + if(!$s) _die('SQL error during safety check: '.mysql_error().'

Attempted SQL:
'.htmlspecialchars($q).'
'); + $r = $db->fetchrow($s); + if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); + $db->free_result(); + } + $s = RenderMan::preprocess_text($subject); + $t = RenderMan::preprocess_text($text); + $sql = 'UPDATE '.table_prefix.'comments SET subject=\''.$s.'\',comment_data=\''.$t.'\' WHERE comment_id='.$id.' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\''; + $result = $db->sql_query($sql); + if($result) + return 'good'; + else return 'Enano encountered a problem whilst saving the comment. + Performed SQL: + '.$sql.' + + Error returned by MySQL: '.mysql_error(); + } + + /** + * Deletes a comment. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $name the name the user posted under + * @param $subj the subject of the comment to be deleted + * @param $text the text of the comment to be deleted + * @param $id the javascript list ID, used internally by the client-side app + * @return string + */ + + function deletecomment($page_id, $namespace, $name, $subj, $text, $id) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + if(!$session->get_permissions('edit_comments')) + return 'alert("Access to delete/edit comments is denied");'; + + if(!preg_match('#^([0-9]+)$#', (string)$id)) die('$_GET[id] is improperly formed.'); + $n = $db->escape($name); + $s = $db->escape($subj); + $t = $db->escape($text); + + // Safety check - username/login + if(!$session->get_permissions('mod_comments')) // allows mods to delete comments + { + if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.

Please log in and try again.'); + $q = 'SELECT c.name FROM '.table_prefix.'comments c, '.table_prefix.'users u WHERE comment_data=\''.$t.'\' AND subject=\''.$s.'\' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND u.user_id=c.user_id;'; + $s = $db->sql_query($q); + if(!$s) _die('SQL error during safety check: '.mysql_error().'

Attempted SQL:
'.htmlspecialchars($q).'
'); + $r = $db->fetchrow($s); + if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); + $db->free_result(); + } + $q = 'DELETE FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND name=\''.$n.'\' AND subject=\''.$s.'\' AND comment_data=\''.$t.'\' LIMIT 1;'; + $e=$db->sql_query($q); + if(!$e) return('alert(unesape(\''.rawurlencode('Error during query: '.mysql_error().'\n\nQuery:\n'.$q).'\'));'); + return('good'); + } + + /** + * Deletes a comment in a cleaner fashion. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $id the comment ID (primary key) + * @return string + */ + + function deletecomment_neater($page_id, $namespace, $id) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + if(!preg_match('#^([0-9]+)$#', (string)$id)) die('$_GET[id] is improperly formed.'); + + if(!$session->get_permissions('edit_comments')) + return 'alert("Access to delete/edit comments is denied");'; + + // Safety check - username/login + if(!$session->get_permissions('mod_comments')) // allows mods to delete comments + { + if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.

Please log in and try again.'); + $q = 'SELECT c.name FROM '.table_prefix.'comments c, '.table_prefix.'users u WHERE comment_id='.$id.' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND u.user_id=c.user_id;'; + $s = $db->sql_query($q); + if(!$s) _die('SQL error during safety check: '.mysql_error().'

Attempted SQL:
'.htmlspecialchars($q).'
'); + $r = $db->fetchrow($s); + if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.'); + $db->free_result(); + } + $q = 'DELETE FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\' AND comment_id='.$id.' LIMIT 1;'; + $e=$db->sql_query($q); + if(!$e) return('alert(unesape(\''.rawurlencode('Error during query: '.mysql_error().'\n\nQuery:\n'.$q).'\'));'); + return('good'); + } + + /** + * Renames a page. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $name the new name for the page + * @return string error string or success message + */ + + function rename($page_id, $namespace, $name) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $pname = $paths->nslist[$namespace] . $page_id; + + $prot = ( ( $paths->pages[$pname]['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $paths->pages[$pname]['protected'] == 1) ? true : false; + $wiki = ( ( $paths->pages[$pname]['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $paths->pages[$pname]['wiki_mode'] == 1) ? true : false; + + if( empty($name)) die('Name is too short'); + if( ( $session->get_permissions('rename') && ( ( $prot && $session->get_permissions('even_when_protected') ) || !$prot ) ) && ( $paths->namespace != 'Special' && $paths->namespace != 'Admin' )) { + $e = $db->sql_query('INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,page_id,namespace,author,edit_summary) VALUES('.time().', \''.date('d M Y h:i a').'\', \'page\', \'rename\', \''.$paths->cpage['urlname_nons'].'\', \''.$paths->namespace.'\', \''.$session->username.'\', \''.$paths->cpage['name'].'\')'); + if(!$e) $db->_die('The page title could not be updated.'); + $e = $db->sql_query('UPDATE '.table_prefix.'pages SET name=\''.$db->escape($name).'\' WHERE urlname=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$e) $db->_die('The page title could not be updated.'); + else return('The page "'.$paths->pages[$pname]['name'].'" has been renamed to "'.$name.'". You are encouraged to leave a comment explaining your action. + +You will see the change take effect the next time you reload this page.'); + } else { + return('Access is denied.'); + } + } + + /** + * Flushes (clears) the action logs for a given page + * @param $page_id the page ID + * @param $namespace the namespace + * @return string error/success string + */ + + function flushlogs($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('clear_logs')) die('Administrative privileges are required to flush logs, you loser.'); + $e = $db->sql_query('DELETE FROM '.table_prefix.'logs WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$e) $db->_die('The log entries could not be deleted.'); + $e = $db->sql_query('SELECT page_text,char_tag FROM '.table_prefix.'page_text WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$e) $db->_die('The current page text could not be selected; as a result, creating the backup of the page failed. Please make a backup copy of the page by clicking Edit this page and then clicking Save Changes.'); + $row = $db->fetchrow(); + $db->free_result(); + $q='INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,page_id,namespace,page_text,char_tag,author,edit_summary,minor_edit) VALUES(\'page\', \'edit\', '.time().', \''.date('d M Y h:i a').'\', \''.$page_id.'\', \''.$namespace.'\', \''.$db->escape($row['page_text']).'\', \''.$row['char_tag'].'\', \''.$session->username.'\', \''."Automatic backup created when logs were purged".'\', '.'false'.');'; + if(!$db->sql_query($q)) $db->_die('The history (log) entry could not be inserted into the logs table.'); + return('The logs for this page have been cleared. A backup of this page has been added to the logs table so that this page can be restored in case of vandalism or spam later.'); + } + + /** + * Deletes a page. + * @param $page_id the condemned page ID + * @param $namespace the condemned namespace + * @return string + */ + + function deletepage($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $perms = $session->fetch_page_acl($page_id, $namespace); + if(!$perms->get_permissions('delete_page')) die('Administrative privileges are required to delete pages, you loser.'); + $e = $db->sql_query('INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,page_id,namespace,author) VALUES('.time().', \''.date('d M Y h:i a').'\', \'page\', \'delete\', \''.$page_id.'\', \''.$namespace.'\', \''.$session->username.'\')'); + if(!$e) $db->_die('The page log entry could not be inserted.'); + $e = $db->sql_query('DELETE FROM '.table_prefix.'categories WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\''); + if(!$e) $db->_die('The page categorization entries could not be deleted.'); + $e = $db->sql_query('DELETE FROM '.table_prefix.'comments WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\''); + if(!$e) $db->_die('The page comments could not be deleted.'); + $e = $db->sql_query('DELETE FROM '.table_prefix.'page_text WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\''); + if(!$e) $db->_die('The page text entry could not be deleted.'); + $e = $db->sql_query('DELETE FROM '.table_prefix.'pages WHERE urlname=\''.$page_id.'\' AND namespace=\''.$namespace.'\''); + if(!$e) $db->_die('The page entry could not be deleted.'); + $e = $db->sql_query('DELETE FROM '.table_prefix.'files WHERE page_id=\''.$page_id.'\''); + if(!$e) $db->_die('The file entry could not be deleted.'); + return('This page has been deleted. Note that there is still a log of edits and actions in the database, and anyone with admin rights can raise this page from the dead unless the log is cleared. If the deleted file is an image, there may still be cached thumbnails of it in the cache/ directory, which is inaccessible to users.'); + } + + /** + * Increments the deletion votes for a page by 1, and adds the current username/IP to the list of users that have voted for the page to prevent dual-voting + * @param $page_id the page ID + * @param $namespace the namespace + * @return string + */ + + function delvote($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('vote_delete')) + return 'Access denied'; + $pname = $paths->nslist[$namespace] . $page_id; + $cv = $paths->pages[$pname]['delvotes']; + $ips = $paths->pages[$pname]['delvote_ips']; + $ips = explode('|', $ips); + if(in_array($_SERVER['REMOTE_ADDR'], $ips)) return('It appears that you have already voted to have this page deleted.'); + if($session->user_logged_in) + if(in_array($session->username, $ips)) + return('It appears that you have already voted to have this page deleted.'); + $ips[] = $_SERVER['REMOTE_ADDR']; + if($session->user_logged_in) $ips[] = $session->username; + $ips = implode('|', $ips); + $ips = substr($ips, 1, strlen($ips)); + $cv++; + $q = 'UPDATE '.table_prefix.'pages SET delvotes='.$cv.',delvote_ips=\''.$ips.'\' WHERE urlname=\''.$page_id.'\' AND namespace=\''.$namespace.'\''; + $w = $db->sql_query($q); + if(!$w) return("Error updating pages table: ".mysql_error()."\n\nAttemped SQL:\n".$q); + return('Your vote to have this page deleted has been cast.'."\nYou are encouraged to leave a comment explaining the reason for your vote."); + } + + /** + * Resets the number of votes against a page to 0. + * @param $page_id the page ID + * @param $namespace the namespace + * @return string + */ + + function resetdelvotes($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('vote_reset')) die('You need moderator rights in order to do this, stinkin\' hacker.'); + $q = 'UPDATE '.table_prefix.'pages SET delvotes=0,delvote_ips=\'\' WHERE urlname=\''.$page_id.'\' AND namespace=\''.$namespace.'\''; + $e = $db->sql_query($q); + if(!$e) $db->_die('The number of delete votes was not reset.'); + else return('The number of votes for having this page deleted has been reset to zero.'); + } + + /** + * Gets a list of styles for a given theme name. + * @param $id the name of the directory for the theme + * @return string Javascript code + */ + + function getstyles() + { + $dir = './themes/'.$_GET['id'].'/css/'; + $list = Array(); + // Open a known directory, and proceed to read its contents + if (is_dir($dir)) { + if ($dh = opendir($dir)) { + while (($file = readdir($dh)) !== false) { + if(preg_match('#^(.*?)\.css$#is', $file) && $file != '_printable.css') { // _printable.css should be included with every theme + // it should be a copy of the original style, but + // mostly black and white + // Note to self: document this + $list[] = substr($file, 0, strlen($file)-4); + } + } + closedir($dh); + } + } else return($dir.' is not a dir'); + $l = 'var list = new Array();'; + $i = -1; + foreach($list as $li) { + $i++; + $l .= "list[$i] = '$li';"; + } + return $l; + } + + /** + * Assembles a Javascript app with category information + * @param $page_id the page ID + * @param $namespace the namespace + * @return string Javascript code + */ + + function catedit($page_id, $namespace) + { + $d = PageUtils::catedit_raw($page_id, $namespace); + return $d[0] . ' /* BEGIN CONTENT */ document.getElementById("ajaxEditContainer").innerHTML = unescape(\''.rawurlencode($d[1]).'\');'; + } + + /** + * Does the actual HTML/javascript generation for cat editing, but returns an array + * @access private + */ + + function catedit_raw($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + ob_start(); + $_ob = ''; + $e = $db->sql_query('SELECT category_id FROM '.table_prefix.'categories WHERE page_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\''.$paths->namespace.'\''); + if(!$e) jsdie('Error selecting category information for current page: '.mysql_error()); + $cat_current = Array(); + while($r = $db->fetchrow()) + { + $cat_current[] = $r; + } + $db->free_result(); + $cat_all = Array(); + for($i=0;$ipages)/2;$i++) + { + if($paths->pages[$i]['namespace']=='Category') $cat_all[] = $paths->pages[$i]; + } + + // Make $cat_all an associative array, like $paths->pages + $sz = sizeof($cat_all); + for($i=0;$i<$sz;$i++) + { + $cat_all[$cat_all[$i]['urlname_nons']] = $cat_all[$i]; + } + // Now, the "zipper" function - join the list of categories with the list of cats that this page is a part of + $cat_info = $cat_all; + for($i=0;$i'; + if ( sizeof($cat_info) < 1 ) + { + $_ob .= '

There are no categories on this site yet.

'; + } + for ( $i = 0; $i < sizeof($cat_info) / 2; $i++ ) + { + // Protection code added 1/3/07 + // Updated 3/4/07 + $is_prot = false; + $perms = $session->fetch_page_acl($cat_info[$i]['urlname_nons'], 'Category'); + if ( !$session->get_permissions('edit_cat') || !$perms->get_permissions('edit_cat') || + ( $cat_info[$i]['really_protected'] && !$perms->get_permissions('even_when_protected') ) ) + $is_prot = true; + $prot = ( $is_prot ) ? ' disabled="disabled" ' : ''; + $prottext = ( $is_prot ) ? ' (protected)' : ''; + echo 'catlist['.$i.'] = \''.$cat_info[$i]['urlname_nons'].'\';'; + $_ob .= ''.$cat_info[$i]['name'].$prottext.'
'; + } + + $disabled = ( sizeof($cat_info) < 1 ) ? 'disabled="disabled"' : ''; + + $_ob .= '
'; + + $cont = ob_get_contents(); + ob_end_clean(); + return Array($cont, $_ob); + } + + /** + * Saves category information + * WARNING: If $which_cats is empty, all the category information for the selected page will be nuked! + * @param $page_id string the page ID + * @param $namespace string the namespace + * @param $which_cats array associative array of categories to put the page in + * @return string "GOOD" on success, error string on failure + */ + + function catsave($page_id, $namespace, $which_cats) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('edit_cat')) return('Insufficient privileges to change category information'); + + $page_perms = $session->fetch_page_acl($page_id, $namespace); + $page_data =& $paths->pages[$paths->nslist[$namespace].$page_id]; + + $cat_all = Array(); + for($i=0;$ipages)/2;$i++) + { + if($paths->pages[$i]['namespace']=='Category') $cat_all[] = $paths->pages[$i]; + } + + // Make $cat_all an associative array, like $paths->pages + $sz = sizeof($cat_all); + for($i=0;$i<$sz;$i++) + { + $cat_all[$cat_all[$i]['urlname_nons']] = $cat_all[$i]; + } + + $rowlist = Array(); + + for($i=0;$ifetch_page_acl($cat_all[$i]['urlname_nons'], 'Category'); + if ( !$session->get_permissions('edit_cat') || !$perms->get_permissions('edit_cat') || + ( $cat_all[$i]['really_protected'] && !$perms->get_permissions('even_when_protected') ) || + ( !$page_perms->get_permissions('even_when_protected') && $page_data['protected'] == '1' ) ) + $auth = false; + if(!$auth) + { + // Find out if the page is currently in the category + $q = $db->sql_query('SELECT * FROM '.table_prefix.'categories WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$q) + return 'MySQL error: '.$db->get_error(); + if($db->numrows() > 0) + { + $auth = true; + $which_cats[$cat_all[$i]['urlname_nons']] = true; // Force the category to stay in its current state + } + $db->free_result(); + } + if(isset($which_cats[$cat_all[$i]['urlname_nons']]) && $which_cats[$cat_all[$i]['urlname_nons']] == true /* for clarity ;-) */ && $auth ) $rowlist[] = '(\''.$page_id.'\', \''.$namespace.'\', \''.$cat_all[$i]['urlname_nons'].'\')'; + } + if(sizeof($rowlist) > 0) + { + $val = implode(',', $rowlist); + $q = 'INSERT INTO '.table_prefix.'categories(page_id,namespace,category_id) VALUES' . $val . ';'; + $e = $db->sql_query('DELETE FROM '.table_prefix.'categories WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$e) $db->_die('The old category data could not be deleted.'); + $e = $db->sql_query($q); + if(!$e) $db->_die('The new category data could not be inserted.'); + return('GOOD'); + } + else + { + $e = $db->sql_query('DELETE FROM '.table_prefix.'categories WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$e) $db->_die('The old category data could not be deleted.'); + return('GOOD'); + } + } + + /** + * Sets the wiki mode level for a page. + * @param $page_id string the page ID + * @param $namespace string the namespace + * @param $level int 0 for off, 1 for on, 2 for use global setting + * @return string "GOOD" on success, error string on failure + */ + + function setwikimode($page_id, $namespace, $level) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('set_wiki_mode')) return('Insufficient access rights'); + if(!isset($level) || (isset($level) && !preg_match('#^([0-2]){1}$#', (string)$level))) return('Invalid mode string'); + $q = $db->sql_query('UPDATE '.table_prefix.'pages SET wiki_mode='.$level.' WHERE urlname=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$q) return('Error during update query: '.mysql_error()."\n\nSQL Backtrace:\n".$db->sql_backtrace()); + return('GOOD'); + } + + /** + * Sets the access password for a page. + * @param $page_id string the page ID + * @param $namespace string the namespace + * @param $pass string the SHA1 hash of the password - if the password doesn't match the regex ^([0-9a-f]*){40,40}$ it will be sha1'ed + * @return string + */ + + function setpass($page_id, $namespace, $pass) + { + global $db, $session, $paths, $template, $plugins; // Common objects + // Determine permissions + if($paths->pages[$paths->nslist[$namespace].$page_id]['password'] != '') + $a = $session->get_permissions('password_reset'); + else + $a = $session->get_permissions('password_set'); + if(!$a) + return 'Access is denied'; + if(!isset($pass)) return('Password was not set on URL'); + $p = $pass; + if(!preg_match('#([0-9a-f]){40,40}#', $p)) $p = sha1($p); + if($p=='da39a3ee5e6b4b0d3255bfef95601890afd80709') $p = ''; + $e = $db->sql_query('UPDATE '.table_prefix.'pages SET password=\''.$p.'\' WHERE urlname=\''.$page_id.'\' AND namespace=\''.$namespace.'\';'); + if(!$e) die('PageUtils::setpass(): Error during update query: '.mysql_error()."\n\nSQL Backtrace:\n".$db->sql_backtrace()); + if($p=='') return('The password for this page has been disabled.'); + else return('The password for this page has been set.'); + } + + /** + * Generates some preview HTML + * @param $text string the wikitext to use + * @return string + */ + + function genPreview($text) + { + return '
Reminder: This is only a preview - your changes to this page have not yet been saved.
'.RenderMan::render(RenderMan::preprocess_text($text, false, false)).'
'; + } + + /** + * Makes a scrollable box + * @param string $text the inner HTML + * @param int $height Optional - the maximum height. Defaults to 250. + * @return string + */ + + function scrollBox($text, $height = 250) + { + return '
'.$text.'
'; + } + + /** + * Generates a diff summary between two page revisions. + * @param $page_id the page ID + * @param $namespace the namespace + * @param $id1 the time ID of the first revision + * @param $id2 the time ID of the second revision + * @return string XHTML-formatted diff + */ + + function pagediff($page_id, $namespace, $id1, $id2) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('history_view')) + return 'Access denied'; + if(!preg_match('#^([0-9]+)$#', (string)$id1) || + !preg_match('#^([0-9]+)$#', (string)$id2 )) return 'SQL injection attempt'; + // OK we made it through security + // Safest way to make sure we don't end up with the revisions in wrong columns is to make 2 queries + if(!$q1 = $db->sql_query('SELECT page_text,char_tag,author,edit_summary FROM '.table_prefix.'logs WHERE time_id='.$id1.' AND log_type=\'page\' AND action=\'edit\' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';')) return 'MySQL error: '.mysql_error(); + if(!$q2 = $db->sql_query('SELECT page_text,char_tag,author,edit_summary FROM '.table_prefix.'logs WHERE time_id='.$id2.' AND log_type=\'page\' AND action=\'edit\' AND page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';')) return 'MySQL error: '.mysql_error(); + $row1 = $db->fetchrow($q1); + $db->free_result($q1); + $row2 = $db->fetchrow($q2); + $db->free_result($q2); + if(sizeof($row1) < 1 || sizeof($row2) < 2) return 'Couldn\'t find any rows that matched the query. The time ID probably doesn\'t exist in the logs table.'; + $text1 = $row1['page_text']; + $text2 = $row2['page_text']; + $time1 = date('F d, Y h:i a', $id1); + $time2 = date('F d, Y h:i a', $id2); + $_ob = " +

Comparing revisions: {$time1} → {$time2}

+ "; + // Free some memory + unset($row1, $row2, $q1, $q2); + + $_ob .= RenderMan::diff($text1, $text2); + return $_ob; + } + + /** + * Gets ACL information about the selected page for target type X and target ID Y. + * @param string $page_id The page ID + * @param string $namespace The namespace + * @param array $parms What to select. This is an array purely for JSON compatibility. It should be an associative array with keys target_type and target_id. + * @return array + */ + + function acl_editor($parms = Array()) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$session->get_permissions('edit_acl') && $session->user_level < USER_LEVEL_ADMIN) + return 'Access is denied.'; + $parms['page_id'] = ( isset($parms['page_id']) ) ? $parms['page_id'] : false; + $parms['namespace'] = ( isset($parms['namespace']) ) ? $parms['namespace'] : false; + $page_id =& $parms['page_id']; + $namespace =& $parms['namespace']; + $page_where_clause = ( empty($page_id) || empty($namespace) ) ? 'AND a.page_id IS NULL AND a.namespace IS NULL' : 'AND a.page_id=\''.$db->escape($page_id).'\' AND a.namespace=\''.$db->escape($namespace).'\''; + $page_where_clause_lite = ( empty($page_id) || empty($namespace) ) ? 'AND page_id IS NULL AND namespace IS NULL' : 'AND page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\''; + //die(print_r($page_id,true)); + $template->load_theme(); + // $perms_obj = $session->fetch_page_acl($page_id, $namespace); + $perms_obj =& $session; + $return = Array(); + if ( !file_exists(ENANO_ROOT . '/themes/' . $session->theme . '/acledit.tpl') ) + { + return Array( + 'mode' => 'error', + 'error' => 'It seems that (a) the file acledit.tpl is missing from these theme, and (b) the JSON response is working.', + ); + } + $return['template'] = $template->extract_vars('acledit.tpl'); + $return['page_id'] = $page_id; + $return['namespace'] = $namespace; + if(isset($parms['mode'])) + { + switch($parms['mode']) + { + case 'listgroups': + $return['groups'] = Array(); + $q = $db->sql_query('SELECT group_id,group_name FROM '.table_prefix.'groups ORDER BY group_name ASC;'); + while($row = $db->fetchrow()) + { + $return['groups'][] = Array( + 'id' => $row['group_id'], + 'name' => $row['group_name'], + ); + } + $db->free_result(); + break; + case 'seltarget': + $return['mode'] = 'seltarget'; + $return['acl_types'] = $perms_obj->acl_types; + $return['acl_deps'] = $perms_obj->acl_deps; + $return['acl_descs'] = $perms_obj->acl_descs; + $return['target_type'] = $parms['target_type']; + $return['target_id'] = $parms['target_id']; + switch($parms['target_type']) + { + case ACL_TYPE_USER: + $q = $db->sql_query('SELECT a.rules,u.user_id FROM '.table_prefix.'users AS u + LEFT JOIN '.table_prefix.'acl AS a + ON a.target_id=u.user_id + WHERE a.target_type='.ACL_TYPE_USER.' + AND u.username=\''.$db->escape($parms['target_id']).'\' + '.$page_where_clause.';'); + if(!$q) + return(Array('mode'=>'error','error'=>mysql_error())); + if($db->numrows() < 1) + { + $return['type'] = 'new'; + $q = $db->sql_query('SELECT user_id FROM '.table_prefix.'users WHERE username=\''.$db->escape($parms['target_id']).'\';'); + if(!$q) + return(Array('mode'=>'error','error'=>mysql_error())); + if($db->numrows() < 1) + return Array('mode'=>'error','error'=>'The username you entered was not found.'); + $row = $db->fetchrow(); + $return['target_name'] = $return['target_id']; + $return['target_id'] = intval($row['user_id']); + $return['current_perms'] = $session->acl_types; + } + else + { + $return['type'] = 'edit'; + $row = $db->fetchrow(); + $return['target_name'] = $return['target_id']; + $return['target_id'] = intval($row['user_id']); + $return['current_perms'] = $session->acl_merge($perms_obj->acl_types, $session->string_to_perm($row['rules'])); + } + $db->free_result(); + // Eliminate types that don't apply to this namespace + if ( $namespace ) + { + foreach ( $return['current_perms'] AS $i => $perm ) + { + if ( ( $page_id != null && $namespace != null ) && ( !in_array ( $namespace, $session->acl_scope[$i] ) && !in_array('All', $session->acl_scope[$i]) ) ) + { + // echo "// SCOPE CONTROL: eliminating: $i\n"; + unset($return['current_perms'][$i]); + unset($return['acl_types'][$i]); + unset($return['acl_descs'][$i]); + unset($return['acl_deps'][$i]); + } + } + } + break; + case ACL_TYPE_GROUP: + $q = $db->sql_query('SELECT a.rules,g.group_name,g.group_id FROM '.table_prefix.'groups AS g + LEFT JOIN '.table_prefix.'acl AS a + ON a.target_id=g.group_id + WHERE a.target_type='.ACL_TYPE_GROUP.' + AND g.group_id=\''.intval($parms['target_id']).'\' + '.$page_where_clause.';'); + if(!$q) + return(Array('mode'=>'error','error'=>mysql_error())); + if($db->numrows() < 1) + { + $return['type'] = 'new'; + $q = $db->sql_query('SELECT group_id,group_name FROM '.table_prefix.'groups WHERE group_id=\''.intval($parms['target_id']).'\';'); + if(!$q) + return(Array('mode'=>'error','error'=>mysql_error())); + if($db->numrows() < 1) + return Array('mode'=>'error','error'=>'The group ID you submitted is not valid.'); + $row = $db->fetchrow(); + $return['target_name'] = $row['group_name']; + $return['target_id'] = intval($row['group_id']); + $return['current_perms'] = $session->acl_types; + } + else + { + $return['type'] = 'edit'; + $row = $db->fetchrow(); + $return['target_name'] = $row['group_name']; + $return['target_id'] = intval($row['group_id']); + $return['current_perms'] = $session->acl_merge($session->acl_types, $session->string_to_perm($row['rules'])); + } + $db->free_result(); + // Eliminate types that don't apply to this namespace + if ( $namespace ) + { + foreach ( $return['current_perms'] AS $i => $perm ) + { + if ( ( $page_id != false && $namespace != false ) && ( !in_array ( $namespace, $session->acl_scope[$i] ) && !in_array('All', $session->acl_scope[$i]) ) ) + { + // echo "// SCOPE CONTROL: eliminating: $i\n"; //; ".print_r($namespace,true).":".print_r($page_id,true)."\n"; + unset($return['current_perms'][$i]); + unset($return['acl_types'][$i]); + unset($return['acl_descs'][$i]); + unset($return['acl_deps'][$i]); + } + } + } + //return Array('mode'=>'debug','text'=>print_r($return, true)); + break; + default: + return Array('mode'=>'error','error','Invalid ACL type ID'); + break; + } + return $return; + break; + case 'save_new': + case 'save_edit': + $q = $db->sql_query('DELETE FROM '.table_prefix.'acl WHERE target_type='.intval($parms['target_type']).' AND target_id='.intval($parms['target_id']).' + '.$page_where_clause_lite.';'); + if(!$q) + return Array('mode'=>'error','error'=>mysql_error()); + $rules = $session->perm_to_string($parms['perms']); + if ( sizeof ( $rules ) < 1 ) + { + return array( + 'mode' => 'error', + 'error' => 'Supplied rule list has a length of zero' + ); + } + $q = ($page_id && $namespace) ? 'INSERT INTO '.table_prefix.'acl ( target_type, target_id, page_id, namespace, rules ) + VALUES( '.intval($parms['target_type']).', '.intval($parms['target_id']).', \''.$db->escape($page_id).'\', \''.$db->escape($namespace).'\', \''.$db->escape($rules).'\' )' : + 'INSERT INTO '.table_prefix.'acl ( target_type, target_id, rules ) + VALUES( '.intval($parms['target_type']).', '.intval($parms['target_id']).', \''.$db->escape($rules).'\' )'; + if(!$db->sql_query($q)) return Array('mode'=>'error','error'=>mysql_error()); + return Array( + 'mode' => 'success', + 'target_type' => $parms['target_type'], + 'target_id' => $parms['target_id'], + 'target_name' => $parms['target_name'], + 'page_id' => $page_id, + 'namespace' => $namespace, + ); + break; + case 'delete': + $q = $db->sql_query('DELETE FROM '.table_prefix.'acl WHERE target_type='.intval($parms['target_type']).' AND target_id='.intval($parms['target_id']).' + '.$page_where_clause_lite.';'); + if(!$q) + return Array('mode'=>'error','error'=>mysql_error()); + return Array( + 'mode' => 'delete', + 'target_type' => $parms['target_type'], + 'target_id' => $parms['target_id'], + 'target_name' => $parms['target_name'], + 'page_id' => $page_id, + 'namespace' => $namespace, + ); + break; + default: + return Array('mode'=>'error','error'=>'Hacking attempt'); + break; + } + } + return $return; + } + + /** + * Same as PageUtils::acl_editor(), but the parms are a JSON string instead of an array. This also returns a JSON string. + * @param string $parms Same as PageUtils::acl_editor/$parms, but should be a valid JSON string. + * @return string + */ + + function acl_json($parms = '{ }') + { + global $db, $session, $paths, $template, $plugins; // Common objects + $json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE); + $parms = $json->decode($parms); + $ret = PageUtils::acl_editor($parms); + $ret = $json->encode($ret); + return $ret; + } + + /** + * A non-Javascript frontend for the ACL API. + * @param array The request data, if any, this should be in the format required by PageUtils::acl_editor() + */ + + function aclmanager($parms) + { + global $db, $session, $paths, $template, $plugins; // Common objects + ob_start(); + // Convenience + $formstart = '
'; + $formend = '
'; + $parms = PageUtils::acl_preprocess($parms); + $response = PageUtils::acl_editor($parms); + $response = PageUtils::acl_postprocess($response); + + //die('
' . htmlspecialchars(print_r($response, true)) . '
'); + + switch($response['mode']) + { + case 'debug': + echo '
' . htmlspecialchars($response['text']) . '
'; + break; + case 'stage1': + echo '

Manage page access

+

Please select who should be affected by this access rule.

'; + echo $formstart; + echo '

+

+

+

' . $template->username_field('data[target_id_user]') . '

+

What should this access rule control?

+

+

+
+ + + + +
'; + echo $formend; + break; + case 'success': + echo '
+ Permissions updated
+ The permissions for ' . $response['target_name'] . ' on this page have been updated successfully.
+ ' . $formstart . ' + + + + + + + + + ' . $formend . ' +
'; + break; + case 'delete': + echo '
+ Rule deleted
+ The selected access rule has been successfully deleted.
+ ' . $formstart . ' + + + + + + + + + ' . $formend . ' +
'; + break; + case 'seltarget': + if ( $response['type'] == 'edit' ) + { + echo '

Editing permissions

'; + } + else + { + echo '

Create new rule

'; + } + $type = ( $response['target_type'] == ACL_TYPE_GROUP ) ? 'group' : 'user'; + $scope = ( $response['page_id'] ) ? 'this page' : 'this entire site'; + echo 'This panel allows you to edit what the '.$type.' "'.$response['target_name'].'" can do on '.$scope.'. Unless you set a permission to "Deny", these permissions may be overridden by other rules.'; + echo $formstart; + $parser = $template->makeParserText( $response['template']['acl_field_begin'] ); + echo $parser->run(); + $parser = $template->makeParserText( $response['template']['acl_field_item'] ); + $cls = 'row2'; + foreach ( $response['acl_types'] as $acl_type => $value ) + { + $vars = Array( + 'FIELD_DENY_CHECKED' => '', + 'FIELD_DISALLOW_CHECKED' => '', + 'FIELD_WIKIMODE_CHECKED' => '', + 'FIELD_ALLOW_CHECKED' => '', + ); + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $vars['ROW_CLASS'] = $cls; + + switch ( $response['current_perms'][$acl_type] ) + { + case AUTH_ALLOW: + $vars['FIELD_ALLOW_CHECKED'] = 'checked="checked"'; + break; + case AUTH_WIKIMODE: + $vars['FIELD_WIKIMODE_CHECKED'] = 'checked="checked"'; + break; + case AUTH_DISALLOW: + default: + $vars['FIELD_DISALLOW_CHECKED'] = 'checked="checked"'; + break; + case AUTH_DENY: + $vars['FIELD_DENY_CHECKED'] = 'checked="checked"'; + break; + } + $vars['FIELD_NAME'] = 'data[perms][' . $acl_type . ']'; + $vars['FIELD_DESC'] = $response['acl_descs'][$acl_type]; + $parser->assign_vars($vars); + echo $parser->run(); + } + $parser = $template->makeParserText( $response['template']['acl_field_end'] ); + echo $parser->run(); + echo '
+ + + + + + +    +
'; + echo $formend; + break; + case 'error': + ob_end_clean(); + die_friendly('Error occurred', '

Error returned by permissions API:

' . htmlspecialchars($response['error']) . '
'); + break; + } + $ret = ob_get_contents(); + ob_end_clean(); + echo + $template->getHeader() . + $ret . + $template->getFooter(); + } + + /** + * Preprocessor to turn the form-submitted data from the ACL editor into something the backend can handle + * @param array The posted data + * @return array + * @access private + */ + + function acl_preprocess($parms) + { + if ( !isset($parms['mode']) ) + // Nothing to do + return $parms; + switch ( $parms['mode'] ) + { + case 'seltarget': + + // Who's affected? + $parms['target_type'] = intval( $parms['target_type'] ); + $parms['target_id'] = ( $parms['target_type'] == ACL_TYPE_GROUP ) ? $parms['target_id_grp'] : $parms['target_id_user']; + + case 'save_edit': + case 'save_new': + if ( isset($parms['act_delete_rule']) ) + { + $parms['mode'] = 'delete'; + } + + // Scope (just this page or entire site?) + if ( $parms['scope'] == 'entire_site' || ( $parms['page_id'] == 'false' && $parms['namespace'] == 'false' ) ) + { + $parms['page_id'] = false; + $parms['namespace'] = false; + } + + break; + } + + if ( isset($parms['act_go_stage1']) ) + { + $parms = array( + 'mode' => 'listgroups' + ); + } + + return $parms; + } + + function acl_postprocess($response) + { + if(!isset($response['mode'])) + { + if ( isset($response['groups']) ) + $response['mode'] = 'stage1'; + else + $response = Array( + 'mode' => 'error', + 'error' => 'Invalid action passed by API backend.', + ); + } + return $response; + } + +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/paths.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/paths.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,812 @@ +pages = Array(); + + dc_here('paths: setting up namespaces, admin nodes'); + + // DEFINE NAMESPACES HERE + // The key names should NOT EVER be changed, or Enano will be very broken + $this->nslist = Array( + 'Article' =>'', + 'User' =>'User:', + 'File' =>'File:', + 'Help' =>'Help:', + 'Admin' =>'Admin:', + 'Special' =>'Special:', + 'System' =>'Enano:', + 'Template'=>'Template:', + 'Category'=>'Category:', + 'Project' =>str_replace(' ', '_', getConfig('site_name')).':', + ); + + // ACL types + // Note: you can set any of these to AUTH_DENY to universally and unconditionally deny access to the selected action. + // These can also be added from within plugins + + $session->register_acl_type('read', AUTH_ALLOW, 'Read page(s)'); + $session->register_acl_type('post_comments', AUTH_ALLOW, 'Post comments', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('edit_comments', AUTH_ALLOW, 'Edit own comments', Array('post_comments'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('edit_page', AUTH_WIKIMODE, 'Edit page', Array('view_source'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('view_source', AUTH_WIKIMODE, 'View source', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); // Only used if the page is protected + $session->register_acl_type('mod_comments', AUTH_DISALLOW, 'Moderate comments', Array('edit_comments'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('history_view', AUTH_WIKIMODE, 'View history/diffs', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('history_rollback', AUTH_DISALLOW, 'Rollback history', Array('history_view'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('history_rollback_extra', AUTH_DISALLOW, 'Undelete page(s)', Array('history_rollback'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('protect', AUTH_DISALLOW, 'Protect page(s)', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('rename', AUTH_WIKIMODE, 'Rename page(s)', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('clear_logs', AUTH_DISALLOW, 'Clear page logs (dangerous)', Array('read', 'protect', 'even_when_protected'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('vote_delete', AUTH_ALLOW, 'Vote to delete', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('vote_reset', AUTH_DISALLOW, 'Reset delete votes', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('delete_page', AUTH_DISALLOW, 'Delete page(s)', Array(), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('set_wiki_mode', AUTH_DISALLOW, 'Set per-page wiki mode', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('password_set', AUTH_DISALLOW, 'Set password', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('password_reset', AUTH_DISALLOW, 'Disable/reset password', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('mod_misc', AUTH_DISALLOW, 'Super moderator (generate SQL backtraces, view IP addresses, and send large numbers of private messages)', Array(), 'All'); + $session->register_acl_type('edit_cat', AUTH_WIKIMODE, 'Edit categorization', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('even_when_protected', AUTH_DISALLOW, 'Allow editing, renaming, and categorization even when protected', Array('edit_page', 'rename', 'mod_comments', 'edit_cat'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('upload_files', AUTH_DISALLOW, 'Upload files', Array('create_page'), 'Article|User|Project|Template|File|Help|System|Category|Special'); + $session->register_acl_type('upload_new_version', AUTH_WIKIMODE, 'Upload new versions of files', Array('upload_files'), 'Article|User|Project|Template|File|Help|System|Category|Special'); + $session->register_acl_type('create_page', AUTH_WIKIMODE, 'Create pages', Array(), 'Article|User|Project|Template|File|Help|System|Category|Special'); + $session->register_acl_type('php_in_pages', AUTH_DISALLOW, 'Embed PHP code in pages', Array('edit_page'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('edit_acl', AUTH_DISALLOW, 'Edit access control lists', Array('read', 'post_comments', 'edit_comments', 'edit_page', 'view_source', 'mod_comments', 'history_view', 'history_rollback', 'history_rollback_extra', 'protect', 'rename', 'clear_logs', 'vote_delete', 'vote_reset', 'delete_page', 'set_wiki_mode', 'password_set', 'password_reset', 'mod_misc', 'edit_cat', 'even_when_protected', 'upload_files', 'upload_new_version', 'create_page', 'php_in_pages')); + + // DO NOT add new admin pages here! Use a plugin to call $paths->addAdminNode(); + $this->addAdminNode('General', 'General Configuration', 'GeneralConfig'); + $this->addAdminNode('General', 'File uploads', 'UploadConfig'); + $this->addAdminNode('General', 'Allowed file types', 'UploadAllowedMimeTypes'); + $this->addAdminNode('General', 'Manage Plugins', 'PluginManager'); + $this->addAdminNode('General', 'Backup database', 'DBBackup'); + $this->addAdminNode('Content', 'Manage Pages', 'PageManager'); + $this->addAdminNode('Content', 'Edit page content', 'PageEditor'); + $this->addAdminNode('Appearance', 'Manage themes', 'ThemeManager'); + $this->addAdminNode('Users', 'Manage users', 'UserManager'); + $this->addAdminNode('Users', 'Edit groups', 'GroupManager'); + $this->addAdminNode('Users', 'Ban control', 'BanControl'); + $this->addAdminNode('Users', 'Mass e-mail', 'MassEmail'); + + $code = $plugins->setHook('acl_rule_init'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + $this->wiki_mode = (int)getConfig('wiki_mode')=='1'; + $this->template_cache = Array(); + } + function pathManager() + { + $this->__construct(); + } + function init() + { + global $db, $session, $paths, $template, $plugins; // Common objects + + dc_here('paths: selecting master page data'); + + $code = $plugins->setHook('paths_init_before'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + $e = $db->sql_query('SELECT name,urlname,namespace,special,visible,comments_on,protected,delvotes,delvote_ips,wiki_mode,password FROM '.table_prefix.'pages ORDER BY name;'); + if( !$e ) + { + $db->_die('The error seems to have occured while selecting the page information. File: includes/paths.php; line: '.__LINE__); + } + while($r = $db->fetchrow()) + { + + $r['urlname_nons'] = $r['urlname']; + $r['urlname'] = $this->nslist[$r['namespace']] . $r['urlname']; // Applies the User:/File:/etc prefixes to the URL names + + if ( $r['delvotes'] == null) + { + $r['delvotes'] = 0; + } + if ( $r['protected'] == 0 || $r['protected'] == 1 ) + { + $r['really_protected'] = (int)$r['protected']; + } + else if ( $r['protected'] == 2 && getConfig('wiki_mode') == '1') + { + $r['really_protected'] = 1; + } + else if ( $r['protected'] == 2 && getConfig('wiki_mode') == '0' ) + { + $r['really_protected'] = 0; + } + + $this->pages[$r['urlname']] = $r; + $this->pages[] =& $this->pages[$r['urlname']]; + + } + $db->free_result(); + dc_here('paths: determining page ID'); + if( isset($_GET['title']) ) + { + if ( $_GET['title'] == '' && getConfig('main_page') != '' ) + { + $this->main_page(); + } + if(strstr($_GET['title'], ' ')) + { + $loc = urldecode(rawurldecode($_SERVER['REQUEST_URI'])); + $loc = str_replace(' ', '_', $loc); + $loc = str_replace('+', '_', $loc); + $loc = str_replace('%20', '_', $loc); + redirect($loc, 'Redirecting...', 'Space detected in the URL, please wait whilst you are redirected', 0); + exit; + } + $url_namespace_special = substr($_GET['title'], 0, strlen($this->nslist['Special']) ); + $url_namespace_template = substr($_GET['title'], 0, strlen($this->nslist['Template']) ); + if($url_namespace_special == $this->nslist['Special'] || $url_namespace_template == $this->nslist['Template'] ) + { + $ex = explode('/', $_GET['title']); + $this->page = $ex[0]; + } + else + { + $this->page = $_GET['title']; + } + $this->fullpage = $_GET['title']; + } + elseif( isset($_SERVER['PATH_INFO']) ) + { + $pi = explode('/', $_SERVER['PATH_INFO']); + + if( !isset($pi[1]) || (isset($pi[1]) && $pi[1] == '' && getConfig('main_page') != '') ) + { + $this->main_page(); + } + if( strstr($pi[1], ' ') ) + { + $loc = str_replace(' ', '_', urldecode(rawurldecode($_SERVER['REQUEST_URI']))); + $loc = str_replace('+', '_', $loc); + $loc = str_replace('%20', '_', $loc); + redirect($loc, 'Redirecting...', 'Please wait whilst you are redirected', 3); + exit; + } + unset($pi[0]); + if( substr($pi[1], 0, strlen($this->nslist['Special'])) == $this->nslist['Special'] || substr($pi[1], 0, strlen($this->nslist['Template'])) == $this->nslist['Template'] ) + { + $pi2 = $pi[1]; + } + else + { + $pi2 = implode('/', $pi); + } + $this->page = $pi2; + $this->fullpage = implode('/', $pi); + } + else + { + $k = array_keys($_GET); + foreach($k as $c) + { + if(substr($c, 0, 1) == '/') + { + $this->page = substr($c, 1, strlen($c)); + + // Bugfix for apache somehow passing dots as underscores + global $mime_types; + + $exts = array_keys($mime_types); + $exts = '(' . implode('|', $exts) . ')'; + + if ( preg_match( '#_'.$exts.'#i', $this->page ) ) + { + $this->page = preg_replace( '#_'.$exts.'#i', '.\\1', $this->page ); + } + + $this->fullpage = $this->page; + + if(substr($this->page, 0, strlen($this->nslist['Special']))==$this->nslist['Special'] || substr($this->page, 0, strlen($this->nslist['Template']))==$this->nslist['Template']) + { + $ex = explode('/', $this->page); + $this->page = $ex[0]; + } + if(strstr($this->page, ' ')) + { + $loc = str_replace(' ', '_', urldecode(rawurldecode($_SERVER['REQUEST_URI']))); + $loc = str_replace('+', '_', $loc); + $loc = str_replace('%20', '_', $loc); + redirect($loc, 'Redirecting...', 'Space in the URL detected, please wait whilst you are redirected', 0); + exit; + } + break; + } + } + if(!$this->page && !($this->page == '' && getConfig('main_page') == '')) + { + $this->main_page(); + } + } + + dc_here('paths: setting $paths->cpage'); + + if(isset($this->pages[$this->page])) + { + dc_here('paths: page existence verified, our page ID is: '.$this->page); + $this->page_exists = true; + $this->cpage = $this->pages[$this->page]; + $this->namespace = $this->cpage['namespace']; + if(!isset($this->cpage['wiki_mode'])) $this->cpage['wiki_mode'] = 2; + + // Determine the wiki mode for this page, now that we have this->cpage established + if($this->cpage['wiki_mode'] == 2) + { + $this->wiki_mode = (int)getConfig('wiki_mode'); + } + else + { + $this->wiki_mode = $this->cpage['wiki_mode']; + } + // Allow the user to create/modify his user page uncondtionally (admins can still protect the page) + if($this->page == $this->nslist['User'].str_replace(' ', '_', $session->username)) + { + $this->wiki_mode = true; + } + // And above all, if the site requires wiki mode to be off for non-logged-in users, disable it now + if(getConfig('wiki_mode_require_login')=='1' && !$session->user_logged_in) + { + $this->wiki_mode = false; + } + if($this->cpage['protected'] == 2) + { + // The page is semi-protected, determine permissions + if($session->user_logged_in && $session->reg_time + 60*60*24*4 < time()) + { + $this->page_protected = 0; + } + else + { + $this->page_protected = 1; + } + } + else + { + $this->page_protected = $this->cpage['protected']; + } + } + else + { + dc_here('paths: page doesn\'t exist, creating new page in memory
our page ID is: '.$this->page); + $this->page_exists = false; + $this->cpage = Array( + 'name'=>str_replace('_', ' ', $this->page), + 'urlname'=>$this->page, + 'namespace'=>'Article', + 'special'=>0, + 'visible'=>0, + 'comments_on'=>1, + 'protected'=>0, + 'delvotes'=>0, + 'delvote_ips'=>'', + 'wiki_mode'=>2, + ); + // Look for a namespace prefix in the urlname, and assign a different namespace, if necessary + $k = array_keys($this->nslist); + for($i=0;$inslist);$i++) + { + $ln = strlen($this->nslist[$k[$i]]); + if( substr($this->page, 0, $ln) == $this->nslist[$k[$i]] ) + { + $this->cpage['namespace'] = $k[$i]; + $this->cpage['urlname_nons'] = substr($this->page, strlen($this->nslist[$this->cpage['namespace']]), strlen($this->page)); + if(!isset($this->cpage['wiki_mode'])) + { + $this->cpage['wiki_mode'] = 2; + } + } + } + $this->namespace = $this->cpage['namespace']; + + if($this->namespace=='System') + { + $this->cpage['protected'] = 1; + } + if($this->namespace=='Special') + { + // Can't load nonexistent pages + $this->main_page(); + } + // Allow the user to create/modify his user page uncondtionally (admins can still protect the page) + if($this->page == $this->nslist['User'].str_replace(' ', '_', $session->username)) + { + $this->wiki_mode = true; + } + } + // This is used in the admin panel to keep track of form submission targets + $this->cpage['module'] = $this->cpage['urlname']; + + // Page is set up, call any hooks + $code = $plugins->setHook('page_set'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + $session->init_permissions(); + } + + function add_page($flags) + { + //dc_dump($flags, 'paths: page added by plugin:'); + $flags['urlname_nons'] = $flags['urlname']; + $flags['urlname'] = $this->nslist[$flags['namespace']] . $flags['urlname']; // Applies the User:/File:/etc prefixes to the URL names + $pages_len = sizeof($this->pages)/2; + $this->pages[$pages_len] = $flags; + $this->pages[$flags['urlname']] =& $this->pages[$pages_len]; + } + + function main_page() + { + if( is_string(getConfig('main_page')) ) + { + header('Location: '.makeUrl(getConfig('main_page'))); + die('If you aren\'t redirected, click here.'); + } + else + { + header('Location: '.makeUrl($this->pages[0]['urlname'])); + die('If you aren\'t redirected, click here.'); + } + exit; + } + + function sysmsg($n) + { + global $db, $session, $paths, $template, $plugins; // Common objects + dc_here('paths: system message requested: '.$n); + $q = $db->sql_query('SELECT page_text, char_tag FROM '.table_prefix.'page_text WHERE page_id=\''.$db->escape($n).'\' AND namespace=\'System\''); + if( !$q ) + { + $db->_die('Error during generic selection of system page data.'); + } + if($db->numrows() < 1) + { + return false; + //$db->_die('Error during generic selection of system page data: there were no rows in the text table that matched the page text query.'); + } + $r = $db->fetchrow(); + $db->free_result(); + $message = $r['page_text']; + + $message = preg_replace('/(.*?)<\/noinclude>/is', '', $message); + + return $message; + } + function get_pageid_from_url() + { + if(isset($_GET['title'])) + { + if( $_GET['title'] == '' && getConfig('main_page') != '' ) + { + $this->main_page(); + } + if(strstr($_GET['title'], ' ')) + { + $loc = urldecode(rawurldecode($_SERVER['REQUEST_URI'])); + $loc = str_replace(' ', '_', $loc); + $loc = str_replace('+', '_', $loc); + header('Location: '.$loc); + exit; + } + $ret = $_GET['title']; + } + elseif(isset($_SERVER['PATH_INFO'])) + { + $pi = explode('/', $_SERVER['PATH_INFO']); + + if(!isset($pi[1]) || (isset($pi[1]) && $pi[1] == '')) + { + return false; + } + + if(strstr($pi[1], ' ')) + { + $loc = urldecode(rawurldecode($_SERVER['REQUEST_URI'])); + $loc = str_replace(' ', '_', $loc); + $loc = str_replace('+', '_', $loc); + header('Location: '.$loc); + exit; + } + if( !( substr($pi[1], 0, strlen($this->nslist['Special'])) == $this->nslist['Special'] ) ) + { + unset($pi[0]); + $pi[1] = implode('/', $pi); + } + $ret = $pi[1]; + } + else + { + $k = array_keys($_GET); + foreach($k as $c) + { + if(substr($c, 0, 1) == '/') + { + $ret = substr($c, 1, strlen($c)); + if(substr($ret, 0, strlen($this->nslist['Special'])) == $this->nslist['Special'] || + substr($ret, 0, strlen($this->nslist['Admin'])) == $this->nslist['Admin']) + { + $ret = explode('/', $ret); + $ret = $ret[0]; + } + break; + } + } + } + + return ( isset($ret) ) ? $ret : false; + } + // Parses a (very carefully formed) array into Javascript code compatible with the Tigra Tree Menu used in the admin menu + function parseAdminTree() + { + $k = array_keys($this->admin_tree); + $i = 0; + $ret = ''; + $ret .= "var TREE_ITEMS = [\n ['Administration panel home', 'javascript:ajaxPage(\'".$this->nslist['Admin']."Home\');',\n "; + foreach($k as $key) + { + $i++; + $ret .= "['".$key."', 'javascript:trees[0].toggle($i)', \n"; + foreach($this->admin_tree[$key] as $c) + { + $i++; + $ret .= " ['".$c['name']."', 'javascript:ajaxPage(\\'".$this->nslist['Admin'].$c['pageid']."\\');'],\n"; + } + $ret .= " ],\n"; + } + $ret .= " ['Log out of admin panel', 'javascript:ajaxPage(\\'".$this->nslist['Admin']."AdminLogout\\');'],\n"; + // I used this while I painstakingly wrote the Runt code that auto-expands certain nodes based on the value of a bitfield stored in a cookie. *shudders* + // $ret .= " ['(debug) Clear menu bitfield', 'javascript:createCookie(\\'admin_menu_state\\', \\'1\\', 365);'],\n"; + $ret .= "]\n];"; + return $ret; + } + function addAdminNode($section, $page_title, $url) + { + if(!isset($this->admin_tree[$section])) + { + $this->admin_tree[$section] = Array(); + } + $this->admin_tree[$section][] = Array( + 'name'=>$page_title, + 'pageid'=>$url + ); + } + function getParam($id = 0) + { + if(isset($_SERVER['PATH_INFO'])) + { + $pi = explode('/', $_SERVER['PATH_INFO']); + $id = $id + 2; + return isset($pi[$id]) ? $pi[$id] : false; + } + else if( isset($_GET['title']) ) + { + $pi = explode('/', $_GET['title']); + $id = $id + 1; + return isset($pi[$id]) ? $pi[$id] : false; + } + else + { + $k = array_keys($_GET); + foreach($k as $c) + { + if(substr($c, 0, 1) == '/') + { + + // Bugfix for apache somehow passing dots as underscores + global $mime_types; + $exts = array_keys($mime_types); + $exts = '(' . implode('|', $exts) . ')'; + if ( preg_match( '#_'.$exts.'#i', $c ) ) + $c = preg_replace( '#_'.$exts.'#i', '.\\1', $c ); + + $pi = explode('/', $c); + $id = $id + 2; + return isset($pi[$id]) ? $pi[$id] : false; + } + } + return false; + } + } + + function getAllParams() + { + if(isset($_SERVER['PATH_INFO'])) + { + $pi = explode('/', $_SERVER['PATH_INFO']); + unset($pi[0], $pi[1]); + return implode('/', $pi); + } + else if( isset($_GET['title']) ) + { + $pi = explode('/', $_GET['title']); + unset($pi[0]); + return implode('/', $pi); + } + else + { + $k = array_keys($_GET); + foreach($k as $c) + { + if(substr($c, 0, 1) == '/') + { + // Bugfix for apache somehow passing dots as underscores + global $mime_types; + $exts = array_keys($mime_types); + $exts = '(' . implode('|', $exts) . ')'; + if ( preg_match( '#_'.$exts.'#i', $c ) ) + $c = preg_replace( '#_'.$exts.'#i', '.\\1', $c ); + + $pi = explode('/', $c); + unset($pi[0], $pi[1]); + return implode('/', $pi); + } + } + return false; + } + } + + /** + * Creates a new namespace in memory + * @param string $id the namespace ID + * @param string $prefix the URL prefix, must not be blank or already used + * @return bool true on success false on failure + */ + + function create_namespace($id, $prefix) + { + if(in_array($prefix, $this->nslist)) + { + // echo 'Warning: pathManager::create_namespace: Prefix "'.$prefix.'" is already taken
'; + return false; + } + if( isset($this->nslist[$id]) ) + { + // echo 'Warning: pathManager::create_namespace: Namespace ID "'.$prefix.'" is already taken
'; + return false; + } + $this->nslist[$id] = $prefix; + } + + /** + * Fetches the page texts for searching + */ + + function fetch_page_search_texts() + { + global $db, $session, $paths, $template, $plugins; // Common objects + $texts = Array(); + $q = $db->sql_query('SELECT t.page_id,t.namespace,t.page_text,t.char_tag FROM '.table_prefix.'page_text AS t + LEFT JOIN '.table_prefix.'pages AS p + ON t.page_id=p.urlname + WHERE p.namespace=t.namespace + AND ( p.password=\'\' OR p.password=\'da39a3ee5e6b4b0d3255bfef95601890afd80709\' ) + AND p.visible=1;'); // Only indexes "visible" pages + + if( !$q ) + { + return false; + } + while($row = $db->fetchrow()) + { + $pid = $this->nslist[$row['namespace']] . $row['page_id']; + $texts[$pid] = $row['page_text']; + } + $db->free_result(); + + return $texts; + } + + /** + * Fetches a MySQL search query to use for Searcher::searchMySQL() + */ + + function fetch_page_search_resource() + { + global $db, $session, $paths, $template, $plugins; // Common objects + // sha1('') returns "da39a3ee5e6b4b0d3255bfef95601890afd80709" + $texts = 'SELECT t.page_text,CONCAT(\'ns=\',t.namespace,\';pid=\',t.page_id) FROM '.table_prefix.'page_text AS t + LEFT JOIN '.table_prefix.'pages AS p + ON ( t.page_id=p.urlname AND t.namespace=p.namespace ) + WHERE p.namespace=t.namespace + AND ( p.password=\'\' OR p.password=\'da39a3ee5e6b4b0d3255bfef95601890afd80709\' ) + AND p.visible=1;'; // Only indexes "visible" pages + return $texts; + } + + /** + * Rebuilds the search index + */ + + function rebuild_search_index() + { + global $db, $session, $paths, $template, $plugins; // Common objects + $search = new Searcher(); + $texts = Array(); + $textq = $db->sql_unbuffered_query($this->fetch_page_search_resource()); + if(!$textq) $db->_die(''); + while($row = $db->fetchrow_num()) + { + $texts[(string)$row[1]] = $row[0]; + } + $search->buildIndex($texts); + // echo '
'.print_r($search->index, true).'
'; + // return; + $q = $db->sql_query('DELETE FROM '.table_prefix.'search_index'); + if(!$q) return false; + $secs = Array(); + $q = 'INSERT INTO '.table_prefix.'search_index(word,page_names) VALUES'; + foreach($search->index as $word => $pages) + { + $secs[] = '(\''.$db->escape($word).'\', \''.$db->escape($pages).'\')'; + } + $q .= implode(',', $secs); + unset($secs); + $q .= ';'; + $result = $db->sql_query($q); + $db->free_result(); + if($result) + return true; + else + $db->_die('The search index was trying to rebuild itself when the error occured.'); + } + + /** + * Partially rebuilds the search index, removing/inserting entries only for the current page + * @param string $page_id + * @param string $namespace + */ + + function rebuild_page_index($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$db->sql_query('SELECT page_text FROM '.table_prefix.'page_text + WHERE page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\';')) + { + return $db->get_error(); + } + $row = $db->fetchrow(); + $db->free_result(); + $search = new Searcher(); + $search->buildIndex(Array("ns={$namespace};pid={$page_id}"=>$row['page_text'])); + $new_index = $search->index; + + $keys = array_keys($search->index); + foreach($keys as $i => $k) + { + $c =& $keys[$i]; + $c = hexencode($c, '', ''); + } + $keys = "word=0x" . implode ( " OR word=0x", $keys ) . ""; + + // Zap the cache + $cache = array_keys($search->index); + if ( count($cache) < 1 ) + { + return false; + } + $cache = "query LIKE '%" . implode ( "%' OR query LIKE '%", $cache ) . "%'"; + $db->sql_query('DELETE FROM '.table_prefix.'search_cache WHERE '.$cache); + + $query = $db->sql_query('SELECT word,page_names FROM '.table_prefix.'search_index WHERE '.$keys.';'); + + while($row = $db->fetchrow()) + { + $row['word'] = rtrim($row['word'], "\0"); + $new_index[ $row['word'] ] = $row['page_names'] . ',' . $search->index[ $row['word'] ]; + } + $db->free_result(); + + $db->sql_query('DELETE FROM '.table_prefix.'search_index WHERE '.$keys.';'); + + $secs = Array(); + $q = 'INSERT INTO '.table_prefix.'search_index(word,page_names) VALUES'; + foreach($new_index as $word => $pages) + { + $secs[] = '(\''.$db->escape($word).'\', \''.$db->escape($pages).'\')'; + } + $q .= implode(',', $secs); + unset($secs); + $q .= ';'; + if(!$db->check_query($q)) + { + die('BUG: PathManager::rebuild_page_index: Query rejected by SQL parser:
'.$q.'
'); + } + $result = $db->sql_query($q); + if($result) + return true; + else + $db->_die('The search index was trying to rebuild itself when the error occured.'); + + } + + /** + * Creates an instance of the Searcher class, including index info + * @return object + */ + + function makeSearcher($match_case = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $search = new Searcher(); + $q = $db->sql_query('SELECT word,page_names FROM '.table_prefix.'search_index;'); + if(!$q) + { + echo $db->get_error(); + return false; + } + $idx = Array(); + while($row = $db->fetchrow($q)) + { + $row['word'] = rtrim($row['word'], "\0"); + $idx[$row['word']] = $row['page_names']; + } + $db->free_result(); + $search->index = $idx; + if($match_case) + $search->match_case = true; + return $search; + } + + /** + * Creates an associative array filled with the values of all the page titles + * @return array + */ + + function get_page_titles() + { + $texts = Array(); + for ( $i = 0; $i < sizeof($this->pages) / 2; $i++ ) + { + $texts[$this->pages[$i]['urlname']] = $this->pages[$i]['name']; + } + return $texts; + } + + /** + * Creates an instance of the Searcher class, including index info for page titles + * @return object + */ + + function makeTitleSearcher($match_case = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $search = new Searcher(); + $texts = $this->get_page_titles(); + $search->buildIndex($texts); + if($match_case) + $search->match_case = true; + return $search; + } + +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/paths.php~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/paths.php~ Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,808 @@ +pages = Array(); + + dc_here('paths: setting up namespaces, admin nodes'); + + // DEFINE NAMESPACES HERE + // The key names should NOT EVER be changed, or Enano will be very broken + $this->nslist = Array( + 'Article' =>'', + 'User' =>'User:', + 'File' =>'File:', + 'Help' =>'Help:', + 'Admin' =>'Admin:', + 'Special' =>'Special:', + 'System' =>'Enano:', + 'Template'=>'Template:', + 'Category'=>'Category:', + 'Project' =>str_replace(' ', '_', getConfig('site_name')).':', + ); + + // ACL types + // Note: you can set any of these to AUTH_DENY to universally and unconditionally deny access to the selected action. + // These can also be added from within plugins + + $session->register_acl_type('read', AUTH_ALLOW, 'Read page(s)'); + $session->register_acl_type('post_comments', AUTH_ALLOW, 'Post comments', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('edit_comments', AUTH_ALLOW, 'Edit own comments', Array('post_comments'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('edit_page', AUTH_WIKIMODE, 'Edit page', Array('view_source'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('view_source', AUTH_WIKIMODE, 'View source', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); // Only used if the page is protected + $session->register_acl_type('mod_comments', AUTH_DISALLOW, 'Moderate comments', Array('edit_comments'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('history_view', AUTH_WIKIMODE, 'View history/diffs', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('history_rollback', AUTH_DISALLOW, 'Rollback history', Array('history_view'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('history_rollback_extra', AUTH_DISALLOW, 'Undelete page(s)', Array('history_rollback'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('protect', AUTH_DISALLOW, 'Protect page(s)', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('rename', AUTH_WIKIMODE, 'Rename page(s)', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('clear_logs', AUTH_DISALLOW, 'Clear page logs (dangerous)', Array('read', 'protect', 'even_when_protected'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('vote_delete', AUTH_ALLOW, 'Vote to delete', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('vote_reset', AUTH_DISALLOW, 'Reset delete votes', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('delete_page', AUTH_DISALLOW, 'Delete page(s)', Array(), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('set_wiki_mode', AUTH_DISALLOW, 'Set per-page wiki mode', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('password_set', AUTH_DISALLOW, 'Set password', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('password_reset', AUTH_DISALLOW, 'Disable/reset password', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('mod_misc', AUTH_DISALLOW, 'Super moderator (generate SQL backtraces, view IP addresses, and send large numbers of private messages)', Array(), 'All'); + $session->register_acl_type('edit_cat', AUTH_WIKIMODE, 'Edit categorization', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('even_when_protected', AUTH_DISALLOW, 'Allow editing, renaming, and categorization even when protected', Array('edit_page', 'rename', 'mod_comments', 'edit_cat'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('upload_files', AUTH_DISALLOW, 'Upload files', Array('create_page'), 'Article|User|Project|Template|File|Help|System|Category|Special'); + $session->register_acl_type('upload_new_version', AUTH_WIKIMODE, 'Upload new versions of files', Array('upload_files'), 'Article|User|Project|Template|File|Help|System|Category|Special'); + $session->register_acl_type('create_page', AUTH_WIKIMODE, 'Create pages', Array(), 'Article|User|Project|Template|File|Help|System|Category|Special'); + $session->register_acl_type('php_in_pages', AUTH_DISALLOW, 'Embed PHP code in pages', Array('edit_page'), 'Article|User|Project|Template|File|Help|System|Category'); + $session->register_acl_type('edit_acl', AUTH_DISALLOW, 'Edit access control lists', Array('read', 'post_comments', 'edit_comments', 'edit_page', 'view_source', 'mod_comments', 'history_view', 'history_rollback', 'history_rollback_extra', 'protect', 'rename', 'clear_logs', 'vote_delete', 'vote_reset', 'delete_page', 'set_wiki_mode', 'password_set', 'password_reset', 'mod_misc', 'edit_cat', 'even_when_protected', 'upload_files', 'upload_new_version', 'create_page', 'php_in_pages')); + + // DO NOT add new admin pages here! Use a plugin to call $paths->addAdminNode(); + $this->addAdminNode('General', 'General Configuration', 'GeneralConfig'); + $this->addAdminNode('General', 'File uploads', 'UploadConfig'); + $this->addAdminNode('General', 'Allowed file types', 'UploadAllowedMimeTypes'); + $this->addAdminNode('General', 'Manage Plugins', 'PluginManager'); + $this->addAdminNode('General', 'Backup database', 'DBBackup'); + $this->addAdminNode('Content', 'Manage Pages', 'PageManager'); + $this->addAdminNode('Content', 'Edit page content', 'PageEditor'); + $this->addAdminNode('Appearance', 'Manage themes', 'ThemeManager'); + $this->addAdminNode('Users', 'Manage users', 'UserManager'); + $this->addAdminNode('Users', 'Edit groups', 'GroupManager'); + $this->addAdminNode('Users', 'Ban control', 'BanControl'); + $this->addAdminNode('Users', 'Mass e-mail', 'MassEmail'); + + $code = $plugins->setHook('acl_rule_init'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + $this->wiki_mode = (int)getConfig('wiki_mode')=='1'; + $this->template_cache = Array(); + } + function pathManager() + { + $this->__construct(); + } + function init() + { + global $db, $session, $paths, $template, $plugins; // Common objects + + dc_here('paths: selecting master page data'); + + $code = $plugins->setHook('paths_init_before'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + $e = $db->sql_query('SELECT name,urlname,namespace,special,visible,comments_on,protected,delvotes,delvote_ips,wiki_mode,password FROM '.table_prefix.'pages ORDER BY name;'); + if( !$e ) + { + $db->_die('The error seems to have occured while selecting the page information. File: includes/paths.php; line: '.__LINE__); + } + while($r = $db->fetchrow()) + { + + $r['urlname_nons'] = $r['urlname']; + $r['urlname'] = $this->nslist[$r['namespace']] . $r['urlname']; // Applies the User:/File:/etc prefixes to the URL names + + if ( $r['delvotes'] == null) + { + $r['delvotes'] = 0; + } + if ( $r['protected'] == 0 || $r['protected'] == 1 ) + { + $r['really_protected'] = (int)$r['protected']; + } + else if ( $r['protected'] == 2 && getConfig('wiki_mode') == '1') + { + $r['really_protected'] = 1; + } + else if ( $r['protected'] == 2 && getConfig('wiki_mode') == '0' ) + { + $r['really_protected'] = 0; + } + + $this->pages[$r['urlname']] = $r; + $this->pages[] =& $this->pages[$r['urlname']]; + + } + $db->free_result(); + dc_here('paths: determining page ID'); + if( isset($_GET['title']) ) + { + if ( $_GET['title'] == '' && getConfig('main_page') != '' ) + { + $this->main_page(); + } + if(strstr($_GET['title'], ' ')) + { + $loc = urldecode(rawurldecode($_SERVER['REQUEST_URI'])); + $loc = str_replace(' ', '_', $loc); + $loc = str_replace('+', '_', $loc); + $loc = str_replace('%20', '_', $loc); + redirect($loc, 'Redirecting...', 'Space detected in the URL, please wait whilst you are redirected', 0); + exit; + } + $url_namespace_special = substr($_GET['title'], 0, strlen($this->nslist['Special']) ); + $url_namespace_template = substr($_GET['title'], 0, strlen($this->nslist['Template']) ); + if($url_namespace_special == $this->nslist['Special'] || $url_namespace_template == $this->nslist['Template'] ) + { + $ex = explode('/', $_GET['title']); + $this->page = $ex[0]; + } + else + { + $this->page = $_GET['title']; + } + $this->fullpage = $_GET['title']; + } + elseif( isset($_SERVER['PATH_INFO']) ) + { + $pi = explode('/', $_SERVER['PATH_INFO']); + + if( !isset($pi[1]) || (isset($pi[1]) && $pi[1] == '' && getConfig('main_page') != '') ) + { + $this->main_page(); + } + if( strstr($pi[1], ' ') ) + { + $loc = str_replace(' ', '_', urldecode(rawurldecode($_SERVER['REQUEST_URI']))); + $loc = str_replace('+', '_', $loc); + $loc = str_replace('%20', '_', $loc); + redirect($loc, 'Redirecting...', 'Please wait whilst you are redirected', 3); + exit; + } + unset($pi[0]); + if( substr($pi[1], 0, strlen($this->nslist['Special'])) == $this->nslist['Special'] || substr($pi[1], 0, strlen($this->nslist['Template'])) == $this->nslist['Template'] ) + { + $pi2 = $pi[1]; + } + else + { + $pi2 = implode('/', $pi); + } + $this->page = $pi2; + $this->fullpage = implode('/', $pi); + } + else + { + $k = array_keys($_GET); + foreach($k as $c) + { + if(substr($c, 0, 1) == '/') + { + $this->page = substr($c, 1, strlen($c)); + + // Bugfix for apache somehow passing dots as underscores + global $mime_types; + $exts = array_keys($mime_types); + $exts = '(' . implode('|', $exts) . ')'; + if ( preg_match( '#_'.$exts.'#i', $this->page ) ) + $this->page = preg_replace( '#_'.$exts.'#i', '.\\1', $this->page ); + + $this->fullpage = $this->page; + + if(substr($this->page, 0, strlen($this->nslist['Special']))==$this->nslist['Special'] || substr($this->page, 0, strlen($this->nslist['Template']))==$this->nslist['Template']) + { + $ex = explode('/', $this->page); + $this->page = $ex[0]; + } + if(strstr($this->page, ' ')) + { + $loc = str_replace(' ', '_', urldecode(rawurldecode($_SERVER['REQUEST_URI']))); + $loc = str_replace('+', '_', $loc); + $loc = str_replace('%20', '_', $loc); + redirect($loc, 'Redirecting...', 'Space in the URL detected, please wait whilst you are redirected', 0); + exit; + } + break; + } + } + if(!$this->page && !($this->page == '' && getConfig('main_page') == '')) + { + $this->main_page(); + } + } + + dc_here('paths: setting $paths->cpage'); + + if(isset($this->pages[$this->page])) + { + dc_here('paths: page existence verified, our page ID is: '.$this->page); + $this->page_exists = true; + $this->cpage = $this->pages[$this->page]; + $this->namespace = $this->cpage['namespace']; + if(!isset($this->cpage['wiki_mode'])) $this->cpage['wiki_mode'] = 2; + + // Determine the wiki mode for this page, now that we have this->cpage established + if($this->cpage['wiki_mode'] == 2) + { + $this->wiki_mode = (int)getConfig('wiki_mode'); + } + else + { + $this->wiki_mode = $this->cpage['wiki_mode']; + } + // Allow the user to create/modify his user page uncondtionally (admins can still protect the page) + if($this->page == $this->nslist['User'].str_replace(' ', '_', $session->username)) + { + $this->wiki_mode = true; + } + // And above all, if the site requires wiki mode to be off for non-logged-in users, disable it now + if(getConfig('wiki_mode_require_login')=='1' && !$session->user_logged_in) + { + $this->wiki_mode = false; + } + if($this->cpage['protected'] == 2) + { + // The page is semi-protected, determine permissions + if($session->user_logged_in && $session->reg_time + 60*60*24*4 < time()) + { + $this->page_protected = 0; + } + else + { + $this->page_protected = 1; + } + } + else + { + $this->page_protected = $this->cpage['protected']; + } + } + else + { + dc_here('paths: page doesn\'t exist, creating new page in memory
our page ID is: '.$this->page); + $this->page_exists = false; + $this->cpage = Array( + 'name'=>str_replace('_', ' ', $this->page), + 'urlname'=>$this->page, + 'namespace'=>'Article', + 'special'=>0, + 'visible'=>0, + 'comments_on'=>1, + 'protected'=>0, + 'delvotes'=>0, + 'delvote_ips'=>'', + 'wiki_mode'=>2, + ); + // Look for a namespace prefix in the urlname, and assign a different namespace, if necessary + $k = array_keys($this->nslist); + for($i=0;$inslist);$i++) + { + $ln = strlen($this->nslist[$k[$i]]); + if( substr($this->page, 0, $ln) == $this->nslist[$k[$i]] ) + { + $this->cpage['namespace'] = $k[$i]; + $this->cpage['urlname_nons'] = substr($this->page, strlen($this->nslist[$this->cpage['namespace']]), strlen($this->page)); + if(!isset($this->cpage['wiki_mode'])) + { + $this->cpage['wiki_mode'] = 2; + } + } + } + $this->namespace = $this->cpage['namespace']; + + if($this->namespace=='System') + { + $this->cpage['protected'] = 1; + } + if($this->namespace=='Special') + { + // Can't load nonexistent pages + $this->main_page(); + } + // Allow the user to create/modify his user page uncondtionally (admins can still protect the page) + if($this->page == $this->nslist['User'].str_replace(' ', '_', $session->username)) + { + $this->wiki_mode = true; + } + } + // This is used in the admin panel to keep track of form submission targets + $this->cpage['module'] = $this->cpage['urlname']; + + // Page is set up, call any hooks + $code = $plugins->setHook('page_set'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + $session->init_permissions(); + } + + function add_page($flags) + { + //dc_dump($flags, 'paths: page added by plugin:'); + $flags['urlname_nons'] = $flags['urlname']; + $flags['urlname'] = $this->nslist[$flags['namespace']] . $flags['urlname']; // Applies the User:/File:/etc prefixes to the URL names + $pages_len = sizeof($this->pages)/2; + $this->pages[$pages_len] = $flags; + $this->pages[$flags['urlname']] =& $this->pages[$pages_len]; + } + + function main_page() + { + if( is_string(getConfig('main_page')) ) + { + header('Location: '.makeUrl(getConfig('main_page'))); + die('If you aren\'t redirected, click here.'); + } + else + { + header('Location: '.makeUrl($this->pages[0]['urlname'])); + die('If you aren\'t redirected, click here.'); + } + exit; + } + + function sysmsg($n) + { + global $db, $session, $paths, $template, $plugins; // Common objects + dc_here('paths: system message requested: '.$n); + $q = $db->sql_query('SELECT page_text, char_tag FROM '.table_prefix.'page_text WHERE page_id=\''.$db->escape($n).'\' AND namespace=\'System\''); + if( !$q ) + { + $db->_die('Error during generic selection of system page data.'); + } + if($db->numrows() < 1) + { + return false; + //$db->_die('Error during generic selection of system page data: there were no rows in the text table that matched the page text query.'); + } + $r = $db->fetchrow(); + $db->free_result(); + $message = $r['page_text']; + + $message = preg_replace('/(.*?)<\/noinclude>/is', '', $message); + + return $message; + } + function get_pageid_from_url() + { + if(isset($_GET['title'])) + { + if( $_GET['title'] == '' && getConfig('main_page') != '' ) + { + $this->main_page(); + } + if(strstr($_GET['title'], ' ')) + { + $loc = urldecode(rawurldecode($_SERVER['REQUEST_URI'])); + $loc = str_replace(' ', '_', $loc); + $loc = str_replace('+', '_', $loc); + header('Location: '.$loc); + exit; + } + $ret = $_GET['title']; + } + elseif(isset($_SERVER['PATH_INFO'])) + { + $pi = explode('/', $_SERVER['PATH_INFO']); + + if(!isset($pi[1]) || (isset($pi[1]) && $pi[1] == '')) + { + return false; + } + + if(strstr($pi[1], ' ')) + { + $loc = urldecode(rawurldecode($_SERVER['REQUEST_URI'])); + $loc = str_replace(' ', '_', $loc); + $loc = str_replace('+', '_', $loc); + header('Location: '.$loc); + exit; + } + if( !( substr($pi[1], 0, strlen($this->nslist['Special'])) == $this->nslist['Special'] ) ) + { + unset($pi[0]); + $pi[1] = implode('/', $pi); + } + $ret = $pi[1]; + } + else + { + $k = array_keys($_GET); + foreach($k as $c) + { + if(substr($c, 0, 1) == '/') + { + $ret = substr($c, 1, strlen($c)); + if(substr($ret, 0, strlen($this->nslist['Special'])) == $this->nslist['Special'] || + substr($ret, 0, strlen($this->nslist['Admin'])) == $this->nslist['Admin']) + { + $ret = explode('/', $ret); + $ret = $ret[0]; + } + break; + } + } + } + + return ( isset($ret) ) ? $ret : false; + } + // Parses a (very carefully formed) array into Javascript code compatible with the Tigra Tree Menu used in the admin menu + function parseAdminTree() + { + $k = array_keys($this->admin_tree); + $i = 0; + $ret = ''; + $ret .= "var TREE_ITEMS = [\n ['Administration panel home', 'javascript:ajaxPage(\'".$this->nslist['Admin']."Home\');',\n "; + foreach($k as $key) + { + $i++; + $ret .= "['".$key."', 'javascript:trees[0].toggle($i)', \n"; + foreach($this->admin_tree[$key] as $c) + { + $i++; + $ret .= " ['".$c['name']."', 'javascript:ajaxPage(\\'".$this->nslist['Admin'].$c['pageid']."\\');'],\n"; + } + $ret .= " ],\n"; + } + $ret .= " ['Log out of admin panel', 'javascript:ajaxPage(\\'".$this->nslist['Admin']."AdminLogout\\');'],\n"; + // I used this while I painstakingly wrote the Runt code that auto-expands certain nodes based on the value of a bitfield stored in a cookie. *shudders* + // $ret .= " ['(debug) Clear menu bitfield', 'javascript:createCookie(\\'admin_menu_state\\', \\'1\\', 365);'],\n"; + $ret .= "]\n];"; + return $ret; + } + function addAdminNode($section, $page_title, $url) + { + if(!isset($this->admin_tree[$section])) + { + $this->admin_tree[$section] = Array(); + } + $this->admin_tree[$section][] = Array( + 'name'=>$page_title, + 'pageid'=>$url + ); + } + function getParam($id = 0) + { + if(isset($_SERVER['PATH_INFO'])) + { + $pi = explode('/', $_SERVER['PATH_INFO']); + $id = $id + 2; + return isset($pi[$id]) ? $pi[$id] : false; + } + else if( isset($_GET['title']) ) + { + $pi = explode('/', $_GET['title']); + $id = $id + 1; + return isset($pi[$id]) ? $pi[$id] : false; + } + else + { + $k = array_keys($_GET); + foreach($k as $c) + { + if(substr($c, 0, 1) == '/') + { + + // Bugfix for apache somehow passing dots as underscores + global $mime_types; + $exts = array_keys($mime_types); + $exts = '(' . implode('|', $exts) . ')'; + if ( preg_match( '#_'.$exts.'#i', $c ) ) + $c = preg_replace( '#_'.$exts.'#i', '.\\1', $c ); + + $pi = explode('/', $c); + $id = $id + 2; + return isset($pi[$id]) ? $pi[$id] : false; + } + } + return false; + } + } + + function getAllParams() + { + if(isset($_SERVER['PATH_INFO'])) + { + $pi = explode('/', $_SERVER['PATH_INFO']); + unset($pi[0], $pi[1]); + return implode('/', $pi); + } + else if( isset($_GET['title']) ) + { + $pi = explode('/', $_GET['title']); + unset($pi[0]); + return implode('/', $pi); + } + else + { + $k = array_keys($_GET); + foreach($k as $c) + { + if(substr($c, 0, 1) == '/') + { + // Bugfix for apache somehow passing dots as underscores + global $mime_types; + $exts = array_keys($mime_types); + $exts = '(' . implode('|', $exts) . ')'; + if ( preg_match( '#_'.$exts.'#i', $c ) ) + $c = preg_replace( '#_'.$exts.'#i', '.\\1', $c ); + + $pi = explode('/', $c); + unset($pi[0], $pi[1]); + return implode('/', $pi); + } + } + return false; + } + } + + /** + * Creates a new namespace in memory + * @param string $id the namespace ID + * @param string $prefix the URL prefix, must not be blank or already used + * @return bool true on success false on failure + */ + + function create_namespace($id, $prefix) + { + if(in_array($prefix, $this->nslist)) + { + // echo 'Warning: pathManager::create_namespace: Prefix "'.$prefix.'" is already taken
'; + return false; + } + if( isset($this->nslist[$id]) ) + { + // echo 'Warning: pathManager::create_namespace: Namespace ID "'.$prefix.'" is already taken
'; + return false; + } + $this->nslist[$id] = $prefix; + } + + /** + * Fetches the page texts for searching + */ + + function fetch_page_search_texts() + { + global $db, $session, $paths, $template, $plugins; // Common objects + $texts = Array(); + $q = $db->sql_query('SELECT t.page_id,t.namespace,t.page_text,t.char_tag FROM '.table_prefix.'page_text AS t + LEFT JOIN '.table_prefix.'pages AS p + ON t.page_id=p.urlname + WHERE p.namespace=t.namespace + AND ( p.password=\'\' OR p.password=\'da39a3ee5e6b4b0d3255bfef95601890afd80709\' ) + AND p.visible=1;'); // Only indexes "visible" pages + + if( !$q ) + { + return false; + } + while($row = $db->fetchrow()) + { + $pid = $this->nslist[$row['namespace']] . $row['page_id']; + $texts[$pid] = $row['page_text']; + } + $db->free_result(); + + return $texts; + } + + /** + * Fetches a MySQL search query to use for Searcher::searchMySQL() + */ + + function fetch_page_search_resource() + { + global $db, $session, $paths, $template, $plugins; // Common objects + // sha1('') returns "da39a3ee5e6b4b0d3255bfef95601890afd80709" + $texts = 'SELECT t.page_text,CONCAT(\'ns=\',t.namespace,\';pid=\',t.page_id) FROM '.table_prefix.'page_text AS t + LEFT JOIN '.table_prefix.'pages AS p + ON ( t.page_id=p.urlname AND t.namespace=p.namespace ) + WHERE p.namespace=t.namespace + AND ( p.password=\'\' OR p.password=\'da39a3ee5e6b4b0d3255bfef95601890afd80709\' ) + AND p.visible=1;'; // Only indexes "visible" pages + return $texts; + } + + /** + * Rebuilds the search index + */ + + function rebuild_search_index() + { + global $db, $session, $paths, $template, $plugins; // Common objects + $search = new Searcher(); + $texts = Array(); + $textq = $db->sql_unbuffered_query($this->fetch_page_search_resource()); + if(!$textq) $db->_die(''); + while($row = $db->fetchrow_num()) + { + $texts[(string)$row[1]] = $row[0]; + } + $search->buildIndex($texts); + // echo '
'.print_r($search->index, true).'
'; + // return; + $q = $db->sql_query('DELETE FROM '.table_prefix.'search_index'); + if(!$q) return false; + $secs = Array(); + $q = 'INSERT INTO '.table_prefix.'search_index(word,page_names) VALUES'; + foreach($search->index as $word => $pages) + { + $secs[] = '(\''.$db->escape($word).'\', \''.$db->escape($pages).'\')'; + } + $q .= implode(',', $secs); + unset($secs); + $q .= ';'; + $result = $db->sql_query($q); + $db->free_result(); + if($result) + return true; + else + $db->_die('The search index was trying to rebuild itself when the error occured.'); + } + + /** + * Partially rebuilds the search index, removing/inserting entries only for the current page + * @param string $page_id + * @param string $namespace + */ + + function rebuild_page_index($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$db->sql_query('SELECT page_text FROM '.table_prefix.'page_text + WHERE page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\';')) + { + return $db->get_error(); + } + $row = $db->fetchrow(); + $db->free_result(); + $search = new Searcher(); + $search->buildIndex(Array("ns={$namespace};pid={$page_id}"=>$row['page_text'])); + $new_index = $search->index; + + $keys = array_keys($search->index); + foreach($keys as $i => $k) + { + $c =& $keys[$i]; + $c = hexencode($c, '', ''); + } + $keys = "word=0x" . implode ( " OR word=0x", $keys ) . ""; + + // Zap the cache + $cache = array_keys($search->index); + if ( count($cache) < 1 ) + { + return false; + } + $cache = "query LIKE '%" . implode ( "%' OR query LIKE '%", $cache ) . "%'"; + $db->sql_query('DELETE FROM '.table_prefix.'search_cache WHERE '.$cache); + + $query = $db->sql_query('SELECT word,page_names FROM '.table_prefix.'search_index WHERE '.$keys.';'); + + while($row = $db->fetchrow()) + { + $row['word'] = rtrim($row['word'], "\0"); + $new_index[ $row['word'] ] = $row['page_names'] . ',' . $search->index[ $row['word'] ]; + } + $db->free_result(); + + $db->sql_query('DELETE FROM '.table_prefix.'search_index WHERE '.$keys.';'); + + $secs = Array(); + $q = 'INSERT INTO '.table_prefix.'search_index(word,page_names) VALUES'; + foreach($new_index as $word => $pages) + { + $secs[] = '(\''.$db->escape($word).'\', \''.$db->escape($pages).'\')'; + } + $q .= implode(',', $secs); + unset($secs); + $q .= ';'; + if(!$db->check_query($q)) + { + die('BUG: PathManager::rebuild_page_index: Query rejected by SQL parser:
'.$q.'
'); + } + $result = $db->sql_query($q); + if($result) + return true; + else + $db->_die('The search index was trying to rebuild itself when the error occured.'); + + } + + /** + * Creates an instance of the Searcher class, including index info + * @return object + */ + + function makeSearcher($match_case = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $search = new Searcher(); + $q = $db->sql_query('SELECT word,page_names FROM '.table_prefix.'search_index;'); + if(!$q) + { + echo $db->get_error(); + return false; + } + $idx = Array(); + while($row = $db->fetchrow($q)) + { + $row['word'] = rtrim($row['word'], "\0"); + $idx[$row['word']] = $row['page_names']; + } + $db->free_result(); + $search->index = $idx; + if($match_case) + $search->match_case = true; + return $search; + } + + /** + * Creates an associative array filled with the values of all the page titles + * @return array + */ + + function get_page_titles() + { + $texts = Array(); + for ( $i = 0; $i < sizeof($this->pages) / 2; $i++ ) + { + $texts[$this->pages[$i]['urlname']] = $this->pages[$i]['name']; + } + return $texts; + } + + /** + * Creates an instance of the Searcher class, including index info for page titles + * @return object + */ + + function makeTitleSearcher($match_case = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $search = new Searcher(); + $texts = $this->get_page_titles(); + $search->buildIndex($texts); + if($match_case) + $search->match_case = true; + return $search; + } + +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/plugins.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/plugins.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,105 @@ +load_list = Array(); + + $plugins = Array(); + + // Open a known directory, and proceed to read its contents + + if (is_dir($dir)) + { + if ($dh = opendir($dir)) + { + while (($file = readdir($dh)) !== false) + { + if(preg_match('#^(.*?)\.php$#is', $file)) + { + if(getConfig('plugin_'.$file) == '1' || in_array($file, $this->system_plugins)) + { + $this->load_list[] = $dir . $file; + $plugid = substr($file, 0, strlen($file)-4); + $f = file_get_contents($dir . $file); + $f = explode("\n", $f); + $f = array_slice($f, 2, 7); + $f[0] = substr($f[0], 13); + $f[1] = substr($f[1], 12); + $f[2] = substr($f[2], 13); + $f[3] = substr($f[3], 8 ); + $f[4] = substr($f[4], 9 ); + $f[5] = substr($f[5], 12); + $plugins[$plugid] = Array(); + $plugins[$plugid]['name'] = $f[0]; + $plugins[$plugid]['uri'] = $f[1]; + $plugins[$plugid]['desc'] = $f[2]; + $plugins[$plugid]['auth'] = $f[3]; + $plugins[$plugid]['vers'] = $f[4]; + $plugins[$plugid]['aweb'] = $f[5]; + } + } + } + closedir($dh); + } + } + $this->loaded_plugins = $plugins; + //die('
'.htmlspecialchars(print_r($plugins, true)).'
'); + } + function setHook($name, $opts = Array()) { + dc_dump($name, 'plugins: hook added: '); + /* + $r = Array(); + if(isset($this->hook_list[$name])) { + for($i=0;$ihook_list[$name]);$i++) { + $ret = eval($this->hook_list[$name][$i]); + if($ret !== null) $r[] = $ret; + } + } + if(sizeof($r) > 0) return $r; + else return false; + */ + if(isset($this->hook_list[$name]) && is_array($this->hook_list[$name])) + { + return $this->hook_list[$name]; + } + else + { + return Array(); + } + } + function attachHook($name, $code) { + dc_dump($code, 'plugins: hook attached: '.$name.'
code:'); + if(!isset($this->hook_list[$name])) + { + $this->hook_list[$name] = Array(); + } + $this->hook_list[$name][] = $code; + } + function loaded($plugid) + { + return isset( $this->loaded_plugins[$plugid] ); + } +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/render.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/render.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,792 @@ +nslist); + for($i=0;$inslist);$i++) + { + $ln = strlen($paths->nslist[$k[$i]]); + if(substr($string, 0, $ln) == $paths->nslist[$k[$i]]) + { + $ns = $k[$i]; + $pg = substr($string, strlen($paths->nslist[$ns]), strlen($string)); + } + } + return Array($pg, $ns); + } + + function getPage($page_id, $namespace, $wiki = 1, $smilies = true, $filter_links = true, $redir = true, $render = true) + { + global $db, $session, $paths, $template, $plugins; // Common objects + dc_here('render: page requested
ID/namespace: '."$page_id, $namespace
Wiki mode: $wiki
Smilies: ".(string)$smilies."
Allow redirects: ".(string)$redir); + + $perms =& $session; + + if ( $page_id != $paths->cpage['urlname_nons'] || $namespace != $paths->namespace ) + { + unset($perms); + unset($perms); // PHP <5.1.5 Zend bug + $perms = $session->fetch_page_acl($page_id, $namespace); + } + + if(!$perms->get_permissions('read')) + return 'Access denied ('.$paths->nslist[$namespace].$page_id.')'; + + if($wiki == 0 || $render == false) + { + if(!$perms->get_permissions('view_source')) + { + return 'Access denied ('.$paths->nslist[$namespace].$page_id.')'; + } + } + + $q = $db->sql_query('SELECT page_text,char_tag FROM '.table_prefix.'page_text WHERE page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\';'); + if ( !$q ) + { + $db->_die('Method called was: RenderMan::getPage(\''.$page_id.'\', \''.$namespace.'\');.'); + } + if ( $db->numrows() < 1 ) + { + return false; + } + $row = $db->fetchrow(); + $db->free_result(); + + $message = $row['page_text']; + $chartag = $row['char_tag']; + unset($row); // Free some memory + + if ( preg_match('#^\#redirect \[\[(.+?)\]\]#', $message, $m) && $redir && !isset($_GET['redirect']) || ( isset($_GET['redirect']) && $_GET['redirect'] != 'no' ) ) + { + dc_here('render: looks like a redirect page to me...'); + $old = $paths->cpage; + $a = RenderMan::strToPageID($m[1]); + $a[0] = str_replace(' ', '_', $a[0]); + + $pageid = str_replace(' ', '_', $paths->nslist[$a[1]] . $a[0]); + $paths->page = $pageid; + $paths->cpage = $paths->pages[$pageid]; + //die('
'.print_r($paths->cpage,true).'
'); + + dc_here('render: wreckin\' $template, and reloading the theme vars to match the new page
This might get messy!'); + + unset($template); + unset($GLOBALS['template']); + + $GLOBALS['template'] = new template(); + global $template; + + $template->template(); // Tear down and rebuild the template parser + $template->load_theme($session->theme, $session->style); + + $data = '
(Redirected from '.$old['name'].')
'.RenderMan::getPage($a[0], $a[1], $wiki, $smilies, $filter_links, false /* Enforces a maximum of one redirect */); + + return $data; + } + else if(preg_match('#^\#redirect \[\[(.+?)\]\]#', $message, $m) && isset($_GET['redirect']) && $_GET['redirect'] == 'no') + { + dc_here('render: looks like a redirect page to me...'); + dc_here('render: skipping redirect as requested on URI'); + preg_match('#^\#redirect \[\[(.+)\]\]#', $message, $m); + $m[1] = str_replace(' ', '_', $m[1]); + $message = preg_replace('#\#redirect \[\[(.+)\]\]#', '
Cute wet-floor iconThis page is a redirector.
This means that this page will not show its own content by default. Instead it will display the contents of the page it redirects to.

To create a redirect page, make the first characters in the page content #redirect [[Page_ID]]. For more information, see the Enano Wiki formatting guide.

This page redirects to '.$paths->pages[$m[1]]['name'].'.


', $message); + } + $session->disallow_password_grab(); + dc_here('render: alright, got the text, formatting...'); + return ($render) ? RenderMan::render($message, $wiki, $smilies, $filter_links) : $message; + } + + function getTemplate($id, $parms) + { + global $db, $session, $paths, $template, $plugins; // Common objects + dc_here('render: template requested: '.$id); + if(!isset($paths->pages[$paths->nslist['Template'].$id])) + { + return '[['.$paths->nslist['Template'].$id.']]'; + } + if(isset($paths->template_cache[$id])) + { + $text = $paths->template_cache[$id]; + } + else + { + $text = RenderMan::getPage($id, 'Template', 0, true, true, 0); + $paths->template_cache[$id] = $text; + } + + $text = preg_replace('/(.*?)<\/noinclude>/is', '', $text); + $text = preg_replace('/(.*?)<\/nodisplay>/is', '\\1', $text); + + preg_match_all('#\(_([0-9]+)_\)#', $text, $matchlist); + + foreach($matchlist[1] as $m) + { + if(isset($parms[((int)$m)+1])) + { + $p = $parms[((int)$m)+1]; + } + else + { + $p = 'Notice: RenderMan::getTemplate(): Parameter '.$m.' is not set'; + } + $text = str_replace('(_'.$m.'_)', $p, $text); + } + $text = RenderMan::include_templates($text); + return $text; + } + + function fetch_template_text($id) + { + global $db, $session, $paths, $template, $plugins; // Common objects + dc_here('render: template raw data requested: '.$id); + if(!isset($paths->pages[$paths->nslist['Template'].$id])) + { + return '[['.$paths->nslist['Template'].$id.']]'; + } + if(isset($paths->template_cache[$id])) + { + $text = $paths->template_cache[$id]; + } + else + { + $text = RenderMan::getPage($id, 'Template', 0, false, false, false, false); + $paths->template_cache[$id] = $text; + } + + if ( is_string($text) ) + { + $text = preg_replace('/(.*?)<\/noinclude>/is', '', $text); + $text = preg_replace('/(.*?)<\/nodisplay>/is', '\\1', $text); + } + + return $text; + } + + function render($text, $wiki = 1, $smilies = true, $filter_links = true) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if($smilies) + { + $text = RenderMan::smilieyize($text); + } + if($wiki == 1) + { + $text = RenderMan::next_gen_wiki_format($text); + } + elseif($wiki == 2) + { + $text = $template->tplWikiFormat($text); + } + return $text; + } + + function PlainTextRender($text, $wiki = 1, $smilies = false, $filter_links = true) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if($smilies) + { + $text = RenderMan::smilieyize($text); + } + if($wiki == 1) + { + $text = RenderMan::next_gen_wiki_format($text, true); + } + elseif($wiki == 2) + { + $text = $template->tplWikiFormat($text); + } + return $text; + } + + function next_gen_wiki_format($text, $plaintext = false, $filter_links = true, $do_params = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $random_id = md5( time() . mt_rand() ); + + // Strip out sections and PHP code + + $php = preg_match_all('#<\?php(.*?)\?>#is', $text, $phpsec); + + for($i=0;$i', '{PHP:'.$random_id.':'.$i.'}', $text); + } + + $nw = preg_match_all('#(.*?)<\/nowiki>#is', $text, $nowiki); + + for($i=0;$i'.$nowiki[1][$i].'', '{NOWIKI:'.$random_id.':'.$i.'}', $text); + } + + $text = preg_replace('/(.*?)<\/noinclude>/is', '\\1', $text); + if ( $paths->namespace == 'Template' ) + { + $text = preg_replace('/(.*?)<\/nodisplay>/is', '', $text); + } + + if ( !$plaintext ) + { + // Process images + + $j = preg_match_all('#\[\[:'.$paths->nslist['File'].'([\w\s0-9_\(\)!@%\^\+\|\.-]+?)\|([0-9]+)\|([0-9]+)\]\]#is', $text, $matchlist); + $matches = Array(); + $matches['images'] =& $matchlist[1]; + $matches['widths'] =& $matchlist[2]; + $matches['heights'] =& $matchlist[3]; + for($i=0;$inslist['File'].$matches['images'][$i])) $text = str_replace('[[:'.$paths->nslist['File'].$matches['images'][$i].'|'.$matches['widths'][$i].'|'.$matches['heights'][$i].']]', + ''.$matches['images'][$i].'', + $text); + } + + $j = preg_match_all('#\[\[:'.$paths->nslist['File'].'([\w\s0-9_\(\)!@%\^\+\|\.-]+?)\]\]#is', $text, $matchlist); + $matches = Array(); + $matches['images'] = $matchlist[1]; + for($i=0;$inslist['File'].$matches['images'][$i])) $text = str_replace('[[:'.$paths->nslist['File'].$matches['images'][$i].']]', + ''.$matches['images'][$i].'', + $text); + } + + } + + if($do_params) + { + preg_match_all('#\(_([0-9]+)_\)#', $text, $matchlist); + foreach($matchlist[1] as $m) + { + $text = str_replace('(_'.$m.'_)', $paths->getParam((int)$m), $text); + } + } + + $text = RenderMan::include_templates($text); + + $text = process_tables($text); + + $wiki =& Text_Wiki::singleton('Mediawiki'); + if($plaintext) + { + $wiki->setRenderConf('Plain', 'wikilink', 'view_url', contentPath); + $result = $wiki->transform($text, 'Plain'); + } + else + { + $wiki->setRenderConf('Xhtml', 'wikilink', 'view_url', contentPath); + $wiki->setRenderConf('Xhtml', 'Url', 'css_descr', 'external'); + $result = $wiki->transform($text, 'Xhtml'); + } + + // Reinsert sections + for($i=0;$i<$nw;$i++) + { + $result = str_replace('{NOWIKI:'.$random_id.':'.$i.'}', $nowiki[1][$i], $result); + } + + // Reinsert PHP + for($i=0;$i<$php;$i++) + { + $result = str_replace('{PHP:'.$random_id.':'.$i.'}', '', $result); + } + + return $result; + + } + + function wikiFormat($message, $filter_links = true, $do_params = false, $plaintext = false) { + global $db, $session, $paths, $template, $plugins; // Common objects + + return RenderMan::next_gen_wiki_format($message, $plaintext, $filter_links, $do_params); + + $random_id = md5( time() . mt_rand() ); + + // Strip out sections + $nw = preg_match_all('#(.*?)<\/nowiki>#is', $message, $nowiki); + + if(!$plaintext) + { + + //return '
'.print_r($nowiki,true).'
'; + + for($i=0;$i'.$nowiki[1][$i].'
', '{NOWIKI:'.$random_id.':'.$i.'}', $message); + } + + $message = preg_replace('/(.*?)<\/noinclude>/is', '\\1', $message); + + //return '
'.htmlspecialchars($message).'
'; + + $j = preg_match_all('#\[\[:'.$paths->nslist['File'].'([\w\s0-9_\(\)!@%\^\+\|\.-]+?)\|([0-9]+)\|([0-9]+)\]\]#is', $message, $matchlist); + $matches = Array(); + $matches['images'] = $matchlist[1]; + $matches['widths'] = $matchlist[2]; + $matches['heights'] = $matchlist[3]; + for($i=0;$inslist['File'].$matches['images'][$i])) $message = str_replace('[[:'.$paths->nslist['File'].$matches['images'][$i].'|'.$matches['widths'][$i].'|'.$matches['heights'][$i].']]', + ''.$matches['images'][$i].'', + $message); + } + + $j = preg_match_all('#\[\[:'.$paths->nslist['File'].'([\w\s0-9_\(\)!@%\^\+\|\.-]+?)\]\]#is', $message, $matchlist); + $matches = Array(); + $matches['images'] = $matchlist[1]; + for($i=0;$inslist['File'].$matches['images'][$i])) $message = str_replace('[[:'.$paths->nslist['File'].$matches['images'][$i].']]', + ''.$matches['images'][$i].'', + $message); + } + + } + + if($do_params) + { + preg_match_all('#\(_([0-9]+)_\)#', $message, $matchlist); + foreach($matchlist[1] as $m) + { + $message = str_replace('(_'.$m.'_)', $paths->getParam((int)$m), $message); + } + } + + $message = RenderMan::include_templates($message); + + // Reinsert sections + for($i=0;$i<$nw;$i++) + { + $message = str_replace('{NOWIKI:'.$random_id.':'.$i.'}', ''.$nowiki[1][$i].'', $message); + } + + $message = process_tables($message); + //if($message2 != $message) return '
'.htmlspecialchars($message2).'
'; + //$message = str_replace(array('', '
'), array('', '
'), $message); + + $wiki =& Text_Wiki::singleton('Mediawiki'); + if($plaintext) + { + $wiki->setRenderConf('Plain', 'wikilink', 'view_url', contentPath); + $result = $wiki->transform($message, 'Plain'); + } else { + $wiki->setRenderConf('Xhtml', 'wikilink', 'view_url', contentPath); + $wiki->setRenderConf('Xhtml', 'Url', 'css_descr', 'external'); + $result = $wiki->transform($message, 'Xhtml'); + } + + // HTML fixes + $result = preg_replace('#([\s]*?)<\/tr>#is', '', $result); + $result = preg_replace('#

([\s]*?)<\/p>#is', '', $result); + $result = preg_replace('#
([\s]*?)\n", "

", $result);
+    $result = preg_replace("/

]*?)><\/p>/", "", $result); + $result = str_replace("
\n", "\n", $result); + $result = str_replace("

", "", $result); + $result = str_replace("
", "", $result); + $result = str_replace("
", "", $result); + $result = str_replace("

", "", $result); + $result = str_replace("
", "", $result); + $result = preg_replace('/<\/table>$/', "

", $result); + + $result = str_replace('', '<nowiki>', $result); + $result = str_replace('', '</nowiki>', $result); + + return $result; + } + + function destroy_javascript($message, $_php = false) + { + $message = preg_replace('#<(script|object|applet|embed|iframe|frame|form|input|select)(.*?)>#is', '<\\1\\2>', $message); + $message = preg_replace('##is', '</\\1\\2>', $message); + $message = preg_replace('#(javascript|script|activex|chrome|about|applet):#is', '\\1:', $message); + if ( $_php ) + { + // Left in only for compatibility + $message = preg_replace('#<(.*?)>#is', '<\\1>', $message); + $message = preg_replace('#<(.*?)>#is', '<\\1>', $message); + $message = preg_replace('#<(\?|\?php|%)(.*?)(\?|%)>#is', '<\\1\\2\\3>', $message); + // strip -type attacks + $message = preg_replace('#<([a-zA-Z:\-]+) (.*?)on([A-Za-z]*)=(.*?)>#is', '<\\1\\2on\\3=\\4>', $message); + } + return $message; + } + + function strip_php($message) + { + return RenderMan::destroy_javascript($message, true); + } + + function sanitize_html($text) + { + $text = htmlspecialchars($text); + $allowed_tags = Array('b', 'i', 'u', 'pre', 'code', 'tt', 'br', 'p', 'nowiki', '!--([^.]+)--'); + foreach($allowed_tags as $t) + { + $text = preg_replace('#<'.$t.'>(.*?)</'.$t.'>#is', '<'.$t.'>\\1', $text); + $text = preg_replace('#<'.$t.' />#is', '<'.$t.' />', $text); + $text = preg_replace('#<'.$t.'>#is', '<'.$t.'>', $text); + } + return $text; + } + + /* * + * Replaces template inclusions with the templates + * @param string $message The text to format + * @return string + * / + + function old_include_templates($message) + { + $random_id = md5( time() . mt_rand() ); + preg_match_all('#\{\{(.+?)\}\}#s', $message, $matchlist); + foreach($matchlist[1] as $m) + { + $mn = $m; + // Strip out wikilinks and re-add them after the explosion (because of the "|") + preg_match_all('#\[\[(.+?)\]\]#i', $m, $linklist); + //echo '
'.print_r($linklist, true).'
'; + for($i=0;$i + * foo = lorem ipsum + * bar = dolor sit amet + *
+ * @return array Example: + * [foo] => lorem ipsum + * [bar] => dolor sit amet + */ + + function parse_template_vars($input) + { + $input = explode("\n", trim( $input )); + $parms = Array(); + $current_line = ''; + $current_parm = ''; + foreach ( $input as $num => $line ) + { + if ( preg_match('/^([ ]*?)([A-z0-9_]+?)([ ]*?)=([ ]*?)(.+?)$/i', $line, $matches) ) + { + $parm =& $matches[2]; + $text =& $matches[5]; + if ( $parm == $current_parm ) + { + $current_line .= $text; + } + else + { + // New parameter + if ( $current_parm != '' ) + $parms[$current_parm] = $current_line; + $current_line = $text; + $current_parm = $parm; + } + } + else if ( $num == 0 ) + { + // Syntax error + return false; + } + else + { + $current_line .= "\n$line"; + } + } + if ( !empty($current_parm) && !empty($current_line) ) + { + $parms[$current_parm] = $current_line; + } + return $parms; + } + + /** + * Processes all template tags within a block of wikitext. + * @param string The text to process + * @return string Formatted text + * @example + * + $text = '{{Template + parm1 = Foo + parm2 = Bar + }}'; + $text = include_templates($text); + * + */ + + function include_templates($text) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $template_regex = "/\{\{([A-z0-9_-]+?)((\n([ ]*?)[A-z0-9]+([ ]*?)=([ ]*?)(.+?))*)\}\}/is"; + if ( $count = preg_match_all($template_regex, $text, $matches) ) + { + for ( $i = 0; $i < $count; $i++ ) + { + $parmsection = trim($matches[2][$i]); + if ( !empty($parmsection) ) + { + $parms = RenderMan::parse_template_vars($parmsection); + foreach ( $parms as $j => $parm ) + { + $parms[$j] = $parm; + } + } + else + { + $parms = Array(); + } + if ( $tpl_code = RenderMan::fetch_template_text($matches[1][$i]) ) + { + $parser = $template->makeParserText($tpl_code); + $parser->assign_vars($parms); + $text = str_replace($matches[0][$i], $parser->run(), $text); + } + } + } + return $text; + } + + /** + * Preprocesses an HTML text string prior to being sent to MySQL. + * @param string $text + * @param bool $strip_all_php - if true, strips all PHP regardless of user permissions. Else, strips PHP only if user level < USER_LEVEL_ADMIN. + */ + function preprocess_text($text, $strip_all_php = true, $sqlescape = true) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $random_id = md5( time() . mt_rand() ); + + $can_do_php = ( $session->get_permissions('php_in_pages') && !$strip_all_php ); + + $text = sanitize_html($text, ( !$can_do_php )); + + if ( !$can_do_php ) + { + // If we can't do PHP, we can't do Javascript either. + $text = RenderMan::destroy_javascript($text); + } + + // Strip out sections and PHP code + + $php = preg_match_all('#(<|<)\?php(.*?)\?(>|>)#is', $text, $phpsec); + + //die('
'.htmlspecialchars(print_r($phpsec, true))."\n".htmlspecialchars(print_r($text, true)).'
'); + + for($i=0;$i(.*?)<\/nowiki>#is', $text, $nowiki); + + for($i=0;$i'.$nowiki[1][$i].'
', '{NOWIKI:'.$random_id.':'.$i.'}', $text); + } + + $text = str_replace('~~~~~', date('G:i, j F Y (T)'), $text); + $text = str_replace('~~~~', "[[User:$session->username|$session->username]] ".date('G:i, j F Y (T)'), $text); + $text = str_replace('~~~', "[[User:$session->username|$session->username]] ", $text); + + // Reinsert sections + for($i=0;$i<$nw;$i++) + { + $text = str_replace('{NOWIKI:'.$random_id.':'.$i.'}', ''.$nowiki[1][$i].'', $text); + } + // Reinsert PHP + for($i=0;$i<$php;$i++) + { + $phsec = ''.$phpsec[1][$i].'?php'.$phpsec[2][$i].'?'.$phpsec[3][$i].''; + if ( $strip_all_php ) + $phsec = htmlspecialchars($phsec); + $text = str_replace('{PHP:'.$random_id.':'.$i.'}', $phsec, $text); + } + + $text = ( $sqlescape ) ? $db->escape($text) : $text; + + return $text; + } + + function smilieyize($text, $complete_urls = false) + { + + $random_id = md5( time() . mt_rand() ); + + // Smileys array - eventually this will be fetched from the database by + // RenderMan::initSmileys during initialization, but it will all be hardcoded for beta 2 + + $smileys = Array( + 'O:-)' => 'face-angel.png', + 'O:)' => 'face-angel.png', + 'O=)' => 'face-angel.png', + ':-)' => 'face-smile.png', + ':)' => 'face-smile.png', + '=)' => 'face-smile-big.png', + ':-(' => 'face-sad.png', + ':(' => 'face-sad.png', + ';(' => 'face-sad.png', + ':-O' => 'face-surprise.png', + ';-)' => 'face-wink.png', + ';)' => 'face-wink.png', + '8-)' => 'face-glasses.png', + '8)' => 'face-glasses.png', + ':-D' => 'face-grin.png', + ':D' => 'face-grin.png', + '=D' => 'face-grin.png', + ':-*' => 'face-kiss.png', + ':*' => 'face-kiss.png', + '=*' => 'face-kiss.png', + ':\'(' => 'face-crying.png', + ':-|' => 'face-plain.png', + ':-\\' => 'face-plain.png', + ':-/' => 'face-plain.png', + ':joke:' => 'face-plain.png', + ']:->' => 'face-devil-grin.png', + ':kiss:' => 'face-kiss.png', + ':-P' => 'face-tongue-out.png', + ':P' => 'face-tongue-out.png', + ':-p' => 'face-tongue-out.png', + ':p' => 'face-tongue-out.png', + ':-X' => 'face-sick.png', + ':X' => 'face-sick.png', + ':sick:' => 'face-sick.png', + ':-]' => 'face-oops.png', + ':]' => 'face-oops.png', + ':oops:' => 'face-oops.png', + ':-[' => 'face-embarassed.png', + ':[' => 'face-embarassed.png' + ); + /* + $keys = array_keys($smileys); + foreach($keys as $k) + { + $regex1 = '#([\W]+)'.preg_quote($k).'([\s\n\r\.]+)#s'; + $regex2 = '\\1'.$k.'\\2'; + $text = preg_replace($regex1, $regex2, $text); + } + */ + + // Strip out sections + //return '
'.htmlspecialchars($text).'
'; + $nw = preg_match_all('#(.*?)<\/nowiki>#is', $text, $nowiki); + + for($i=0;$i'.$nowiki[1][$i].'', '{NOWIKI:'.$random_id.':'.$i.'}', $text); + } + + $keys = array_keys($smileys); + foreach($keys as $k) + { + $t = str_hex($k); + $t = explode(' ', $t); + $s = ''; + foreach($t as $b) + { + $s.='&#x'.$b.';'; + } + $pfx = ( $complete_urls ) ? 'http' . ( isset($_SERVER['HTTPS']) ? 's' : '' ) . '://'.$_SERVER['HTTP_HOST'] : ''; + $text = str_replace(' '.$k, ' '.$s.'', $text); + } + //*/ + + // Reinsert sections + for($i=0;$i<$nw;$i++) + { + $text = str_replace('{NOWIKI:'.$random_id.':'.$i.'}', ''.$nowiki[1][$i].'', $text); + } + + return $text; + } + + /* + * **** DEPRECATED **** + * Replaces some critical characters in a string with MySQL-safe equivalents + * @param $text string the text to escape + * @return array key 0 is the escaped text, key 1 is the character tag + * / + + function escape_page_text($text) + { + $char_tag = md5(microtime() . mt_rand()); + $text = str_replace("'", "{APOS:$char_tag}", $text); + $text = str_replace('"', "{QUOT:$char_tag}", $text); + $text = str_replace("\\", "{SLASH:$char_tag}", $text); + return Array($text, $char_tag); + } + */ + + /* **** DEPRECATED **** + * Reverses the result of RenderMan::escape_page_text(). + * @param $text string the text to unescape + * @param $char_tag string the character tag + * @return string + * / + + function unescape_page_text($text, $char_tag) + { + $text = str_replace("{APOS:$char_tag}", "'", $text); + $text = str_replace("{QUOT:$char_tag}", '"', $text); + $text = str_replace("{SLASH:$char_tag}", "\\", $text); + return $text; + } + */ + + /** + * Generates a summary of the differences between two texts, and formats it as XHTML. + * @param $str1 string the first block of text + * @param $str2 string the second block of text + * @return string + */ + function diff($str1, $str2) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $str1 = explode("\n", $str1); + $str2 = explode("\n", $str2); + $diff = new Diff($str1, $str2); + $renderer = new TableDiffFormatter(); + return ''.$renderer->format($diff).'
'; + } + +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/render.php~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/render.php~ Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,792 @@ +nslist); + for($i=0;$inslist);$i++) + { + $ln = strlen($paths->nslist[$k[$i]]); + if(substr($string, 0, $ln) == $paths->nslist[$k[$i]]) + { + $ns = $k[$i]; + $pg = substr($string, strlen($paths->nslist[$ns]), strlen($string)); + } + } + return Array($pg, $ns); + } + + function getPage($page_id, $namespace, $wiki = 1, $smilies = true, $filter_links = true, $redir = true, $render = true) + { + global $db, $session, $paths, $template, $plugins; // Common objects + dc_here('render: page requested
ID/namespace: '."$page_id, $namespace
Wiki mode: $wiki
Smilies: ".(string)$smilies."
Allow redirects: ".(string)$redir); + + $perms =& $session; + + if ( $page_id != $paths->cpage['urlname_nons'] || $namespace != $paths->namespace ) + { + unset($perms); + unset($perms); // PHP <5.1.5 Zend bug + $perms = $session->fetch_page_acl($page_id, $namespace); + } + + if(!$perms->get_permissions('read')) + return 'Access denied ('.$paths->nslist[$namespace].$page_id.')'; + + if($wiki == 0 || $render == false) + { + if(!$perms->get_permissions('view_source')) + { + return 'Access denied ('.$paths->nslist[$namespace].$page_id.')'; + } + } + + $q = $db->sql_query('SELECT page_text,char_tag FROM '.table_prefix.'page_text WHERE page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\';'); + if ( !$q ) + { + $db->_die('Method called was: RenderMan::getPage(\''.$page_id.'\', \''.$namespace.'\');.'); + } + if ( $db->numrows() < 1 ) + { + return false; + } + $row = $db->fetchrow(); + $db->free_result(); + + $message = $row['page_text']; + $chartag = $row['char_tag']; + unset($row); // Free some memory + + if ( preg_match('#^\#redirect \[\[(.+?)\]\]#', $message, $m) && $redir && !isset($_GET['redirect']) || ( isset($_GET['redirect']) && $_GET['redirect'] != 'no' ) ) + { + dc_here('render: looks like a redirect page to me...'); + $old = $paths->cpage; + $a = RenderMan::strToPageID($m[1]); + $a[0] = str_replace(' ', '_', $a[0]); + + $pageid = str_replace(' ', '_', $paths->nslist[$a[1]] . $a[0]); + $paths->page = $pageid; + $paths->cpage = $paths->pages[$pageid]; + //die('
'.print_r($paths->cpage,true).'
'); + + dc_here('render: wreckin\' $template, and reloading the theme vars to match the new page
This might get messy!'); + + unset($template); + unset($GLOBALS['template']); + + $GLOBALS['template'] = new template(); + global $template; + + $template->template(); // Tear down and rebuild the template parser + $template->load_theme($session->theme, $session->style); + + $data = '
'.RenderMan::getPage($a[0], $a[1], $wiki, $smilies, $filter_links, false /* Enforces a maximum of one redirect */); + + return $data; + } + else if(preg_match('#^\#redirect \[\[(.+?)\]\]#', $message, $m) && isset($_GET['redirect']) && $_GET['redirect'] == 'no') + { + dc_here('render: looks like a redirect page to me...'); + dc_here('render: skipping redirect as requested on URI'); + preg_match('#^\#redirect \[\[(.+)\]\]#', $message, $m); + $m[1] = str_replace(' ', '_', $m[1]); + $message = preg_replace('#\#redirect \[\[(.+)\]\]#', '
Cute wet-floor iconThis page is a redirector.
This means that this page will not show its own content by default. Instead it will display the contents of the page it redirects to.

To create a redirect page, make the first characters in the page content #redirect [[Page_ID]]. For more information, see the Enano Wiki formatting guide.

This page redirects to '.$paths->pages[$m[1]]['name'].'.


', $message); + } + $session->disallow_password_grab(); + dc_here('render: alright, got the text, formatting...'); + return ($render) ? RenderMan::render($message, $wiki, $smilies, $filter_links) : $message; + } + + function getTemplate($id, $parms) + { + global $db, $session, $paths, $template, $plugins; // Common objects + dc_here('render: template requested: '.$id); + if(!isset($paths->pages[$paths->nslist['Template'].$id])) + { + return '[['.$paths->nslist['Template'].$id.']]'; + } + if(isset($paths->template_cache[$id])) + { + $text = $paths->template_cache[$id]; + } + else + { + $text = RenderMan::getPage($id, 'Template', 0, true, true, 0); + $paths->template_cache[$id] = $text; + } + + $text = preg_replace('/(.*?)<\/noinclude>/is', '', $text); + $text = preg_replace('/(.*?)<\/nodisplay>/is', '\\1', $text); + + preg_match_all('#\(_([0-9]+)_\)#', $text, $matchlist); + + foreach($matchlist[1] as $m) + { + if(isset($parms[((int)$m)+1])) + { + $p = $parms[((int)$m)+1]; + } + else + { + $p = 'Notice: RenderMan::getTemplate(): Parameter '.$m.' is not set'; + } + $text = str_replace('(_'.$m.'_)', $p, $text); + } + $text = RenderMan::include_templates($text); + return $text; + } + + function fetch_template_text($id) + { + global $db, $session, $paths, $template, $plugins; // Common objects + dc_here('render: template raw data requested: '.$id); + if(!isset($paths->pages[$paths->nslist['Template'].$id])) + { + return '[['.$paths->nslist['Template'].$id.']]'; + } + if(isset($paths->template_cache[$id])) + { + $text = $paths->template_cache[$id]; + } + else + { + $text = RenderMan::getPage($id, 'Template', 0, false, false, false, false); + $paths->template_cache[$id] = $text; + } + + if ( is_string($text) ) + { + $text = preg_replace('/(.*?)<\/noinclude>/is', '', $text); + $text = preg_replace('/(.*?)<\/nodisplay>/is', '\\1', $text); + } + + return $text; + } + + function render($text, $wiki = 1, $smilies = true, $filter_links = true) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if($smilies) + { + $text = RenderMan::smilieyize($text); + } + if($wiki == 1) + { + $text = RenderMan::next_gen_wiki_format($text); + } + elseif($wiki == 2) + { + $text = $template->tplWikiFormat($text); + } + return $text; + } + + function PlainTextRender($text, $wiki = 1, $smilies = false, $filter_links = true) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if($smilies) + { + $text = RenderMan::smilieyize($text); + } + if($wiki == 1) + { + $text = RenderMan::next_gen_wiki_format($text, true); + } + elseif($wiki == 2) + { + $text = $template->tplWikiFormat($text); + } + return $text; + } + + function next_gen_wiki_format($text, $plaintext = false, $filter_links = true, $do_params = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $random_id = md5( time() . mt_rand() ); + + // Strip out sections and PHP code + + $php = preg_match_all('#<\?php(.*?)\?>#is', $text, $phpsec); + + for($i=0;$i', '{PHP:'.$random_id.':'.$i.'}', $text); + } + + $nw = preg_match_all('#(.*?)<\/nowiki>#is', $text, $nowiki); + + for($i=0;$i'.$nowiki[1][$i].'', '{NOWIKI:'.$random_id.':'.$i.'}', $text); + } + + $text = preg_replace('/(.*?)<\/noinclude>/is', '\\1', $text); + if ( $paths->namespace == 'Template' ) + { + $text = preg_replace('/(.*?)<\/nodisplay>/is', '', $text); + } + + if ( !$plaintext ) + { + // Process images + + $j = preg_match_all('#\[\[:'.$paths->nslist['File'].'([\w\s0-9_\(\)!@%\^\+\|\.-]+?)\|([0-9]+)\|([0-9]+)\]\]#is', $text, $matchlist); + $matches = Array(); + $matches['images'] =& $matchlist[1]; + $matches['widths'] =& $matchlist[2]; + $matches['heights'] =& $matchlist[3]; + for($i=0;$inslist['File'].$matches['images'][$i])) $text = str_replace('[[:'.$paths->nslist['File'].$matches['images'][$i].'|'.$matches['widths'][$i].'|'.$matches['heights'][$i].']]', + ''.$matches['images'][$i].'', + $text); + } + + $j = preg_match_all('#\[\[:'.$paths->nslist['File'].'([\w\s0-9_\(\)!@%\^\+\|\.-]+?)\]\]#is', $text, $matchlist); + $matches = Array(); + $matches['images'] = $matchlist[1]; + for($i=0;$inslist['File'].$matches['images'][$i])) $text = str_replace('[[:'.$paths->nslist['File'].$matches['images'][$i].']]', + ''.$matches['images'][$i].'', + $text); + } + + } + + if($do_params) + { + preg_match_all('#\(_([0-9]+)_\)#', $text, $matchlist); + foreach($matchlist[1] as $m) + { + $text = str_replace('(_'.$m.'_)', $paths->getParam((int)$m), $text); + } + } + + $text = RenderMan::include_templates($text); + + $text = process_tables($text); + + $wiki =& Text_Wiki::singleton('Mediawiki'); + if($plaintext) + { + $wiki->setRenderConf('Plain', 'wikilink', 'view_url', contentPath); + $result = $wiki->transform($text, 'Plain'); + } + else + { + $wiki->setRenderConf('Xhtml', 'wikilink', 'view_url', contentPath); + $wiki->setRenderConf('Xhtml', 'Url', 'css_descr', 'external'); + $result = $wiki->transform($text, 'Xhtml'); + } + + // Reinsert sections + for($i=0;$i<$nw;$i++) + { + $result = str_replace('{NOWIKI:'.$random_id.':'.$i.'}', $nowiki[1][$i], $result); + } + + // Reinsert PHP + for($i=0;$i<$php;$i++) + { + $result = str_replace('{PHP:'.$random_id.':'.$i.'}', '', $result); + } + + return $result; + + } + + function wikiFormat($message, $filter_links = true, $do_params = false, $plaintext = false) { + global $db, $session, $paths, $template, $plugins; // Common objects + + return RenderMan::next_gen_wiki_format($message, $plaintext, $filter_links, $do_params); + + $random_id = md5( time() . mt_rand() ); + + // Strip out sections + $nw = preg_match_all('#(.*?)<\/nowiki>#is', $message, $nowiki); + + if(!$plaintext) + { + + //return '
'.print_r($nowiki,true).'
'; + + for($i=0;$i'.$nowiki[1][$i].'
', '{NOWIKI:'.$random_id.':'.$i.'}', $message); + } + + $message = preg_replace('/(.*?)<\/noinclude>/is', '\\1', $message); + + //return '
'.htmlspecialchars($message).'
'; + + $j = preg_match_all('#\[\[:'.$paths->nslist['File'].'([\w\s0-9_\(\)!@%\^\+\|\.-]+?)\|([0-9]+)\|([0-9]+)\]\]#is', $message, $matchlist); + $matches = Array(); + $matches['images'] = $matchlist[1]; + $matches['widths'] = $matchlist[2]; + $matches['heights'] = $matchlist[3]; + for($i=0;$inslist['File'].$matches['images'][$i])) $message = str_replace('[[:'.$paths->nslist['File'].$matches['images'][$i].'|'.$matches['widths'][$i].'|'.$matches['heights'][$i].']]', + ''.$matches['images'][$i].'', + $message); + } + + $j = preg_match_all('#\[\[:'.$paths->nslist['File'].'([\w\s0-9_\(\)!@%\^\+\|\.-]+?)\]\]#is', $message, $matchlist); + $matches = Array(); + $matches['images'] = $matchlist[1]; + for($i=0;$inslist['File'].$matches['images'][$i])) $message = str_replace('[[:'.$paths->nslist['File'].$matches['images'][$i].']]', + ''.$matches['images'][$i].'', + $message); + } + + } + + if($do_params) + { + preg_match_all('#\(_([0-9]+)_\)#', $message, $matchlist); + foreach($matchlist[1] as $m) + { + $message = str_replace('(_'.$m.'_)', $paths->getParam((int)$m), $message); + } + } + + $message = RenderMan::include_templates($message); + + // Reinsert sections + for($i=0;$i<$nw;$i++) + { + $message = str_replace('{NOWIKI:'.$random_id.':'.$i.'}', ''.$nowiki[1][$i].'', $message); + } + + $message = process_tables($message); + //if($message2 != $message) return '
'.htmlspecialchars($message2).'
'; + //$message = str_replace(array('', '
'), array('', '
'), $message); + + $wiki =& Text_Wiki::singleton('Mediawiki'); + if($plaintext) + { + $wiki->setRenderConf('Plain', 'wikilink', 'view_url', contentPath); + $result = $wiki->transform($message, 'Plain'); + } else { + $wiki->setRenderConf('Xhtml', 'wikilink', 'view_url', contentPath); + $wiki->setRenderConf('Xhtml', 'Url', 'css_descr', 'external'); + $result = $wiki->transform($message, 'Xhtml'); + } + + // HTML fixes + $result = preg_replace('#([\s]*?)<\/tr>#is', '', $result); + $result = preg_replace('#

([\s]*?)<\/p>#is', '', $result); + $result = preg_replace('#
([\s]*?)\n", "

", $result);
+    $result = preg_replace("/

]*?)><\/p>/", "", $result); + $result = str_replace("
\n", "\n", $result); + $result = str_replace("

", "", $result); + $result = str_replace("
", "", $result); + $result = str_replace("
", "", $result); + $result = str_replace("

", "", $result); + $result = str_replace("
", "", $result); + $result = preg_replace('/<\/table>$/', "

", $result); + + $result = str_replace('', '<nowiki>', $result); + $result = str_replace('', '</nowiki>', $result); + + return $result; + } + + function destroy_javascript($message, $_php = false) + { + $message = preg_replace('#<(script|object|applet|embed|iframe|frame|form|input|select)(.*?)>#is', '<\\1\\2>', $message); + $message = preg_replace('##is', '</\\1\\2>', $message); + $message = preg_replace('#(javascript|script|activex|chrome|about|applet):#is', '\\1:', $message); + if ( $_php ) + { + // Left in only for compatibility + $message = preg_replace('#<(.*?)>#is', '<\\1>', $message); + $message = preg_replace('#<(.*?)>#is', '<\\1>', $message); + $message = preg_replace('#<(\?|\?php|%)(.*?)(\?|%)>#is', '<\\1\\2\\3>', $message); + // strip -type attacks + $message = preg_replace('#<([a-zA-Z:\-]+) (.*?)on([A-Za-z]*)=(.*?)>#is', '<\\1\\2on\\3=\\4>', $message); + } + return $message; + } + + function strip_php($message) + { + return RenderMan::destroy_javascript($message, true); + } + + function sanitize_html($text) + { + $text = htmlspecialchars($text); + $allowed_tags = Array('b', 'i', 'u', 'pre', 'code', 'tt', 'br', 'p', 'nowiki', '!--([^.]+)--'); + foreach($allowed_tags as $t) + { + $text = preg_replace('#<'.$t.'>(.*?)</'.$t.'>#is', '<'.$t.'>\\1', $text); + $text = preg_replace('#<'.$t.' />#is', '<'.$t.' />', $text); + $text = preg_replace('#<'.$t.'>#is', '<'.$t.'>', $text); + } + return $text; + } + + /* * + * Replaces template inclusions with the templates + * @param string $message The text to format + * @return string + * / + + function old_include_templates($message) + { + $random_id = md5( time() . mt_rand() ); + preg_match_all('#\{\{(.+?)\}\}#s', $message, $matchlist); + foreach($matchlist[1] as $m) + { + $mn = $m; + // Strip out wikilinks and re-add them after the explosion (because of the "|") + preg_match_all('#\[\[(.+?)\]\]#i', $m, $linklist); + //echo '
'.print_r($linklist, true).'
'; + for($i=0;$i + * foo = lorem ipsum + * bar = dolor sit amet + *
+ * @return array Example: + * [foo] => lorem ipsum + * [bar] => dolor sit amet + */ + + function parse_template_vars($input) + { + $input = explode("\n", trim( $input )); + $parms = Array(); + $current_line = ''; + $current_parm = ''; + foreach ( $input as $num => $line ) + { + if ( preg_match('/^([ ]*?)([A-z0-9_]+?)([ ]*?)=([ ]*?)(.+?)$/i', $line, $matches) ) + { + $parm =& $matches[2]; + $text =& $matches[5]; + if ( $parm == $current_parm ) + { + $current_line .= $text; + } + else + { + // New parameter + if ( $current_parm != '' ) + $parms[$current_parm] = $current_line; + $current_line = $text; + $current_parm = $parm; + } + } + else if ( $num == 0 ) + { + // Syntax error + return false; + } + else + { + $current_line .= "\n$line"; + } + } + if ( !empty($current_parm) && !empty($current_line) ) + { + $parms[$current_parm] = $current_line; + } + return $parms; + } + + /** + * Processes all template tags within a block of wikitext. + * @param string The text to process + * @return string Formatted text + * @example + * + $text = '{{Template + parm1 = Foo + parm2 = Bar + }}'; + $text = include_templates($text); + * + */ + + function include_templates($text) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $template_regex = "/\{\{([A-z0-9_-]+?)((\n([ ]*?)[A-z0-9]+([ ]*?)=([ ]*?)(.+?))*)\}\}/is"; + if ( $count = preg_match_all($template_regex, $text, $matches) ) + { + for ( $i = 0; $i < $count; $i++ ) + { + $parmsection = trim($matches[2][$i]); + if ( !empty($parmsection) ) + { + $parms = parse_template_vars($parmsection); + foreach ( $parms as $j => $parm ) + { + $parms[$j] = $parm; + } + } + else + { + $parms = Array(); + } + if ( $tpl_code = RenderMan::fetch_template_text($matches[1][$i]) ) + { + $parser = $template->makeParserText($tpl_code); + $parser->assign_vars($parms); + $text = str_replace($matches[0][$i], $parser->run(), $text); + } + } + } + return $text; + } + + /** + * Preprocesses an HTML text string prior to being sent to MySQL. + * @param string $text + * @param bool $strip_all_php - if true, strips all PHP regardless of user permissions. Else, strips PHP only if user level < USER_LEVEL_ADMIN. + */ + function preprocess_text($text, $strip_all_php = true, $sqlescape = true) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $random_id = md5( time() . mt_rand() ); + + $can_do_php = ( $session->get_permissions('php_in_pages') && !$strip_all_php ); + + $text = sanitize_html($text, ( !$can_do_php )); + + if ( !$can_do_php ) + { + // If we can't do PHP, we can't do Javascript either. + $text = RenderMan::destroy_javascript($text); + } + + // Strip out sections and PHP code + + $php = preg_match_all('#(<|<)\?php(.*?)\?(>|>)#is', $text, $phpsec); + + //die('
'.htmlspecialchars(print_r($phpsec, true))."\n".htmlspecialchars(print_r($text, true)).'
'); + + for($i=0;$i(.*?)<\/nowiki>#is', $text, $nowiki); + + for($i=0;$i'.$nowiki[1][$i].'
', '{NOWIKI:'.$random_id.':'.$i.'}', $text); + } + + $text = str_replace('~~~~~', date('G:i, j F Y (T)'), $text); + $text = str_replace('~~~~', "[[User:$session->username|$session->username]] ".date('G:i, j F Y (T)'), $text); + $text = str_replace('~~~', "[[User:$session->username|$session->username]] ", $text); + + // Reinsert sections + for($i=0;$i<$nw;$i++) + { + $text = str_replace('{NOWIKI:'.$random_id.':'.$i.'}', ''.$nowiki[1][$i].'', $text); + } + // Reinsert PHP + for($i=0;$i<$php;$i++) + { + $phsec = ''.$phpsec[1][$i].'?php'.$phpsec[2][$i].'?'.$phpsec[3][$i].''; + if ( $strip_all_php ) + $phsec = htmlspecialchars($phsec); + $text = str_replace('{PHP:'.$random_id.':'.$i.'}', $phsec, $text); + } + + $text = ( $sqlescape ) ? $db->escape($text) : $text; + + return $text; + } + + function smilieyize($text, $complete_urls = false) + { + + $random_id = md5( time() . mt_rand() ); + + // Smileys array - eventually this will be fetched from the database by + // RenderMan::initSmileys during initialization, but it will all be hardcoded for beta 2 + + $smileys = Array( + 'O:-)' => 'face-angel.png', + 'O:)' => 'face-angel.png', + 'O=)' => 'face-angel.png', + ':-)' => 'face-smile.png', + ':)' => 'face-smile.png', + '=)' => 'face-smile-big.png', + ':-(' => 'face-sad.png', + ':(' => 'face-sad.png', + ';(' => 'face-sad.png', + ':-O' => 'face-surprise.png', + ';-)' => 'face-wink.png', + ';)' => 'face-wink.png', + '8-)' => 'face-glasses.png', + '8)' => 'face-glasses.png', + ':-D' => 'face-grin.png', + ':D' => 'face-grin.png', + '=D' => 'face-grin.png', + ':-*' => 'face-kiss.png', + ':*' => 'face-kiss.png', + '=*' => 'face-kiss.png', + ':\'(' => 'face-crying.png', + ':-|' => 'face-plain.png', + ':-\\' => 'face-plain.png', + ':-/' => 'face-plain.png', + ':joke:' => 'face-plain.png', + ']:->' => 'face-devil-grin.png', + ':kiss:' => 'face-kiss.png', + ':-P' => 'face-tongue-out.png', + ':P' => 'face-tongue-out.png', + ':-p' => 'face-tongue-out.png', + ':p' => 'face-tongue-out.png', + ':-X' => 'face-sick.png', + ':X' => 'face-sick.png', + ':sick:' => 'face-sick.png', + ':-]' => 'face-oops.png', + ':]' => 'face-oops.png', + ':oops:' => 'face-oops.png', + ':-[' => 'face-embarassed.png', + ':[' => 'face-embarassed.png' + ); + /* + $keys = array_keys($smileys); + foreach($keys as $k) + { + $regex1 = '#([\W]+)'.preg_quote($k).'([\s\n\r\.]+)#s'; + $regex2 = '\\1'.$k.'\\2'; + $text = preg_replace($regex1, $regex2, $text); + } + */ + + // Strip out sections + //return '
'.htmlspecialchars($text).'
'; + $nw = preg_match_all('#(.*?)<\/nowiki>#is', $text, $nowiki); + + for($i=0;$i'.$nowiki[1][$i].'', '{NOWIKI:'.$random_id.':'.$i.'}', $text); + } + + $keys = array_keys($smileys); + foreach($keys as $k) + { + $t = str_hex($k); + $t = explode(' ', $t); + $s = ''; + foreach($t as $b) + { + $s.='&#x'.$b.';'; + } + $pfx = ( $complete_urls ) ? 'http' . ( isset($_SERVER['HTTPS']) ? 's' : '' ) . '://'.$_SERVER['HTTP_HOST'] : ''; + $text = str_replace(' '.$k, ' '.$s.'', $text); + } + //*/ + + // Reinsert sections + for($i=0;$i<$nw;$i++) + { + $text = str_replace('{NOWIKI:'.$random_id.':'.$i.'}', ''.$nowiki[1][$i].'', $text); + } + + return $text; + } + + /* + * **** DEPRECATED **** + * Replaces some critical characters in a string with MySQL-safe equivalents + * @param $text string the text to escape + * @return array key 0 is the escaped text, key 1 is the character tag + * / + + function escape_page_text($text) + { + $char_tag = md5(microtime() . mt_rand()); + $text = str_replace("'", "{APOS:$char_tag}", $text); + $text = str_replace('"', "{QUOT:$char_tag}", $text); + $text = str_replace("\\", "{SLASH:$char_tag}", $text); + return Array($text, $char_tag); + } + */ + + /* **** DEPRECATED **** + * Reverses the result of RenderMan::escape_page_text(). + * @param $text string the text to unescape + * @param $char_tag string the character tag + * @return string + * / + + function unescape_page_text($text, $char_tag) + { + $text = str_replace("{APOS:$char_tag}", "'", $text); + $text = str_replace("{QUOT:$char_tag}", '"', $text); + $text = str_replace("{SLASH:$char_tag}", "\\", $text); + return $text; + } + */ + + /** + * Generates a summary of the differences between two texts, and formats it as XHTML. + * @param $str1 string the first block of text + * @param $str2 string the second block of text + * @return string + */ + function diff($str1, $str2) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $str1 = explode("\n", $str1); + $str2 = explode("\n", $str2); + $diff = new Diff($str1, $str2); + $renderer = new TableDiffFormatter(); + return ''.$renderer->format($diff).'
'; + } + +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/rijndael.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/rijndael.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1052 @@ + + * Ported to PHP by Dan Fuhry + * @package phijndael + * @author Fritz Schneider + * @author Dan Fuhry + * @license BSD-style license + */ + +error_reporting(E_ALL); + +define ('ENC_HEX', 201); +define ('ENC_BASE64', 202); +define ('ENC_BINARY', 203); + +class AESCrypt { + + var $debug = false; + var $mcrypt = false; + + // Rijndael parameters -- Valid values are 128, 192, or 256 + + var $keySizeInBits = 128; + var $blockSizeInBits = 128; + + /////// You shouldn't have to modify anything below this line except for + /////// the function getRandomBytes(). + // + // Note: in the following code the two dimensional arrays are indexed as + // you would probably expect, as array[row][column]. The state arrays + // are 2d arrays of the form state[4][Nb]. + + + // The number of rounds for the cipher, indexed by [Nk][Nb] + var $roundsArray = Array(0,0,0,0,Array(0,0,0,0,10,0, 12,0, 14),0, + Array(0,0,0,0,12,0, 12,0, 14),0, + Array(0,0,0,0,14,0, 14,0, 14) ); + + // The number of bytes to shift by in shiftRow, indexed by [Nb][row] + var $shiftOffsets = Array(0,0,0,0,Array(0,1, 2, 3),0,Array(0,1, 2, 3),0,Array(0,1, 3, 4) ); + + // The round constants used in subkey expansion + var $Rcon = Array( + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, + 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, + 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, + 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, + 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ); + + // Precomputed lookup table for the SBox + var $SBox = Array( + 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, + 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, + 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, + 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, + 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, + 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, + 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, + 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, + 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, + 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, + 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, + 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, + 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, + 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, + 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, + 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, + 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, + 22 ); + + // Precomputed lookup table for the inverse SBox + var $SBoxInverse = Array( + 82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, + 251, 124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, + 233, 203, 84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, + 250, 195, 78, 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, + 109, 139, 209, 37, 114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, + 204, 93, 101, 182, 146, 108, 112, 72, 80, 253, 237, 185, 218, 94, 21, + 70, 87, 167, 141, 157, 132, 144, 216, 171, 0, 140, 188, 211, 10, 247, + 228, 88, 5, 184, 179, 69, 6, 208, 44, 30, 143, 202, 63, 15, 2, + 193, 175, 189, 3, 1, 19, 138, 107, 58, 145, 17, 65, 79, 103, 220, + 234, 151, 242, 207, 206, 240, 180, 230, 115, 150, 172, 116, 34, 231, 173, + 53, 133, 226, 249, 55, 232, 28, 117, 223, 110, 71, 241, 26, 113, 29, + 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27, 252, 86, 62, 75, + 198, 210, 121, 32, 154, 219, 192, 254, 120, 205, 90, 244, 31, 221, 168, + 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, 128, 236, 95, 96, 81, + 127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239, 160, + 224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97, + 23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12, + 125 ); + + function AESCrypt($ks = 128, $bs = 128, $debug = false) + { + $this->__construct($ks, $bs, $debug); + } + + function __construct($ks = 128, $bs = 128, $debug = false) + { + $this->keySizeInBits = $ks; + $this->blockSizeInBits = $bs; + + // Use the Mcrypt library? This speeds things up dramatically. + if(defined('MCRYPT_RIJNDAEL_' . $ks) && defined('MCRYPT_ACCEL')) + { + eval('$mcb = MCRYPT_RIJNDAEL_' . $ks.';'); + $bks = mcrypt_module_get_algo_block_size($mcb); + $bks = $bks * 8; + if ( $bks != $bs ) + { + $mcb = false; + echo (string)$bks; + } + } + else + { + $mcb = false; + } + + $this->mcrypt = $mcb; + + // Cipher parameters ... do not change these + $this->Nk = $this->keySizeInBits / 32; + $this->Nb = $this->blockSizeInBits / 32; + $this->Nr = $this->roundsArray[$this->Nk][$this->Nb]; + $this->debug = $debug; + } + + // Error handler + + function trigger_error($text, $level = E_USER_NOTICE) + { + $bt = debug_backtrace(); + $lastfunc =& $bt[1]; + switch($level) + { + case E_USER_NOTICE: + default: + $desc = 'Notice'; + break; + case E_USER_WARNING: + $desc = 'Warning'; + break; + case E_USER_ERROR: + $desc = 'Fatal'; + break; + } + ob_start(); + if($this->debug || $level == E_USER_ERROR) echo "AES encryption: {$desc}: $text in {$lastfunc['file']} on line {$lastfunc['line']} in function {$lastfunc['function']}
"; + if($this->debug) + { + //echo '
'.enano_debug_print_backtrace(true).'
'; + } + ob_end_flush(); + if($level == E_USER_ERROR) + { + echo '

This can sometimes happen if you are upgrading Enano to a new version and did not log out first. Click here to force cookies to clear and try again. You will be logged out.

'; + exit; + } + } + + function array_slice_js_compat($array, $start, $finish = 0) + { + $len = $finish - $start; + if($len < 0) $len = 0 - $len; + //if($this->debug) echo (string)$len . ' '; + //if(count($array) < $start + $len) + // $this->trigger_error('Index out of range', E_USER_WARNING); + return array_slice($array, $start, $len); + } + + function concat($s1, $s2) + { + if(is_array($s1) && is_array($s2)) + return array_merge($s1, $s2); + elseif( ( is_array($s1) && !is_array($s2) ) || ( !is_array($s1) && is_array($s2) ) ) + { + $this->trigger_error('incompatible types - you can\'t combine a non-array with an array', E_USER_WARNING); + return false; + } + else + return $s1 . $s2; + } + + // This method circularly shifts the array left by the number of elements + // given in its parameter. It returns the resulting array and is used for + // the ShiftRow step. Note that shift() and push() could be used for a more + // elegant solution, but they require IE5.5+, so I chose to do it manually. + + function cyclicShiftLeft($theArray, $positions) { + if(!is_int($positions)) + { + $this->trigger_error('$positions is not an integer! Backtrace:
'.print_r(debug_backtrace(), true).'
', E_USER_WARNING); + return false; + } + $second = array_slice($theArray, 0, $positions); + $first = array_slice($theArray, $positions); + $theArray = array_merge($first, $second); + return $theArray; + } + + // Multiplies the element "poly" of GF(2^8) by x. See the Rijndael spec. + + function xtime($poly) { + $poly <<= 1; + return (($poly & 0x100) ? ($poly ^ 0x11B) : ($poly)); + } + + // Multiplies the two elements of GF(2^8) together and returns the result. + // See the Rijndael spec, but should be straightforward: for each power of + // the indeterminant that has a 1 coefficient in x, add y times that power + // to the result. x and y should be bytes representing elements of GF(2^8) + + function mult_GF256($x, $y) { + $result = 0; + + for ($bit = 1; $bit < 256; $bit *= 2, $y = $this->xtime($y)) { + if ($x & $bit) + $result ^= $y; + } + return $result; + } + + // Performs the substitution step of the cipher. State is the 2d array of + // state information (see spec) and direction is string indicating whether + // we are performing the forward substitution ("encrypt") or inverse + // substitution (anything else) + + function byteSub(&$state, $direction) { + //global $this->SBox, $this->SBoxInverse, $this->Nb; + if ($direction == "encrypt") // Point S to the SBox we're using + $S =& $this->SBox; + else + $S =& $this->SBoxInverse; + for ($i = 0; $i < 4; $i++) // Substitute for every byte in state + for ($j = 0; $j < $this->Nb; $j++) + $state[$i][$j] = $S[$state[$i][$j]]; + } + + // Performs the row shifting step of the cipher. + + function shiftRow(&$state, $direction) { + //global $this->Nb, $this->shiftOffsets; + for ($i=1; $i<4; $i++) // Row 0 never shifts + if ($direction == "encrypt") + $state[$i] = $this->cyclicShiftLeft($state[$i], $this->shiftOffsets[$this->Nb][$i]); + else + $state[$i] = $this->cyclicShiftLeft($state[$i], $this->Nb - $this->shiftOffsets[$this->Nb][$i]); + + } + + // Performs the column mixing step of the cipher. Most of these steps can + // be combined into table lookups on 32bit values (at least for encryption) + // to greatly increase the speed. + + function mixColumn(&$state, $direction) { + //global $this->Nb; + $b = Array(); // Result of matrix multiplications + for ($j = 0; $j < $this->Nb; $j++) { // Go through each column... + for ($i = 0; $i < 4; $i++) { // and for each row in the column... + if ($direction == "encrypt") + $b[$i] = $this->mult_GF256($state[$i][$j], 2) ^ // perform mixing + $this->mult_GF256($state[($i+1)%4][$j], 3) ^ + $state[($i+2)%4][$j] ^ + $state[($i+3)%4][$j]; + else + $b[$i] = $this->mult_GF256($state[$i][$j], 0xE) ^ + $this->mult_GF256($state[($i+1)%4][$j], 0xB) ^ + $this->mult_GF256($state[($i+2)%4][$j], 0xD) ^ + $this->mult_GF256($state[($i+3)%4][$j], 9); + } + for ($i = 0; $i < 4; $i++) // Place result back into column + $state[$i][$j] = $b[$i]; + } + } + + // Adds the current round key to the state information. Straightforward. + + function addRoundKey(&$state, $roundKey) { + //global $this->Nb; + for ($j = 0; $j < $this->Nb; $j++) { // Step through columns... + $state[0][$j] ^= ( $roundKey[$j] & 0xFF); // and XOR + $state[1][$j] ^= (($roundKey[$j]>>8) & 0xFF); + $state[2][$j] ^= (($roundKey[$j]>>16) & 0xFF); + $state[3][$j] ^= (($roundKey[$j]>>24) & 0xFF); + } + } + + // This function creates the expanded key from the input (128/192/256-bit) + // key. The parameter key is an array of bytes holding the value of the key. + // The returned value is an array whose elements are the 32-bit words that + // make up the expanded key. + + function keyExpansion($key) { + //global $this->keySizeInBits, $this->blockSizeInBits, $this->roundsArray, $this->Nk, $this->Nb, $this->Nr, $this->Nk, $this->SBox, $this->Rcon; + $expandedKey = Array(); + + // in case the key size or parameters were changed... + $this->Nk = $this->keySizeInBits / 32; + $this->Nb = $this->blockSizeInBits / 32; + $this->Nr = $this->roundsArray[$this->Nk][$this->Nb]; + + for ($j=0; $j < $this->Nk; $j++) // Fill in input key first + $expandedKey[$j] = + ($key[4*$j]) | ($key[4*$j+1]<<8) | ($key[4*$j+2]<<16) | ($key[4*$j+3]<<24); + + // Now walk down the rest of the array filling in expanded key bytes as + // per Rijndael's spec + for ($j = $this->Nk; $j < $this->Nb * ($this->Nr + 1); $j++) { // For each word of expanded key + $temp = $expandedKey[$j - 1]; + if ($j % $this->Nk == 0) + $temp = ( ($this->SBox[($temp>>8) & 0xFF]) | + ($this->SBox[($temp>>16) & 0xFF]<<8) | + ($this->SBox[($temp>>24) & 0xFF]<<16) | + ($this->SBox[$temp & 0xFF]<<24) ) ^ $this->Rcon[floor($j / $this->Nk) - 1]; + elseif ($this->Nk > 6 && $j % $this->Nk == 4) + $temp = ($this->SBox[($temp>>24) & 0xFF]<<24) | + ($this->SBox[($temp>>16) & 0xFF]<<16) | + ($this->SBox[($temp>>8) & 0xFF]<<8) | + ($this->SBox[ $temp & 0xFF]); + $expandedKey[$j] = $expandedKey[$j-$this->Nk] ^ $temp; + } + return $expandedKey; + } + + // Rijndael's round functions... + + function RijndaelRound(&$state, $roundKey) { + $this->byteSub($state, "encrypt"); + $this->shiftRow($state, "encrypt"); + $this->mixColumn($state, "encrypt"); + $this->addRoundKey($state, $roundKey); + } + + function InverseRijndaelRound(&$state, $roundKey) { + $this->addRoundKey($state, $roundKey); + $this->mixColumn($state, "decrypt"); + $this->shiftRow($state, "decrypt"); + $this->byteSub($state, "decrypt"); + } + + function FinalRijndaelRound(&$state, $roundKey) { + $this->byteSub($state, "encrypt"); + $this->shiftRow($state, "encrypt"); + $this->addRoundKey($state, $roundKey); + } + + function InverseFinalRijndaelRound(&$state, $roundKey){ + $this->addRoundKey($state, $roundKey); + $this->shiftRow($state, "decrypt"); + $this->byteSub($state, "decrypt"); + } + + // encrypt is the basic encryption function. It takes parameters + // block, an array of bytes representing a plaintext block, and expandedKey, + // an array of words representing the expanded key previously returned by + // keyExpansion(). The ciphertext block is returned as an array of bytes. + + function cryptBlock($block, $expandedKey) { + //global $this->blockSizeInBits, $this->Nb, $this->Nr; + $t=count($block)*8; + if (!is_array($block) || count($block)*8 != $this->blockSizeInBits) + { + $this->trigger_error('block is bad or block size is wrong
'.print_r($block, true).'

Aiming for size '.$this->blockSizeInBits.', got '.$t.'.', E_USER_WARNING); + return false; + } + if (!$expandedKey) + return; + + $block = $this->packBytes($block); + $this->addRoundKey($block, $expandedKey); + for ($i=1; $i<$this->Nr; $i++) + $this->RijndaelRound($block, $this->array_slice_js_compat($expandedKey, $this->Nb*$i, $this->Nb*($i+1))); + $this->FinalRijndaelRound($block, $this->array_slice_js_compat($expandedKey, $this->Nb*$this->Nr)); + $ret = $this->unpackBytes($block); + return $ret; + } + + // decrypt is the basic decryption function. It takes parameters + // block, an array of bytes representing a ciphertext block, and expandedKey, + // an array of words representing the expanded key previously returned by + // keyExpansion(). The decrypted block is returned as an array of bytes. + + function unCryptBlock($block, $expandedKey) { + $t = count($block)*8; + if (!is_array($block) || count($block)*8 != $this->blockSizeInBits) + { + $this->trigger_error('$block is not a valid rijndael-block array: '.$this->byteArrayToHex($block).'

'.print_r($block, true).'

Block size is '.$t.', should be '.$this->blockSizeInBits.'

', E_USER_WARNING); + return false; + } + if (!$expandedKey) + { + $this->trigger_error('$expandedKey is invalid', E_USER_WARNING); + return false; + } + + $block = $this->packBytes($block); + $this->InverseFinalRijndaelRound($block, $this->array_slice_js_compat($expandedKey, $this->Nb*$this->Nr)); + for ($i = $this->Nr - 1; $i>0; $i--) + { + $this->InverseRijndaelRound($block, $this->array_slice_js_compat($expandedKey, $this->Nb*$i, $this->Nb*($i+1))); + } + $this->addRoundKey($block, $expandedKey); + $ret = $this->unpackBytes($block); + if(!is_array($ret)) + { + $this->trigger_error('$ret is not an array', E_USER_WARNING); + } + return $ret; + } + + // This method takes a byte array (byteArray) and converts it to a string by + // applying String.fromCharCode() to each value and concatenating the result. + // The resulting string is returned. Note that this function SKIPS zero bytes + // under the assumption that they are padding added in formatPlaintext(). + // Obviously, do not invoke this method on raw data that can contain zero + // bytes. It is really only appropriate for printable ASCII/Latin-1 + // values. Roll your own function for more robust functionality :) + + function byteArrayToString($byteArray) { + $result = ""; + for($i=0; $i "10ff". The function returns a + // string. + + /* + function byteArrayToHex($byteArray) { + $result = ""; + if (!$byteArray) + return; + for ($i=0; $i [16, 255]. This + // function returns an array. + + /* + function hexToByteArray($hexString) { + $byteArray = Array(); + if (strlen($hexString) % 2) // must have even length + return; + if (strstr($hexString, "0x") == $hexString || strstr($hexString, "0X") == $hexString) + $hexString = substr($hexString, 2); + for ($i = 0; $ienano_str_split($str, 2); + foreach($str as $s) + { + $arr[] = intval(hexdec($s)); + } + return $arr; + } + + // This function packs an array of bytes into the four row form defined by + // Rijndael. It assumes the length of the array of bytes is divisible by + // four. Bytes are filled in according to the Rijndael spec (starting with + // column 0, row 0 to 3). This function returns a 2d array. + + function packBytes($octets) { + $state = Array(); + if (!$octets || count($octets) % 4) + return; + + $state[0] = Array(); $state[1] = Array(); + $state[2] = Array(); $state[3] = Array(); + for ($j=0; $jblockSizeInBits; + $bpb = $this->blockSizeInBits / 8; // bytes per block + + // if primitive string or String instance + if (is_string($plaintext)) { + $plaintext = $this->enano_str_split($plaintext); + // Unicode issues here (ignoring high byte) + for ($i=0; $icharCodeAt($plaintext[$i], 0) & 0xFF; + } + + for ($i = $bpb - (sizeof($plaintext) % $bpb); $i > 0 && $i < $bpb; $i--) + $plaintext[] = 0; + + return $plaintext; + } + + // Returns an array containing "howMany" random bytes. YOU SHOULD CHANGE THIS + // TO RETURN HIGHER QUALITY RANDOM BYTES IF YOU ARE USING THIS FOR A "REAL" + // APPLICATION. (edit: done, mt_rand() is relatively secure) + + function getRandomBytes($howMany) { + $bytes = Array(); + for ($i=0; $i<$howMany; $i++) + $bytes[$i] = mt_rand(0, 255); + return $bytes; + } + + // rijndaelEncrypt(plaintext, key, mode) + // Encrypts the plaintext using the given key and in the given mode. + // The parameter "plaintext" can either be a string or an array of bytes. + // The parameter "key" must be an array of key bytes. If you have a hex + // string representing the key, invoke hexToByteArray() on it to convert it + // to an array of bytes. The third parameter "mode" is a string indicating + // the encryption mode to use, either "ECB" or "CBC". If the parameter is + // omitted, ECB is assumed. + // + // An array of bytes representing the cihpertext is returned. To convert + // this array to hex, invoke byteArrayToHex() on it. If you are using this + // "for real" it is a good idea to change the function getRandomBytes() to + // something that returns truly random bits. + + function rijndaelEncrypt($plaintext, $key, $mode = 'ECB') { + //global $this->blockSizeInBits, $this->keySizeInBits; + $bpb = $this->blockSizeInBits / 8; // bytes per block + // var ct; // ciphertext + + if($mode == 'CBC') + { + if (!is_string($plaintext) || !is_array($key)) + { + $this->trigger_error('In CBC mode the first and second parameters should be strings', E_USER_WARNING); + return false; + } + } else { + if (!is_array($plaintext) || !is_array($key)) + { + $this->trigger_error('In ECB mode the first and second parameters should be byte arrays', E_USER_WARNING); + return false; + } + } + if (sizeof($key)*8 != $this->keySizeInBits) + { + $this->trigger_error('The key needs to be '. ( $this->keySizeInBits / 8 ) .' bytes in length', E_USER_WARNING); + return false; + } + if ($mode == "CBC") + $ct = $this->getRandomBytes($bpb); // get IV + else { + $mode = "ECB"; + $ct = Array(); + } + + // convert plaintext to byte array and pad with zeros if necessary. + $plaintext = $this->formatPlaintext($plaintext); + + $expandedKey = $this->keyExpansion($key); + + for ($block=0; $blockarray_slice_js_compat($plaintext, $block*$bpb, ($block+1)*$bpb); + if ($mode == "CBC") + { + for ($i=0; $i<$bpb; $i++) + { + $aBlock[$i] ^= $ct[$block*$bpb + $i]; + } + } + $cp = $this->cryptBlock($aBlock, $expandedKey); + $ct = $this->concat($ct, $cp); + } + + return $ct; + } + + // rijndaelDecrypt(ciphertext, key, mode) + // Decrypts the using the given key and mode. The parameter "ciphertext" + // must be an array of bytes. The parameter "key" must be an array of key + // bytes. If you have a hex string representing the ciphertext or key, + // invoke hexToByteArray() on it to convert it to an array of bytes. The + // parameter "mode" is a string, either "CBC" or "ECB". + // + // An array of bytes representing the plaintext is returned. To convert + // this array to a hex string, invoke byteArrayToHex() on it. To convert it + // to a string of characters, you can use byteArrayToString(). + + function rijndaelDecrypt($ciphertext, $key, $mode = 'ECB') { + //global $this->blockSizeInBits, $this->keySizeInBits; + $bpb = $this->blockSizeInBits / 8; // bytes per block + $pt = Array(); // plaintext array + // $aBlock; // a decrypted block + // $block; // current block number + + if (!$ciphertext) + { + $this->trigger_error('$ciphertext should be a byte array', E_USER_WARNING); + return false; + } + if( !is_array($key) ) + { + $this->trigger_error('$key should be a byte array', E_USER_WARNING); + return false; + } + if( is_string($ciphertext) ) + { + $this->trigger_error('$ciphertext should be a byte array', E_USER_WARNING); + return false; + } + if (sizeof($key)*8 != $this->keySizeInBits) + { + $this->trigger_error('Encryption key is the wrong length', E_USER_WARNING); + return false; + } + if (!$mode) + $mode = "ECB"; // assume ECB if mode omitted + + $expandedKey = $this->keyExpansion($key); + + // work backwards to accomodate CBC mode + for ($block=(sizeof($ciphertext) / $bpb)-1; $block>0; $block--) + { + if( ( $block*$bpb ) + ( ($block+1)*$bpb ) > count($ciphertext) ) + { + //$this->trigger_error('$ciphertext index out of bounds', E_USER_ERROR); + } + $current_block = $this->array_slice_js_compat($ciphertext, $block*$bpb, ($block+1)*$bpb); + if(count($current_block) * 8 != $this->blockSizeInBits) + { + // $c=count($current_block)*8; + // $this->trigger_error('We got a '.$c.'-bit block, instead of '.$this->blockSizeInBits.'', E_USER_ERROR); + } + $aBlock = $this->uncryptBlock($current_block, $expandedKey); + if(!$aBlock) + { + $this->trigger_error('Shared block decryption routine returned false', E_USER_WARNING); + return false; + } + if ($mode == "CBC") + for ($i=0; $i<$bpb; $i++) + $pt[($block-1)*$bpb + $i] = $aBlock[$i] ^ $ciphertext[($block-1)*$bpb + $i]; + else + $pt = $this->concat($aBlock, $pt); + } + + // do last block if ECB (skips the IV in CBC) + if ($mode == "ECB") + { + $x = $this->uncryptBlock($this->array_slice_js_compat($ciphertext, 0, $bpb), $expandedKey); + if(!$x) + { + $this->trigger_error('ECB block decryption routine returned false', E_USER_WARNING); + return false; + } + $pt = $this->concat($x, $pt); + if(!$pt) + { + $this->trigger_error('ECB concatenation routine returned false', E_USER_WARNING); + return false; + } + } + + return $pt; + } + + /** + * Wrapper for encryption. + * @param string $text the text to encrypt + * @param string $key the raw binary key to encrypt with + * @param int $return_encoding optional - can be ENC_BINARY, ENC_HEX or ENC_BASE64 + */ + + function encrypt($text, $key, $return_encoding = ENC_HEX) + { + if ( $this->mcrypt && $this->blockSizeInBits == mcrypt_module_get_algo_block_size(eval('return MCRYPT_RIJNDAEL_'.$this->keySizeInBits.';')) ) + { + $iv_size = mcrypt_get_iv_size($this->mcrypt, MCRYPT_MODE_ECB); + $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); + $cryptext = mcrypt_encrypt($this->mcrypt, $key, $text, MCRYPT_MODE_ECB, $iv); + switch($return_encoding) + { + case ENC_HEX: + default: + $cryptext = $this->strtohex($cryptext); + break; + case ENC_BINARY: + $cryptext = $cryptext; + break; + case ENC_BASE64: + $cryptext = base64_encode($cryptext); + break; + } + } + else + { + $key = $this->prepare_string($key); + $text = $this->prepare_string($text); + $cryptext = $this->rijndaelEncrypt($text, $key, 'ECB'); + if(!is_array($cryptext)) + { + echo 'Warning: encryption failed for string: '.$text.'
'; + return false; + } + switch($return_encoding) + { + case ENC_HEX: + default: + $cryptext = $this->byteArrayToHex($cryptext); + break; + case ENC_BINARY: + $cryptext = $this->byteArrayToString($cryptext); + break; + case ENC_BASE64: + $cryptext = base64_encode($this->byteArrayToString($cryptext)); + break; + } + } + return $cryptext; + } + + /** + * Wrapper for decryption. + * @param string $text the encrypted text + * @param string $key the raw binary key used to encrypt the text + * @param int $input_encoding the encoding used for the encrypted string. Can be ENC_BINARY, ENC_HEX, or ENC_BASE64. + * @return string + */ + + function decrypt($text, $key, $input_encoding = ENC_HEX) + { + switch($input_encoding) + { + case ENC_BINARY: + default: + break; + case ENC_HEX: + $text = $this->hextostring($text); + break; + case ENC_BASE64: + $text = base64_decode($text); + break; + } + //$mod = strlen($text) % $this->blockSizeInBits; + //if($mod != 96) + //die('modulus check failed: '.$mod); + if ( $this->mcrypt ) + { + $iv_size = mcrypt_get_iv_size($this->mcrypt, MCRYPT_MODE_ECB); + $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); + $dypt = mcrypt_decrypt($this->mcrypt, $key, $text, MCRYPT_MODE_ECB, $iv); + } + else + { + $etext = $this->prepare_string($text); + $ekey = $this->prepare_string($key); + $mod = count($etext) % $this->blockSizeInBits; + $dypt = $this->rijndaelDecrypt($etext, $ekey, 'ECB'); + if(!$dypt) + { + echo '
'.print_r($dypt, true).'
'; + $this->trigger_error('Rijndael main decryption routine failed', E_USER_ERROR); + } + $dypt = $this->byteArrayToString($dypt); + } + return $dypt; + } + + /** + * Enano-ese equivalent of str_split() which is only found in PHP5 + * @param $text string the text to split + * @param $inc int size of each block + * @return array + */ + + function enano_str_split($text, $inc = 1) + { + if($inc < 1) return false; + if($inc >= strlen($text)) return Array($text); + $len = ceil(strlen($text) / $inc); + $ret = Array(); + for($i=0;$itrigger_error('First parameter should be an array', E_USER_WARNING); + return false; + } + $ret = ''; + foreach($arr as $a) + { + if($a != 0) $ret .= chr($a); + } + return $ret; + } + */ + + function strtohex($str) + { + $str = $this->enano_str_split($str); + $ret = ''; + foreach($str as $s) + { + $chr = dechex(ord($s)); + if(strlen($chr) < 2) $chr = '0' . $chr; + $ret .= $chr; + } + return $ret; + } + + function gen_readymade_key() + { + $key = $this->strtohex($this->randkey($this->keySizeInBits / 8)); + return $key; + } + + function prepare_string($text) + { + $ret = $this->hexToByteArray($this->strtohex($text)); + if(count($ret) != strlen($text)) + die('problem seems to be the hex conversion'); + return $ret; + } + + /** + * Decodes a hex string. + * @param string $hex The hex code to decode + * @return string + */ + + function hextostring($hex) + { + $hex = $this->enano_str_split($hex, 2); + $bin_key = ''; + foreach($hex as $nibble) + { + $byte = chr(hexdec($nibble)); + $bin_key .= $byte; + } + return $bin_key; + } +} + +/** + * XXTEA encryption arithmetic library. + * + * Copyright (C) 2006 Ma Bingyao + * Version: 1.5 + * LastModified: Dec 5, 2006 + * This library is free. You can redistribute it and/or modify it. + * + * From dandaman32: I am treating this code as GPL, as implied by the license statement above. + */ +class TEACrypt extends AESCrypt { + function long2str($v, $w) { + $len = count($v); + $n = ($len - 1) << 2; + if ($w) { + $m = $v[$len - 1]; + if (($m < $n - 3) || ($m > $n)) return false; + $n = $m; + } + $s = array(); + for ($i = 0; $i < $len; $i++) { + $s[$i] = pack("V", $v[$i]); + } + if ($w) { + return substr(join('', $s), 0, $n); + } + else { + return join('', $s); + } + } + + function str2long($s, $w) { + $v = unpack("V*", $s. str_repeat("\0", (4 - strlen($s) % 4) & 3)); + $v = array_values($v); + if ($w) { + $v[count($v)] = strlen($s); + } + return $v; + } + + function int32($n) { + while ($n >= 2147483648) $n -= 4294967296; + while ($n <= -2147483649) $n += 4294967296; + return (int)$n; + } + + function encrypt($str, $key) { + if ($str == "") { + return ""; + } + $v = $this->str2long($str, true); + $k = $this->str2long($key, false); + if (count($k) < 4) { + for ($i = count($k); $i < 4; $i++) { + $k[$i] = 0; + } + } + $n = count($v) - 1; + + $z = $v[$n]; + $y = $v[0]; + $delta = 0x9E3779B9; + $q = floor(6 + 52 / ($n + 1)); + $sum = 0; + while (0 < $q--) { + $sum = $this->int32($sum + $delta); + $e = $sum >> 2 & 3; + for ($p = 0; $p < $n; $p++) { + $y = $v[$p + 1]; + $mx = $this->int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ $this->int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z)); + $z = $v[$p] = $this->int32($v[$p] + $mx); + } + $y = $v[0]; + $mx = $this->int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ $this->int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z)); + $z = $v[$n] = $this->int32($v[$n] + $mx); + } + return $this->long2str($v, false); + } + + function decrypt($str, $key) { + if ($str == "") { + return ""; + } + $v = $this->str2long($str, false); + $k = $this->str2long($key, false); + if (count($k) < 4) { + for ($i = count($k); $i < 4; $i++) { + $k[$i] = 0; + } + } + $n = count($v) - 1; + + $z = $v[$n]; + $y = $v[0]; + $delta = 0x9E3779B9; + $q = floor(6 + 52 / ($n + 1)); + $sum = $this->int32($q * $delta); + while ($sum != 0) { + $e = $sum >> 2 & 3; + for ($p = $n; $p > 0; $p--) { + $z = $v[$p - 1]; + $mx = $this->int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ $this->int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z)); + $y = $v[$p] = $this->int32($v[$p] - $mx); + } + $z = $v[$n]; + $mx = $this->int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ $this->int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z)); + $y = $v[0] = $this->int32($v[0] - $mx); + $sum = $this->int32($sum - $delta); + } + return $this->long2str($v, true); + } +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/search.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/search.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,636 @@ + $v) + { + $arr3[$k] = $v; + } + return $arr3; +} + +/** + * Algorithm to actually do the searching. This system usually works pretty fast (tested and developed on a site with 22 pages) but one + * caveat of this algorithm is that it has to load the entire index into memory. It also requires manual parsing of the search query + * which can be quite CPU-intensive. On the flip side this algorithm is extremely flexible and can be adapted for other uses very easily. + * + * Most of the time, this system is disabled. It is only used when MySQL can't or won't allow FULLTEXT indices. + * + * @package Enano + * @subpackage Page management frontend + * @license GNU General Public License http://www.enanocms.org/Special:GNU_General_Public_License + */ + +class Searcher +{ + + var $results; + var $index; + var $warnings; + var $match_case = false; + + function __construct() + { + $this->warnings = Array(); + } + + function Searcher() + { + $this->__construct(); + } + + function warn($t) + { + if(!in_array($t, $this->warnings)) $this->warnings[] = $t; + } + + function convertCase($text) + { + return ( $this->match_case ) ? $text : strtolower($text); + } + + function buildIndex($texts) + { + $this->index = Array(); + + foreach($texts as $i => $l) + { + $seed = md5(microtime(true) . mt_rand()); + $texts[$i] = str_replace("'", 'xxxApoS'.$seed.'xxx', $texts[$i]); + $texts[$i] = preg_replace('#([\W_]+)#i', ' ', $texts[$i]); + $texts[$i] = preg_replace('#([ ]+?)#', ' ', $texts[$i]); + $texts[$i] = preg_replace('#([\']*){2,}#s', '', $texts[$i]); + $texts[$i] = str_replace('xxxApoS'.$seed.'xxx', "'", $texts[$i]); + $l = $texts[$i]; + $words = Array(); + $good_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\' '; + $good_chars = enano_str_split($good_chars, 1); + $letters = enano_str_split($l, 1); + foreach($letters as $x => $t) + { + if(!in_array($t, $good_chars)) + unset($letters[$x]); + } + $letters = implode('', $letters); + $words = explode(' ', $letters); + foreach($words as $c => $w) + { + if(strlen($w) < 4) + unset($words[$c]); + else + $words[$c] = $w; + } + $words = array_values($words); + foreach($words as $c => $w) + { + if(isset($this->index[$w])) + { + if(!in_array($i, $this->index[$w])) + $this->index[$w][] = $i; + } + else + { + $this->index[$w] = Array(); + $this->index[$w][] = $i; + } + } + } + foreach($this->index as $k => $v) + { + $this->index[$k] = implode(',', $this->index[$k]); + } + } + + function search($query, $texts) + { + + // OK, let's establish some basics here. Here is the procedure for performing the search: + // * search for items that matches all the terms in the correct order. + // * search for items that match in any order + // * eliminate one term and do the loop all over + + $this->results = Array(); + $query = $this->parseQuery($query); + $querybak = $query; + for($i = sizeof($query['any'])-1; $i >= 0; $i--) + { + $res = $this->performCoreSearch($query, $texts, true); + $this->results = enano_safe_array_merge($this->results, $res); + $res = $this->performCoreSearch($query, $texts, false); + $this->results = enano_safe_array_merge($this->results, $res); + unset($query['any'][$i]); + } + + // Last resort - search for any of the terms instead of all of 'em + $res = $this->performCoreSearch($querybak, $texts, false, true); + $this->results = enano_safe_array_merge($this->results, $res); + + $this->highlightResults($querybak); + } + + // $texts should be a textual MySQL query! + // @todo document + function searchMySQL($query, $texts) + { + global $db; + // OK, let's establish some basics here. Here is the procedure for performing the search: + // * search for items that matches all the terms in the correct order. + // * search for items that match in any order + // * eliminate one term and do the loop all over + + $this->results = Array(); + $query = $this->parseQuery($query); + $querytmp = $query; + $querybak = $query; + for($i = sizeof($querytmp['any'])-1; $i >= 0; $i--) + { + $res = $this->performCoreSearchMySQL($querytmp, $texts, true); + $this->results = enano_safe_array_merge($this->results, $res); + $res = $this->performCoreSearchMySQL($querytmp, $texts, false); + $this->results = enano_safe_array_merge($this->results, $res); + unset($querytmp['any'][$i]); + } + + // Last resort - search for any of the terms instead of all of 'em + $res = $this->performCoreSearchMySQL($querybak, $texts, false, true); + $this->results = enano_safe_array_merge($this->results, $res); + + $this->highlightResults($querybak); + } + + /** + * This method assumes that $query is already parsed and $texts is an (associative) array of possible results + * @param array $query A search query parsed with Searcher::parseQuery() + * @param array $texts The list of possible results + * @param bool $exact_order If true, only matches results with the terms in the same order as the terms in the query + * @return array An associative array of results + * @access private + */ + function performCoreSearch($query, $texts, $exact_order = false, $any = false) + { + $textkeys = array_keys($texts); + $results = Array(); + if($exact_order) + { + $query = $this->concatQueryTerms($query); + } + $query['trm'] = array_merge($query['any'], $query['req']); + # Find all remotely possible results first + // Single-word terms + foreach($this->index as $term => $keys) + { + foreach($query['trm'] as $userterm) + { + if($this->convertCase($userterm) == $this->convertCase($term)) + { + $k = explode(',', $keys); + foreach($k as $idxkey) + { + if(isset($texts[$idxkey])) + { + $results[$idxkey] = $texts[$idxkey]; + } + else + { + if(preg_match('#^([0-9]+)$#', $idxkey)) + { + $idxkey = intval($idxkey); + if(isset($texts[$idxkey])) $results[$idxkey] = $texts[$idxkey]; + } + } + } + } + } + } + // Quoted terms + foreach($query['trm'] as $userterm) + { + if(!preg_match('/[\s"\'~`!@#\$%\^&\*\(\)\{\}:;<>,.\/\?_-]/', $userterm)) continue; + foreach($texts as $k => $t) + { + if(strstr($this->convertCase($t), $this->convertCase($userterm))) + { + // We have a match! + if(!isset($results[$k])) $results[$k] = $t; + } + } + } + // Remove excluded terms + foreach($results as $k => $r) + { + foreach($query['not'] as $not) + { + if(strstr($this->convertCase($r), $this->convertCase($not))) unset($results[$k]); + } + } + if(!$any) + { + // Remove results not containing all terms + foreach($results as $k => $r) + { + foreach($query['any'] as $term) + { + if(!strstr($this->convertCase($r), $this->convertCase($term))) unset($results[$k]); + } + } + } + // Remove results not containing all required terms + foreach($results as $k => $r) + { + foreach($query['req'] as $term) + { + if(!strstr($this->convertCase($r), $this->convertCase($term))) unset($results[$k]); + } + } + return $results; + } + + /** + * This is the same as performCoreSearch, but $texts should be a MySQL result resource. This can save tremendous amounts of memory on large sites. + * @param array $query A search query parsed with Searcher::parseQuery() + * @param string $texts A text MySQL query that selects the text as the first column and the index key as the second column + * @param bool $exact_order If true, only matches results with the terms in the same order as the terms in the query + * @return array An associative array of results + * @access private + */ + function performCoreSearchMySQL($query, $texts, $exact_order = false, $any = false) + { + global $db; + $results = Array(); + if($exact_order) + { + $query = $this->concatQueryTerms($query); + } + $query['trm'] = array_merge($query['any'], $query['req']); + # Find all remotely possible results first + $texts = $db->sql_query($texts); + if ( !$texts ) + $db->_die('The error is in the search engine.'); + if ( $r = $db->fetchrow_num($texts) ) + { + do + { + foreach($this->index as $term => $keys) + { + foreach($query['trm'] as $userterm) + { + if($this->convertCase($userterm) == $this->convertCase($term)) + { + $k = explode(',', $keys); + foreach($k as $idxkey) + { + $row[0] = $r[0]; + $row[1] = $r[1]; + if(!isset($row[1])) + { + echo('PHP PARSER BUG: $row[1] is set but not set... includes/search.php:'.__LINE__); + $GLOBALS['template']->footer(); + exit; + } + if($row[1] == $idxkey) + $results[$idxkey] = $row[0]; + else + { + if(preg_match('#^([0-9]+)$#', $idxkey)) + { + $idxkey = intval($idxkey); + if($row[1] == $idxkey) $results[$idxkey] = $row[0]; + } + } + } + } + } + } + // Quoted terms + foreach($query['trm'] as $userterm) + { + if(!preg_match('/[\s"\'~`!@#\$%\^&\*\(\)\{\}:;<>,.\/\?_-]/', $userterm)) continue; + if(strstr($this->convertCase($r[0]), $this->convertCase($userterm))) + { + // We have a match! + if(!isset($results[$r[1]])) $results[$r[1]] = $r[0]; + } + } + } while( $r = $db->fetchrow_num($texts) ); + } + // Remove excluded terms + foreach($results as $k => $r) + { + foreach($query['not'] as $not) + { + if(strstr($this->convertCase($r), $this->convertCase($not))) unset($results[$k]); + } + } + if(!$any) + { + // Remove results not containing all terms + foreach($results as $k => $r) + { + foreach($query['any'] as $term) + { + if(!strstr($this->convertCase($r), $this->convertCase($term))) unset($results[$k]); + } + } + } + // Remove results not containing all terms + foreach($results as $k => $r) + { + foreach($query['req'] as $term) + { + if(!strstr($this->convertCase($r), $this->convertCase($term))) unset($results[$k]); + } + } + return $results; + } + + function concatQueryTerms($query) + { + $tmp = implode(' ', $query['any']); + unset($query['any']); + $query['any'] = Array(0 => $tmp); + return $query; + } + + /** + * Builds a basic assoc array with a more organized version of the query + */ + + function parseQuery($query) + { + $ret = array( + 'any' => array(), + 'req' => array(), + 'not' => array() + ); + $terms = array(); + $in_quote = false; + $start_term = 0; + $just_finished = false; + for ( $i = 0; $i < strlen($query); $i++ ) + { + $chr = $query{$i}; + $prev = ( $i > 0 ) ? $query{ $i - 1 } : ''; + $next = ( ( $i + 1 ) < strlen($query) ) ? $query{ $i + 1 } : ''; + + if ( ( $chr == ' ' && !$in_quote ) || ( $i + 1 == strlen ( $query ) ) ) + { + $len = ( $next == '' ) ? $i + 1 : $i - $start_term; + $word = substr ( $query, $start_term, $len ); + $terms[] = $word; + $start_term = $i + 1; + } + + elseif ( $chr == '"' && $in_quote && $prev != '\\' ) + { + $word = substr ( $query, $start_term, $i - $start_term + 1 ); + $start_pos = ( $next == ' ' ) ? $i + 2 : $i + 1; + $in_quote = false; + } + + elseif ( $chr == '"' && !$in_quote ) + { + $in_quote = true; + $start_pos = $i; + } + + } + + $ticker = 0; + + foreach ( $terms as $element => $__unused ) + { + $atom =& $terms[$element]; + + $ticker++; + + if ( $ticker == 20 ) + { + $this->warn('Some of your search terms were excluded because searches are limited to 20 terms to prevent excessive server load.'); + break; + } + + if ( substr ( $atom, 0, 2 ) == '+"' && substr ( $atom, ( strlen ( $atom ) - 1 ), 1 ) == '"' ) + { + $word = substr ( $atom, 2, ( strlen( $atom ) - 3 ) ); + if ( strlen ( $word ) < 4 ) + { + $this->warn('One or more of your search terms was excluded because terms must be at least 4 characters in length.'); + $ticker--; + continue; + } + if(in_array($word, $ret['req'])) + { + $this->warn('One or more of your search terms was excluded because duplicate terms were encountered.'); + $ticker--; + continue; + } + $ret['req'][] = $word; + } + elseif ( substr ( $atom, 0, 2 ) == '-"' && substr ( $atom, ( strlen ( $atom ) - 1 ), 1 ) == '"' ) + { + $word = substr ( $atom, 2, ( strlen( $atom ) - 3 ) ); + if ( strlen ( $word ) < 4 ) + { + $this->warn('One or more of your search terms was excluded because terms must be at least 4 characters in length.'); + $ticker--; + continue; + } + if(in_array($word, $ret['not'])) + { + $this->warn('One or more of your search terms was excluded because duplicate terms were encountered.'); + $ticker--; + continue; + } + $ret['not'][] = $word; + } + elseif ( substr ( $atom, 0, 1 ) == '+' ) + { + $word = substr ( $atom, 1 ); + if ( strlen ( $word ) < 4 ) + { + $this->warn('One or more of your search terms was excluded because terms must be at least 4 characters in length.'); + $ticker--; + continue; + } + if(in_array($word, $ret['req'])) + { + $this->warn('One or more of your search terms was excluded because duplicate terms were encountered.'); + $ticker--; + continue; + } + $ret['req'][] = $word; + } + elseif ( substr ( $atom, 0, 1 ) == '-' ) + { + $word = substr ( $atom, 1 ); + if ( strlen ( $word ) < 4 ) + { + $this->warn('One or more of your search terms was excluded because terms must be at least 4 characters in length.'); + $ticker--; + continue; + } + if(in_array($word, $ret['not'])) + { + $this->warn('One or more of your search terms was excluded because duplicate terms were encountered.'); + $ticker--; + continue; + } + $ret['not'][] = $word; + } + elseif ( substr ( $atom, 0, 1 ) == '"' && substr ( $atom, ( strlen($atom) - 1 ), 1 ) == '"' ) + { + $word = substr ( $atom, 1, ( strlen ( $atom ) - 2 ) ); + if ( strlen ( $word ) < 4 ) + { + $this->warn('One or more of your search terms was excluded because terms must be at least 4 characters in length.'); + $ticker--; + continue; + } + if(in_array($word, $ret['any'])) + { + $this->warn('One or more of your search terms was excluded because duplicate terms were encountered.'); + $ticker--; + continue; + } + $ret['any'][] = $word; + } + else + { + $word = $atom; + if ( strlen ( $word ) < 4 ) + { + $this->warn('One or more of your search terms was excluded because terms must be at least 4 characters in length.'); + $ticker--; + continue; + } + if(in_array($word, $ret['any'])) + { + $this->warn('One or more of your search terms was excluded because duplicate terms were encountered.'); + $ticker--; + continue; + } + $ret['any'][] = $word; + } + } + return $ret; + } + + function highlightResults($query, $starttag = '', $endtag = '') + { + $query['trm'] = array_merge($query['any'], $query['req']); + //die('
'.print_r($query, true).'
'); + foreach($query['trm'] as $q) + { + foreach($this->results as $k => $r) + { + $startplace = 0; + //$this->results[$k] = htmlspecialchars($this->results[$k]); + for($i = 0; $i < strlen($r); $i++) + { + $word = substr($r, $i, strlen($q)); + if($this->convertCase($word) == $this->convertCase($q)) + { + $word = $starttag . $word . $endtag; + $this->results[$k] = substr($r, 0, $i) . $word . substr($r, $i + strlen($q), strlen($r)+999999); + $startplace = $i - 75; + if($startplace < 0) $startplace = 0; + $this->results[$k] = '...'.trim(substr($this->results[$k], $startplace, strlen($word) + 150)).'...'; + continue 2; + } + } + } + } + } + +} + +/** + * Developer-friendly way to do searches. :-) Uses the MySQL FULLTEXT index type. + * @package Enano + * @subpackage Search + */ + +class MySQL_Fulltext_Search { + + /** + * Performs a search. + * @param string The search query + * @return resource MySQL result resource - this is an UNBUFFERED query. + */ + + function search($query) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $fulltext_col = 'MATCH(t.page_id,t.namespace,p.name,t.page_text) AGAINST (\'' . $db->escape($query) . '\' IN BOOLEAN MODE)'; + $sql = "SELECT t.page_text,CONCAT('ns=',t.namespace,';pid=',t.page_id) AS page_identifier, $fulltext_col AS score, CHAR_LENGTH(t.page_text) AS length FROM ".table_prefix."page_text AS t + LEFT JOIN ".table_prefix."pages AS p + ON ( p.urlname=t.page_id AND p.namespace=t.namespace) + WHERE $fulltext_col > 0 + AND p.visible=1 + ORDER BY score DESC;"; + $q = $db->sql_unbuffered_query($sql); + if ( !$q ) + $db->_die(); + + return $q; + } + + function highlight_result($query, $result) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $search = new Searcher(); + $parsed_query = $search->parseQuery($query); + return $this->highlight_result_inner($query, $result); + } + + function highlight_result_inner($query, $fulltext, $starttag = '', $endtag = '') + { + $result = false; + $query['trm'] = array_merge($query['any'], $query['req']); + //die('
'.print_r($query, true).'
'); + foreach($query['trm'] as $q) + { + $startplace = 0; + //$this->results[$k] = htmlspecialchars($this->results[$k]); + for($i = 0; $i < strlen($r); $i++) + { + $word = substr($r, $i, strlen($q)); + if($this->convertCase($word) == $this->convertCase($q)) + { + $word = $starttag . $word . $endtag; + $result = substr($fulltext, 0, $i) . $word . substr($r, $i + strlen($q), strlen($r)+99999999); + $startplace = $i - 75; + if($startplace < 0) $startplace = 0; + $result = '...'.trim(substr($result, $startplace, strlen($word) + 150)).'...'; + continue 2; + } + } + } + return $result; + } + +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/sessions.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/sessions.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,2439 @@ +escape($str); } + +/** + * Anything and everything related to security and user management. This includes AES encryption, which is illegal in some countries. + * Documenting the API was not easy - I hope you folks enjoy it. + * @package Enano + * @subpackage Session manager + * @category security, user management, logins, etc. + */ + +class sessionManager { + + # Variables + + /** + * Whether we're logged in or not + * @var bool + */ + + var $user_logged_in = false; + + /** + * Our current low-privilege session key + * @var string + */ + + var $sid; + + /** + * Username of currently logged-in user, or IP address if not logged in + * @var string + */ + + var $username; + + /** + * User ID of currently logged-in user, or -1 if not logged in + * @var int + */ + + var $user_id; + + /** + * Real name of currently logged-in user, or blank if not logged in + * @var string + */ + + var $real_name; + + /** + * E-mail address of currently logged-in user, or blank if not logged in + * @var string + */ + + var $email; + + /** + * User level of current user + * USER_LEVEL_GUEST: guest + * USER_LEVEL_MEMBER: regular user + * USER_LEVEL_CHPREF: default - pseudo-level that allows changing password and e-mail address (requires re-authentication) + * USER_LEVEL_MOD: moderator + * USER_LEVEL_ADMIN: administrator + * @var int + */ + + var $user_level; + + /** + * High-privilege session key + * @var string or false if not running on high-level authentication + */ + + var $sid_super; + + /** + * The user's theme preference, defaults to $template->default_theme + * @var string + */ + + var $theme; + + /** + * The user's style preference, or style auto-detected based on theme if not logged in + * @var string + */ + + var $style; + + /** + * Signature of current user - appended to comments, etc. + * @var string + */ + + var $signature; + + /** + * UNIX timestamp of when we were registered, or 0 if not logged in + * @var int + */ + + var $reg_time; + + /** + * MD5 hash of the current user's password, if applicable + * @var string OR bool false + */ + + var $password_hash; + + /** + * The number of unread private messages this user has. + * @var int + */ + + var $unread_pms = 0; + + /** + * AES key used to encrypt passwords and session key info - irreversibly destroyed when disallow_password_grab() is called + * @var string + */ + + var $private_key; + + /** + * Regex that defines a valid username, minus the ^ and $, these are added later + * @var string + */ + + var $valid_username = '([A-Za-z0-9 \!\@\(\)-]+)'; + + /** + * What we're allowed to do as far as permissions go. This changes based on the value of the "auth" URI param. + * @var string + */ + + var $auth_level = -1; + + /** + * State variable to track if a session timed out + * @var bool + */ + + var $sw_timed_out = false; + + /** + * Switch to track if we're started or not. + * @access private + * @var bool + */ + + var $started = false; + + /** + * Switch to control compatibility mode (for older Enano websites being upgraded) + * @access private + * @var bool + */ + + var $compat = false; + + /** + * Our list of permission types. + * @access private + * @var array + */ + + var $acl_types = Array(); + + /** + * The list of descriptions for the permission types + * @var array + */ + + var $acl_descs = Array(); + + /** + * A list of dependencies for ACL types. + * @var array + */ + + var $acl_deps = Array(); + + /** + * Our tell-all list of permissions. + * @access private - or, preferably, protected + * @var array + */ + + var $perms = Array(); + + /** + * A cache variable - saved after sitewide permissions are checked but before page-specific permissions. + * @var array + * @access private + */ + + var $acl_base_cache = Array(); + + /** + * Stores the scope information for ACL types. + * @var array + * @access private + */ + + var $acl_scope = Array(); + + /** + * Array to track which default permissions are being used + * @var array + * @access private + */ + + var $acl_defaults_used = Array(); + + /** + * Array to track group membership. + * @var array + */ + + var $groups = Array(); + + /** + * Associative array to track group modship. + * @var array + */ + + var $group_mod = Array(); + + # Basic functions + + /** + * Constructor. + */ + + function __construct() + { + global $db, $session, $paths, $template, $plugins; // Common objects + include(ENANO_ROOT.'/config.php'); + unset($dbhost, $dbname, $dbuser, $dbpasswd); + if(isset($crypto_key)) + { + $this->private_key = $crypto_key; + $this->private_key = hexdecode($this->private_key); + } + else + { + if(is_writable(ENANO_ROOT.'/config.php')) + { + // Generate and stash a private key + // This should only happen during an automated silent gradual migration to the new encryption platform. + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + $this->private_key = $aes->gen_readymade_key(); + + $config = file_get_contents(ENANO_ROOT.'/config.php'); + if(!$config) + { + die('$session->__construct(): can\'t get the contents of config.php'); + } + + $config = str_replace("?>", "\$crypto_key = '{$this->private_key}';\n?>", $config); + // And while we're at it... + $config = str_replace('MIDGET_INSTALLED', 'ENANO_INSTALLED', $config); + $fh = @fopen(ENANO_ROOT.'/config.php', 'w'); + if ( !$fh ) + { + die('$session->__construct(): Couldn\'t open config file for writing to store the private key, I tried to avoid something like this...'); + } + + fwrite($fh, $config); + fclose($fh); + } + else + { + die_semicritical('Crypto error', '

No private key was found in the config file, and we can\'t generate one because we don\'t have write access to the config file. Please CHMOD config.php to 666 or 777 and reload this page.

'); + } + } + // Check for compatibility mode + if(defined('IN_ENANO_INSTALL')) + { + $q = $db->sql_query('SELECT old_encryption FROM '.table_prefix.'users LIMIT 1;'); + if(!$q) + { + $error = mysql_error(); + if(strstr($error, "Unknown column 'old_encryption'")) + $this->compat = true; + else + $db->_die('This should never happen and is a bug - the only error that was supposed to happen here didn\'t happen. (sessions.php in constructor, during compat mode check)'); + } + $db->free_result(); + } + } + + /** + * PHP 4 compatible constructor. + */ + + function sessionManager() + { + $this->__construct(); + } + + /** + * Wrapper function to sanitize strings for MySQL and HTML + * @param string $text The text to sanitize + * @return string + */ + + function prepare_text($text) + { + global $db; + return $db->escape(htmlspecialchars($text)); + } + + /** + * Makes a SQL query and handles error checking + * @param string $query The SQL query to make + * @return resource + */ + + function sql($query) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $result = $db->sql_query($query); + if(!$result) + { + $db->_die('The error seems to have occurred somewhere in the session management code.'); + } + return $result; + } + + # Session restoration and permissions + + /** + * Initializes the basic state of things, including most user prefs, login data, cookie stuff + */ + + function start() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if($this->started) return; + $this->started = true; + $user = false; + if(isset($_COOKIE['sid'])) + { + if($this->compat) + { + $userdata = $this->compat_validate_session($_COOKIE['sid']); + } + else + { + $userdata = $this->validate_session($_COOKIE['sid']); + } + if(is_array($userdata)) + { + $data = RenderMan::strToPageID($paths->get_pageid_from_url()); + + if(!$this->compat && $userdata['account_active'] != 1 && $data[1] != 'Special' && $data[1] != 'Admin') + { + $this->logout(); + $a = getConfig('account_activation'); + switch($a) + { + case 'none': + default: + $solution = 'Your account was most likely deactivated by an administrator. Please contact the site administration for further assistance.'; + break; + case 'user': + $solution = 'Please check your e-mail; you should have been sent a message with instructions on how to activate your account. If you do not receive an e-mail from this site within 24 hours, please contact the site administration for further assistance.'; + break; + case 'admin': + $solution = 'This website has been configured so that all user accounts must be activated by the administrator before they can be used, so your account will most likely be activated the next time the one of the administrators visits the site.'; + break; + } + die_semicritical('Account error', '

It appears that your user account has not yet been activated. '.$solution.'

'); + } + + $this->sid = $_COOKIE['sid']; + $this->user_logged_in = true; + $this->user_id = intval($userdata['user_id']); + $this->username = $userdata['username']; + $this->password_hash = $userdata['password']; + $this->user_level = intval($userdata['user_level']); + $this->real_name = $userdata['real_name']; + $this->email = $userdata['email']; + $this->unread_pms = $userdata['num_pms']; + if(!$this->compat) + { + $this->theme = $userdata['theme']; + $this->style = $userdata['style']; + $this->signature = $userdata['signature']; + $this->reg_time = $userdata['reg_time']; + } + // Small security risk here - it allows someone who has already authenticated as an administrator to store the "super" key in + // the cookie. Change this to USER_LEVEL_MEMBER to override that. The same 15-minute restriction applies to this "exploit". + $this->auth_level = $userdata['auth_level']; + if(!isset($template->named_theme_list[$this->theme])) + { + if($this->compat || !is_object($template)) + { + $this->theme = 'oxygen'; + $this->style = 'bleu'; + } + else + { + $this->theme = $template->default_theme; + $this->style = $template->default_style; + } + } + $user = true; + + if(isset($_REQUEST['auth']) && !$this->sid_super) + { + // Now he thinks he's a moderator. Or maybe even an administrator. Let's find out if he's telling the truth. + if($this->compat) + { + $key = $_REQUEST['auth']; + $super = $this->compat_validate_session($key); + } + else + { + $key = strrev($_REQUEST['auth']); + $super = $this->validate_session($key); + } + if(is_array($super)) + { + $this->auth_level = intval($super['auth_level']); + $this->sid_super = $_REQUEST['auth']; + } + } + } + } + if(!$user) + { + //exit; + $this->register_guest_session(); + } + if(!$this->compat) + { + // init groups + $q = $this->sql('SELECT g.group_name,g.group_id,m.is_mod FROM '.table_prefix.'groups AS g + LEFT JOIN '.table_prefix.'group_members AS m + ON g.group_id=m.group_id + WHERE ( m.user_id='.$this->user_id.' + OR g.group_name=\'Everyone\') + ' . ( enano_version() == '1.0RC1' ? '' : 'AND ( m.pending != 1 OR m.pending IS NULL )' ) . ' + ORDER BY group_id ASC;'); // Make sure "Everyone" comes first so the permissions can be overridden + if($row = $db->fetchrow()) + { + do { + $this->groups[$row['group_id']] = $row['group_name']; + $this->group_mod[$row['group_id']] = ( intval($row['is_mod']) == 1 ); + } while($row = $db->fetchrow()); + } + else + { + die('No group info'); + } + } + $this->check_banlist(); + + if ( isset ( $_GET['printable'] ) ) + { + $this->theme = 'printable'; + $this->style = 'default'; + } + + } + + # Logins + + /** + * Attempts to perform a login using crypto functions + * @param string $username The username + * @param string $aes_data The encrypted password, hex-encoded + * @param string $aes_key The MD5 hash of the encryption key, hex-encoded + * @param string $challenge The 256-bit MD5 challenge string - first 128 bits should be the hash, the last 128 should be the challenge salt + * @param int $level The privilege level we're authenticating for, defaults to 0 + * @return string 'success' on success, or error string on failure + */ + + function login_with_crypto($username, $aes_data, $aes_key, $challenge, $level = USER_LEVEL_MEMBER) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $privcache = $this->private_key; + + // Instanciate the Rijndael encryption object + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + + // Fetch our decryption key + + $aes_key = $this->fetch_public_key($aes_key); + if(!$aes_key) + return 'Couldn\'t look up public key "'.$aes_key.'" for decryption'; + + // Convert the key to a binary string + $bin_key = hexdecode($aes_key); + + if(strlen($bin_key) != AES_BITS / 8) + return 'The decryption key is the wrong length'; + + // Decrypt our password + $password = $aes->decrypt($aes_data, $bin_key, ENC_HEX); + + // Initialize our success switch + $success = false; + + // Select the user data from the table, and decrypt that so we can verify the password + $this->sql('SELECT password,old_encryption,user_id,user_level,theme,style,temp_password,temp_password_time FROM '.table_prefix.'users WHERE lcase(username)=\''.$this->prepare_text(strtolower($username)).'\';'); + if($db->numrows() < 1) + return 'The username and/or password is incorrect.'; + $row = $db->fetchrow(); + + // Check to see if we're logging in using a temporary password + + if((intval($row['temp_password_time']) + 3600*24) > time() ) + { + $temp_pass = $aes->decrypt( $row['temp_password'], $this->private_key, ENC_HEX ); + if( $temp_pass == $password ) + { + $url = makeUrlComplete('Special', 'PasswordReset/stage2/' . $row['user_id'] . '/' . $row['temp_password']); + + $code = $plugins->setHook('login_password_reset'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + redirect($url, 'Login sucessful', 'Please wait while you are transferred to the Password Reset form.'); + exit; + } + } + + if($row['old_encryption'] == 1) + { + // The user's password is stored using the obsolete and insecure MD5 algorithm, so we'll update the field with the new password + if(md5($password) == $row['password']) + { + $pass_stashed = $aes->encrypt($password, $this->private_key, ENC_HEX); + $this->sql('UPDATE '.table_prefix.'users SET password=\''.$pass_stashed.'\',old_encryption=0 WHERE user_id='.$row['user_id'].';'); + $success = true; + } + } + else + { + // Our password field is up-to-date with the >=1.0RC1 encryption standards, so decrypt the password in the table and see if we have a match; if so then do challenge authentication + $real_pass = $aes->decrypt(hexdecode($row['password']), $this->private_key, ENC_BINARY); + if($password == $real_pass) + { + // Yay! We passed AES authentication, now do an MD5 challenge check to make sure we weren't spoofed + $chal = substr($challenge, 0, 32); + $salt = substr($challenge, 32, 32); + $correct_challenge = md5( $real_pass . $salt ); + if($chal == $correct_challenge) + $success = true; + } + } + if($success) + { + if($level > $row['user_level']) + return 'You are not authorized for this level of access.'; + + $sess = $this->register_session(intval($row['user_id']), $username, $password, $level); + if($sess) + { + $this->username = $username; + $this->user_id = intval($row['user_id']); + $this->theme = $row['theme']; + $this->style = $row['style']; + + if($level > USER_LEVEL_MEMBER) + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); + else + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')'); + + $code = $plugins->setHook('login_success'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + return 'success'; + } + else + return 'Your login credentials were correct, but an internal error occurred while registering the session key in the database.'; + } + else + { + if($level > USER_LEVEL_MEMBER) + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); + else + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')'); + + return 'The username and/or password is incorrect.'; + } + } + + /** + * Attempts to login without using crypto stuff, mainly for use when the other side doesn't like Javascript + * This method of authentication is inherently insecure, there's really nothing we can do about it except hope and pray that everyone moves to Firefox + * Technically it still uses crypto, but it only decrypts the password already stored, which is (obviously) required for authentication + * @param string $username The username + * @param string $password The password -OR- the MD5 hash of the password if $already_md5ed is true + * @param bool $already_md5ed This should be set to true if $password is an MD5 hash, and should be false if it's plaintext. Defaults to false. + * @param int $level The privilege level we're authenticating for, defaults to 0 + */ + + function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $pass_hashed = ( $already_md5ed ) ? $password : md5($password); + + // Perhaps we're upgrading Enano? + if($this->compat) + { + return $this->login_compat($username, $pass_hashed, $level); + } + + // Instanciate the Rijndael encryption object + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + + // Initialize our success switch + $success = false; + + // Retrieve the real password from the database + $this->sql('SELECT password,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix.'users WHERE lcase(username)=\''.$this->prepare_text(strtolower($username)).'\';'); + if($db->numrows() < 1) + return 'The username and/or password is incorrect.'; + $row = $db->fetchrow(); + + // Check to see if we're logging in using a temporary password + + if((intval($row['temp_password_time']) + 3600*24) > time() ) + { + $temp_pass = $aes->decrypt( $row['temp_password'], $this->private_key, ENC_HEX ); + if( md5($temp_pass) == $pass_hashed ) + { + $code = $plugins->setHook('login_password_reset'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + header('Location: ' . makeUrlComplete('Special', 'PasswordReset/stage2/' . $row['user_id'] . '/' . $row['temp_password']) ); + + exit; + } + } + + if($row['old_encryption'] == 1) + { + // The user's password is stored using the obsolete and insecure MD5 algorithm - we'll update the field with the new password + if($pass_hashed == $row['password'] && !$already_md5ed) + { + $pass_stashed = $aes->encrypt($password, $this->private_key, ENC_HEX); + $this->sql('UPDATE '.table_prefix.'users SET password=\''.$pass_stashed.'\',old_encryption=0 WHERE user_id='.$row['user_id'].';'); + $success = true; + } + elseif($pass_hashed == $row['password'] && $already_md5ed) + { + // We don't have the real password so don't bother with encrypting it, just call it success and get out of here + $success = true; + } + } + else + { + // Our password field is up-to-date with the >=1.0RC1 encryption standards, so decrypt the password in the table and see if we have a match + $real_pass = $aes->decrypt($row['password'], $this->private_key); + if($pass_hashed == md5($real_pass)) + { + $success = true; + } + } + if($success) + { + if((int)$level > (int)$row['user_level']) + return 'You are not authorized for this level of access.'; + $sess = $this->register_session(intval($row['user_id']), $username, $real_pass, $level); + if($sess) + { + if($level > USER_LEVEL_MEMBER) + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); + else + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')'); + + $code = $plugins->setHook('login_success'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + return 'success'; + } + else + return 'Your login credentials were correct, but an internal error occured while registering the session key in the database.'; + } + else + { + if($level > USER_LEVEL_MEMBER) + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); + else + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')'); + + return 'The username and/or password is incorrect.'; + } + } + + /** + * Attempts to log in using the old table structure and algorithm. + * @param string $username + * @param string $password This should be an MD5 hash + * @return string 'success' if successful, or error message on failure + */ + + function login_compat($username, $password, $level = 0) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $pass_hashed =& $password; + $this->sql('SELECT password,user_id,user_level FROM '.table_prefix.'users WHERE username=\''.$this->prepare_text($username).'\';'); + if($db->numrows() < 1) + return 'The username and/or password is incorrect.'; + $row = $db->fetchrow(); + if($row['password'] == $password) + { + if((int)$level > (int)$row['user_level']) + return 'You are not authorized for this level of access.'; + $sess = $this->register_session_compat(intval($row['user_id']), $username, $password, $level); + if($sess) + return 'success'; + else + return 'Your login credentials were correct, but an internal error occured while registering the session key in the database.'; + } + else + { + return 'The username and/or password is incorrect.'; + } + } + + /** + * Registers a session key in the database. This function *ASSUMES* that the username and password have already been validated! + * Basically the session key is a base64-encoded cookie (encrypted with the site's private key) that says "u=[username];p=[sha1 of password]" + * @param int $user_id + * @param string $username + * @param string $password + * @param int $level The level of access to grant, defaults to USER_LEVEL_MEMBER + * @return bool + */ + + function register_session($user_id, $username, $password, $level = USER_LEVEL_MEMBER) + { + $salt = md5(microtime() . mt_rand()); + $passha1 = sha1($password); + $session_key = "u=$username;p=$passha1;s=$salt"; + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + $session_key = $aes->encrypt($session_key, $this->private_key, ENC_HEX); + if($level > USER_LEVEL_MEMBER) + { + $hexkey = strrev($session_key); + $this->sid_super = $hexkey; + $_GET['auth'] = $hexkey; + } + else + { + setcookie( 'sid', $session_key, time()+315360000, scriptPath.'/' ); + $_COOKIE['sid'] = $session_key; + } + $keyhash = md5($session_key); + $ip = ip2hex($_SERVER['REMOTE_ADDR']); + if(!$ip) + die('$session->register_session: Remote-Addr was spoofed'); + $time = time(); + if(!is_int($user_id)) + die('Somehow an SQL injection attempt crawled into our session registrar! (1)'); + if(!is_int($level)) + die('Somehow an SQL injection attempt crawled into our session registrar! (2)'); + + $query = $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$keyhash.'\', \''.$salt.'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');'); + return true; + } + + /** + * Identical to register_session in nature, but uses the old login/table structure. DO NOT use this. + * @see sessionManager::register_session() + * @access private + */ + + function register_session_compat($user_id, $username, $password, $level = 0) + { + $salt = md5(microtime() . mt_rand()); + $thekey = md5($password . $salt); + if($level > 0) + { + $this->sid_super = $thekey; + } + else + { + setcookie( 'sid', $thekey, time()+315360000, scriptPath.'/' ); + $_COOKIE['sid'] = $thekey; + } + $ip = ip2hex($_SERVER['REMOTE_ADDR']); + if(!$ip) + die('$session->register_session: Remote-Addr was spoofed'); + $time = time(); + if(!is_int($user_id)) + die('Somehow an SQL injection attempt crawled into our session registrar! (1)'); + if(!is_int($level)) + die('Somehow an SQL injection attempt crawled into our session registrar! (2)'); + $query = $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$thekey.'\', \''.$salt.'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');'); + return true; + } + + /** + * Creates/restores a guest session + * @todo implement real session management for guests + */ + + function register_guest_session() + { + global $db, $session, $paths, $template, $plugins; // Common objects + $this->username = $_SERVER['REMOTE_ADDR']; + $this->user_level = USER_LEVEL_GUEST; + if($this->compat || defined('IN_ENANO_INSTALL')) + { + $this->theme = 'oxygen'; + $this->style = 'bleu'; + } + else + { + $this->theme = ( isset($_GET['theme']) && isset($template->named_theme_list[$_GET['theme']])) ? $_GET['theme'] : $template->default_theme; + $this->style = ( isset($_GET['style']) && file_exists(ENANO_ROOT.'/themes/'.$this->theme . '/css/'.$_GET['style'].'.css' )) ? $_GET['style'] : substr($template->named_theme_list[$this->theme]['default_style'], 0, strlen($template->named_theme_list[$this->theme]['default_style'])-4); + } + $this->user_id = 1; + } + + /** + * Validates a session key, and returns the userdata associated with the key or false + * @param string $key The session key to validate + * @return array Keys are 'user_id', 'username', 'email', 'real_name', 'user_level', 'theme', 'style', 'signature', 'reg_time', 'account_active', 'activation_key', and 'auth_level' or bool false if validation failed. The key 'auth_level' is the maximum authorization level that this key provides. + */ + + function validate_session($key) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE, true); + $decrypted_key = $aes->decrypt($key, $this->private_key, ENC_HEX); + + if ( !$decrypted_key ) + { + die_semicritical('AES encryption error', '

Something went wrong during the AES decryption process.

'.print_r($decrypted_key, true).'
'); + } + + $n = preg_match('/^u='.$this->valid_username.';p=([A-Fa-f0-9]+?);s=([A-Fa-f0-9]+?)$/', $decrypted_key, $keydata); + if($n < 1) + { + // echo '(debug) $session->validate_session: Key does not match regex
Decrypted key: '.$decrypted_key; + return false; + } + $keyhash = md5($key); + $salt = $db->escape($keydata[3]); + $query = $this->sql('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms,x.* FROM '.table_prefix.'session_keys AS k + LEFT JOIN '.table_prefix.'users AS u + ON ( u.user_id=k.user_id ) + LEFT JOIN '.table_prefix.'users_extra AS x + ON ( u.user_id=x.user_id OR x.user_id IS NULL ) + LEFT JOIN '.table_prefix.'privmsgs AS p + ON ( p.message_to=u.username AND p.message_read=0 ) + WHERE k.session_key=\''.$keyhash.'\' + AND k.salt=\''.$salt.'\' + GROUP BY u.user_id;'); + if($db->numrows() < 1) + { + // echo '(debug) $session->validate_session: Key was not found in database
'; + return false; + } + $row = $db->fetchrow(); + $row['user_id'] =& $row['uid']; + $ip = ip2hex($_SERVER['REMOTE_ADDR']); + if($row['auth_level'] > $row['user_level']) + { + // Failed authorization check + // echo '(debug) $session->validate_session: access to this auth level denied
'; + return false; + } + if($ip != $row['source_ip']) + { + // Failed IP address check + // echo '(debug) $session->validate_session: IP address mismatch
'; + return false; + } + + // Do the password validation + $real_pass = $aes->decrypt($row['password'], $this->private_key, ENC_HEX); + + //die('
'.print_r($keydata, true).'
'); + if(sha1($real_pass) != $keydata[2]) + { + // Failed password check + // echo '(debug) $session->validate_session: encrypted password is wrong
Real password: '.$real_pass.'
Real hash: '.sha1($real_pass).'
User hash: '.$keydata[2]; + return false; + } + + $time_now = time(); + $time_key = $row['time'] + 900; + if($time_now > $time_key && $row['auth_level'] > USER_LEVEL_MEMBER) + { + // Session timed out + // echo '(debug) $session->validate_session: super session timed out
'; + $this->sw_timed_out = true; + return false; + } + + // If this is an elevated-access session key, update the time + if( $row['auth_level'] > USER_LEVEL_MEMBER ) + { + $this->sql('UPDATE '.table_prefix.'session_keys SET time='.time().' WHERE session_key=\''.$keyhash.'\';'); + } + + $row['password'] = md5($real_pass); + return $row; + } + + /** + * Validates a session key, and returns the userdata associated with the key or false. Optimized for compatibility with the old MD5-based auth system. + * @param string $key The session key to validate + * @return array Keys are 'user_id', 'username', 'email', 'real_name', 'user_level', 'theme', 'style', 'signature', 'reg_time', 'account_active', 'activation_key', and 'auth_level' or bool false if validation failed. The key 'auth_level' is the maximum authorization level that this key provides. + */ + + function compat_validate_session($key) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $key = $db->escape($key); + + $query = $this->sql('SELECT u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,k.source_ip,k.salt,k.time,k.auth_level FROM '.table_prefix.'session_keys AS k + LEFT JOIN '.table_prefix.'users AS u + ON u.user_id=k.user_id + WHERE k.session_key=\''.$key.'\';'); + if($db->numrows() < 1) + { + // echo '(debug) $session->validate_session: Key '.$key.' was not found in database
'; + return false; + } + $row = $db->fetchrow(); + $ip = ip2hex($_SERVER['REMOTE_ADDR']); + if($row['auth_level'] > $row['user_level']) + { + // Failed authorization check + // echo '(debug) $session->validate_session: user not authorized for this access level'; + return false; + } + if($ip != $row['source_ip']) + { + // Failed IP address check + // echo '(debug) $session->validate_session: IP address mismatch; IP in table: '.$row['source_ip'].'; reported IP: '.$ip.''; + return false; + } + + // Do the password validation + $real_key = md5($row['password'] . $row['salt']); + + //die('
'.print_r($keydata, true).'
'); + if($real_key != $key) + { + // Failed password check + // echo '(debug) $session->validate_session: supplied password is wrong
Real key: '.$real_key.'
User key: '.$key; + return false; + } + + $time_now = time(); + $time_key = $row['time'] + 900; + if($time_now > $time_key && $row['auth_level'] >= 1) + { + $this->sw_timed_out = true; + // Session timed out + // echo '(debug) $session->validate_session: super session timed out
'; + return false; + } + + return $row; + } + + /** + * Demotes us to one less than the specified auth level. AKA destroys elevated authentication and/or logs out the user, depending on $level + * @param int $level How low we should go - USER_LEVEL_MEMBER means demote to USER_LEVEL_GUEST, and anything more powerful than USER_LEVEL_MEMBER means demote to USER_LEVEL_MEMBER + * @return string 'success' if successful, or error on failure + */ + + function logout($level = USER_LEVEL_MEMBER) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $ou = $this->username; + $oid = $this->user_id; + if($level > USER_LEVEL_CHPREF) + { + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + if(!$this->user_logged_in || $this->auth_level < USER_LEVEL_MOD) return 'success'; + // Destroy elevated privileges + $keyhash = md5(strrev($this->sid_super)); + $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.$keyhash.'\' AND user_id=\'' . $this->user_id . '\';'); + $this->sid_super = false; + $this->auth_level = USER_LEVEL_MEMBER; + } + else + { + if($this->user_logged_in) + { + // Completely destroy our session + if($this->auth_level > USER_LEVEL_CHPREF) + { + $this->logout(USER_LEVEL_ADMIN); + } + $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.md5($this->sid).'\';'); + setcookie( 'sid', '', time()-(3600*24), scriptPath.'/' ); + } + } + $code = $plugins->setHook('logout_success'); // , Array('level'=>$level,'old_username'=>$ou,'old_user_id'=>$oid)); + foreach ( $code as $cmd ) + { + eval($cmd); + } + return 'success'; + } + + # Miscellaneous stuff + + /** + * Appends the high-privilege session key to the URL if we are authorized to do high-privilege stuff + * @param string $url The URL to add session data to + * @return string + */ + + function append_sid($url) + { + $sep = ( strstr($url, '?') ) ? '&' : '?'; + if ( $this->sid_super ) + { + $url = $url . $sep . 'auth=' . urlencode($this->sid_super); + // echo($this->sid_super.'
'); + } + return $url; + } + + /** + * Grabs the user's password MD5 + * @return string, or bool false if access denied + */ + + function grab_password_hash() + { + if(!$this->password_hash) return false; + return $this->password_hash; + } + + /** + * Destroys the user's password MD5 in memory + */ + + function disallow_password_grab() + { + $this->password_hash = false; + return false; + } + + /** + * Generates an AES key and stashes it in the database + * @return string Hex-encoded AES key + */ + + function rijndael_genkey() + { + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + $key = $aes->gen_readymade_key(); + $keys = getConfig('login_key_cache'); + if(is_string($keys)) + $keys .= $key; + else + $keys = $key; + setConfig('login_key_cache', $keys); + return $key; + } + + /** + * Generate a totally random 128-bit value for MD5 challenges + * @return string + */ + + function dss_rand() + { + $aes = new AESCrypt(); + $random = $aes->randkey(128); + unset($aes); + return md5(microtime() . $random); + } + + /** + * Fetch a cached login public key using the MD5sum as an identifier. Each key can only be fetched once before it is destroyed. + * @param string $md5 The MD5 sum of the key + * @return string, or bool false on failure + */ + + function fetch_public_key($md5) + { + $keys = getConfig('login_key_cache'); + $keys = enano_str_split($keys, AES_BITS / 4); + + foreach($keys as $i => $k) + { + if(md5($k) == $md5) + { + unset($keys[$i]); + if(count($keys) > 0) + { + if ( strlen(getConfig('login_key_cache') ) > 64000 ) + { + // This should only need to be done once every month or so for an average-size site + setConfig('login_key_cache', ''); + } + else + { + $keys = implode('', array_values($keys)); + setConfig('login_key_cache', $keys); + } + } + else + { + setConfig('login_key_cache', ''); + } + return $k; + } + } + // Couldn't find the key... + return false; + } + + /** + * Adds a user to a group. + * @param int User ID + * @param int Group ID + * @param bool Group moderator - defaults to false + * @return bool True on success, false on failure + */ + + function add_user_to_group($user_id, $group_id, $is_mod = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + // Validation + if ( !is_int($user_id) || !is_int($group_id) || !is_bool($is_mod) ) + return false; + if ( $user_id < 1 || $group_id < 1 ) + return false; + + $mod_switch = ( $is_mod ) ? '1' : '0'; + $q = $this->sql('SELECT member_id,is_mod FROM '.table_prefix.'group_members WHERE user_id=' . $user_id . ' AND group_id=' . $group_id . ';'); + if ( !$q ) + $db->_die(); + if ( $db->numrows() < 1 ) + { + // User is not in group + $this->sql('INSERT INTO '.table_prefix.'group_members(user_id,group_id,is_mod) VALUES(' . $user_id . ', ' . $group_id . ', ' . $mod_switch . ');'); + return true; + } + else + { + $row = $db->fetchrow(); + // Update modship status + if ( strval($row['is_mod']) == $mod_switch ) + { + // Modship unchanged + return true; + } + else + { + // Modship changed + $this->sql('UPDATE '.table_prefix.'group_members SET is_mod=' . $mod_switch . ' WHERE member_id=' . $row['member_id'] . ';'); + return true; + } + } + return false; + } + + /** + * Removes a user from a group. + * @param int User ID + * @param int Group ID + * @return bool True on success, false on failure + * @todo put a little more error checking in... + */ + + function remove_user_from_group($user_id, $group_id) + { + if ( !is_int($user_id) || !is_int($group_id) ) + return false; + $this->sql('DELETE FROM '.table_prefix."group_members WHERE user_id=$user_id AND group_id=$group_id;"); + return true; + } + + /** + * Checks the banlist to ensure that we're an allowed user. Doesn't return anything because it dies if the user is banned. + */ + + function check_banlist() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if($this->compat) + $q = $this->sql('SELECT ban_id,ban_type,ban_value,is_regex FROM '.table_prefix.'banlist ORDER BY ban_type;'); + else + $q = $this->sql('SELECT ban_id,ban_type,ban_value,is_regex,reason FROM '.table_prefix.'banlist ORDER BY ban_type;'); + if(!$q) $db->_die('The banlist data could not be selected.'); + $banned = false; + while($row = $db->fetchrow()) + { + if($this->compat) + $row['reason'] = 'None available - session manager is in compatibility mode'; + switch($row['ban_type']) + { + case BAN_IP: + if(intval($row['is_regex'])==1) { + if(preg_match('#'.$row['ban_value'].'#i', $_SERVER['REMOTE_ADDR'])) + { + $banned = true; + $reason = $row['reason']; + } + } + else { + if($row['ban_value']==$_SERVER['REMOTE_ADDR']) { $banned = true; $reason = $row['reason']; } + } + break; + case BAN_USER: + if(intval($row['is_regex'])==1) { + if(preg_match('#'.$row['ban_value'].'#i', $this->username)) + { + $banned = true; + $reason = $row['reason']; + } + } + else { + if($row['ban_value']==$this->username) { $banned = true; $reason = $row['reason']; } + } + break; + case BAN_EMAIL: + if(intval($row['is_regex'])==1) { + if(preg_match('#'.$row['ban_value'].'#i', $this->email)) + { + $banned = true; + $reason = $row['reason']; + } + } + else { + if($row['ban_value']==$this->email) { $banned = true; $reason = $row['reason']; } + } + break; + default: + die('Ban error: rule "'.$row['ban_value'].'" has an invalid type ('.$row['ban_type'].')'); + } + } + if($banned && $paths->get_pageid_from_url() != $paths->nslist['Special'].'CSS') + { + // This guy is banned - kill the session, kill the database connection, bail out, and be pretty about it + die_semicritical('Ban notice', '
You have been banned from this website. Please contact the site administrator for more information.

Reason:
'.$reason.'
'); + exit; + } + } + + # Registration + + /** + * Registers a user. This does not perform any type of login. + * @param string $username + * @param string $password This should be unencrypted. + * @param string $email + * @param string $real_name Optional, defaults to ''. + */ + + function create_user($username, $password, $email, $real_name = '') { + global $db, $session, $paths, $template, $plugins; // Common objects + + // Initialize AES + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + + if(!preg_match('#^'.$this->valid_username.'$#', $username)) return 'The username you chose contains invalid characters.'; + $username = $this->prepare_text($username); + $email = $this->prepare_text($email); + $real_name = $this->prepare_text($real_name); + $password = $aes->encrypt($password, $this->private_key, ENC_HEX); + + $nameclause = ( $real_name != '' ) ? ' OR real_name=\''.$real_name.'\'' : ''; + $q = $this->sql('SELECT * FROM '.table_prefix.'users WHERE lcase(username)=\''.strtolower($username).'\' OR email=\''.$email.'\''.$nameclause.';'); + if($db->numrows() > 0) { + $r = 'The '; + $i=0; + $row = $db->fetchrow(); + // Wow! An error checker that actually speaks English with the properest grammar! :-P + if($row['username'] == $username) { $r .= 'username'; $i++; } + if($row['email'] == $email) { if($i) $r.=', '; $r .= 'e-mail address'; $i++; } + if($row['real_name'] == $real_name && $real_name != '') { if($i) $r.=', and '; $r .= 'real name'; $i++; } + $r .= ' that you entered '; + $r .= ( $i == 1 ) ? 'is' : 'are'; + $r .= ' already in use by another user.'; + return $r; + } + + // Require the account to be activated? + switch(getConfig('account_activation')) + { + case 'none': + default: + $active = '1'; + break; + case 'user': + $active = '0'; + break; + case 'admin': + $active = '0'; + break; + } + + // Generate a totally random activation key + $actkey = sha1 ( microtime() . mt_rand() ); + + // We good, create the user + $this->sql('INSERT INTO '.table_prefix.'users ( username, password, email, real_name, theme, style, reg_time, account_active, activation_key, user_level ) VALUES ( \''.$username.'\', \''.$password.'\', \''.$email.'\', \''.$real_name.'\', \''.$template->default_theme.'\', \''.$template->default_style.'\', '.time().', '.$active.', \''.$actkey.'\', '.USER_LEVEL_CHPREF.' )'); + + // Require the account to be activated? + switch(getConfig('account_activation')) + { + case 'none': + default: + break; + case 'user': + $a = $this->send_activation_mail($username); + if(!$a) + { + $this->admin_activation_request($username); + return 'The activation e-mail could not be sent due to an internal error. This could possibly be due to an incorrect SMTP configuration. A request has been sent to the administrator to activate your account for you. ' . $a; + } + break; + case 'admin': + $this->admin_activation_request($username); + break; + } + + // Leave some data behind for the hook + $code = $plugins->setHook('user_registered'); // , Array('username'=>$username)); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + // $this->register_session($username, $password); + return 'success'; + } + + /** + * Attempts to send an e-mail to the specified user with activation instructions. + * @param string $u The usernamd of the user requesting activation + * @return bool true on success, false on failure + */ + + function send_activation_mail($u, $actkey = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $q = $this->sql('SELECT username,email FROM '.table_prefix.'users WHERE user_id=1 OR user_level=' . USER_LEVEL_ADMIN . ' ORDER BY user_id ASC;'); + $un = $db->fetchrow(); + $admin_user = $un['username']; + $q = $this->sql('SELECT username,activation_key,account_active,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($u).'\';'); + $r = $db->fetchrow(); + if ( empty($r['email']) ) + $db->_die('BUG: $session->send_activation_mail(): no e-mail address in row'); + $message = 'Dear '.$u.', +Thank you for registering on '.getConfig('site_name').'. Your account creation is almost complete. To complete the registration process, please click the following link or paste it into your web browser: + +'; + if(isset($_SERVER['HTTPS'])) $prot = 'https'; + else $prot = 'http'; + if($_SERVER['SERVER_PORT'] == '80') $p = ''; + else $p = ':'.$_SERVER['SERVER_PORT']; + $sidbak = false; + if($this->sid_super) + $sidbak = $this->sid_super; + $this->sid_super = false; + $aklink = makeUrlNS('Special', 'ActivateAccount/'.str_replace(' ', '_', $u).'/'. ( ( is_string($actkey) ) ? $actkey : $r['activation_key'] ) ); + if($sidbak) + $this->sid_super = $sidbak; + unset($sidbak); + $message .= "$prot://".$_SERVER['HTTP_HOST'].$p.$aklink; + $message .= "\n\nSincerely yours, \n$admin_user and the ".$_SERVER['HTTP_HOST']." administration team"; + error_reporting(E_ALL); + dc_dump($r, 'session: about to send activation e-mail to '.$r['email']); + if(getConfig('smtp_enabled') == '1') + { + $result = smtp_send_email($r['email'], getConfig('site_name').' website account activation', preg_replace("#(?sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE user_id='.$user.';'); // This is SAFE! This is only called if $user is an integer + } + elseif(is_string($user)) + { + $q = $this->sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($user).'\';'); + } + else + { + return false; + } + + $row = $db->fetchrow(); + $temp_pass = $this->random_pass(); + + $this->register_temp_password($row['user_id'], $temp_pass); + + $site_name = getConfig('site_name'); + + $message = "Dear {$row['username']}, + +Someone (hopefully you) on the {$site_name} website requested that a new password be created. + +The request was sent from the IP address {$_SERVER['REMOTE_ADDR']}. + +If you did not request the new password, then you do not need to do anything; the password will be invalidated after 24 hours. + +If you did request this password, then please log in using the password shown below: + + Password: {$temp_pass} + +After you log in using this password, you will be able to reset your real password. You can only log in using this temporary password once. + +Sincerely yours, +The {$site_name} administration team +"; + + if(getConfig('smtp_enabled') == '1') + { + $result = smtp_send_email($row['email'], getConfig('site_name').' password reset', preg_replace("#(?'.$result.'

'; + $result = false; + } + } else { + $result = mail($row['email'], getConfig('site_name').' password reset', preg_replace("#(?encrypt($password, $this->private_key, ENC_HEX); + $this->sql('UPDATE '.table_prefix.'users SET temp_password=\'' . $temp_pass . '\',temp_password_time='.time().' WHERE user_id='.intval($user_id).';'); + } + + /** + * Sends a request to the admin panel to have the username $u activated. + * @param string $u The username of the user requesting activation + */ + + function admin_activation_request($u) + { + global $db; + $this->sql('INSERT INTO '.table_prefix.'logs(log_type, action, time_id, date_string, author, edit_summary) VALUES(\'admin\', \'activ_req\', '.time().', \''.date('d M Y h:i a').'\', \''.$this->username.'\', \''.$db->escape($u).'\');'); + } + + /** + * Activates a user account. If the action fails, a report is sent to the admin. + * @param string $user The username of the user requesting activation + * @param string $key The activation key + */ + + function activate_account($user, $key) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $this->sql('UPDATE '.table_prefix.'users SET account_active=1 WHERE username=\''.$db->escape($user).'\' AND activation_key=\''.$db->escape($key).'\';'); + $r = mysql_affected_rows(); + if ( $r > 0 ) + { + $e = $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'activ_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($user).'\', \''.$_SERVER['REMOTE_ADDR'].'\')'); + } + else + { + $e = $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'activ_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($user).'\', \''.$_SERVER['REMOTE_ADDR'].'\')'); + } + return $r; + } + + /** + * For a given user level identifier (USER_LEVEL_*), returns a string describing that user level. + * @param int User level + * @return string + */ + + function userlevel_to_string($user_level) + { + switch ( $user_level ) + { + case USER_LEVEL_GUEST: + return 'Low - guest privileges'; + case USER_LEVEL_MEMBER: + return 'Standard - normal member level'; + case USER_LEVEL_CHPREF: + return 'Medium - user can change his/her own e-mail address and password'; + case USER_LEVEL_MOD: + return 'High - moderator privileges'; + case USER_LEVEL_ADMIN: + return 'Highest - administrative privileges'; + default: + return "Unknown ($user_level)"; + } + } + + /** + * Updates a user's information in the database. Note that any of the values except $user_id can be false if you want to preserve the old values. + * @param int $user_id The user ID of the user to update - this cannot be changed + * @param string $username The new username + * @param string $old_pass The current password - only required if sessionManager::$user_level < USER_LEVEL_ADMIN. This should usually be an UNENCRYPTED string. This can also be an array - if it is, key 0 is treated as data AES-encrypted with key 1 + * @param string $password The new password + * @param string $email The new e-mail address + * @param string $realname The new real name + * @param string $signature The updated forum/comment signature + * @param int $user_level The updated user level + * @return string 'success' if successful, or array of error strings on failure + */ + + function update_user($user_id, $username = false, $old_pass = false, $password = false, $email = false, $realname = false, $signature = false, $user_level = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + // Create some arrays + + $errors = Array(); // Used to hold error strings + $strs = Array(); // Sub-query statements + + // Scan the user ID for problems + if(intval($user_id) < 1) $errors[] = 'SQL injection attempt'; + + // Instanciate the AES encryption class + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + + // If all of our input vars are false, then we've effectively done our job so get out of here + if($username === false && $password === false && $email === false && $realname === false && $signature === false && $user_level === false) + { + // echo 'debug: $session->update_user(): success (no changes requested)'; + return 'success'; + } + + // Initialize our authentication check + $authed = false; + + // Verify the inputted password + if(is_string($old_pass)) + { + $q = $this->sql('SELECT password FROM '.table_prefix.'users WHERE user_id='.$user_id.';'); + if($db->numrows() < 1) + { + $errors[] = 'The password data could not be selected for verification.'; + } + else + { + $row = $db->fetchrow(); + $real = $aes->decrypt($row['password'], $this->private_key, ENC_HEX); + if($real == $old_pass) + $authed = true; + } + } + + elseif(is_array($old_pass)) + { + $old_pass = $aes->decrypt($old_pass[0], $old_pass[1]); + $q = $this->sql('SELECT password FROM '.table_prefix.'users WHERE user_id='.$user_id.';'); + if($db->numrows() < 1) + { + $errors[] = 'The password data could not be selected for verification.'; + } + else + { + $row = $db->fetchrow(); + $real = $aes->decrypt($row['password'], $this->private_key, ENC_HEX); + if($real == $old_pass) + $authed = true; + } + } + + // Initialize our query + $q = 'UPDATE '.table_prefix.'users SET '; + + if($this->auth_level >= USER_LEVEL_ADMIN || $authed) // Need the current password in order to update the e-mail address, change the username, or reset the password + { + // Username + if(is_string($username)) + { + // Check the username for problems + if(!preg_match('#^'.$this->valid_username.'$#', $username)) + $errors[] = 'The username you entered contains invalid characters.'; + $strs[] = 'username=\''.$db->escape($username).'\''; + } + // Password + if(is_string($password) && strlen($password) >= 6) + { + // Password needs to be encrypted before being stashed + $encpass = $aes->encrypt($password, $this->private_key, ENC_HEX); + if(!$encpass) + $errors[] = 'The password could not be encrypted due to an internal error.'; + $strs[] = 'password=\''.$encpass.'\''; + } + // E-mail addy + if(is_string($email)) + { + // I didn't write this regex. + if(!preg_match('/^(?:[\w\d]+\.?)+@(?:(?:[\w\d]\-?)+\.)+\w{2,4}$/', $email)) + $errors[] = 'The e-mail address you entered is invalid.'; + $strs[] = 'email=\''.$db->escape($email).'\''; + } + } + // Real name + if(is_string($realname)) + { + $strs[] = 'real_name=\''.$db->escape($realname).'\''; + } + // Forum/comment signature + if(is_string($signature)) + { + $strs[] = 'signature=\''.$db->escape($signature).'\''; + } + // User level + if(is_int($user_level)) + { + $strs[] = 'user_level='.$user_level; + } + + // Add our generated query to the query string + $q .= implode(',', $strs); + + // One last error check + if(sizeof($strs) < 1) $errors[] = 'An internal error occured building the SQL query, this is a bug'; + if(sizeof($errors) > 0) return $errors; + + // Free our temp arrays + unset($strs, $errors); + + // Finalize the query and run it + $q .= ' WHERE user_id='.$user_id.';'; + $this->sql($q); + + // We also need to trigger re-activation. + if ( is_string($email) ) + { + switch(getConfig('account_activation')) + { + case 'user': + case 'admin': + + if ( $session->user_level >= USER_LEVEL_MOD && getConfig('account_activation') == 'admin' ) + // Don't require re-activation by admins for admins + break; + + // retrieve username + if ( !$username ) + { + $q = $this->sql('SELECT username FROM '.table_prefix.'users WHERE user_id='.$user_id.';'); + if($db->numrows() < 1) + { + $errors[] = 'The username could not be selected.'; + } + else + { + $row = $db->fetchrow(); + $username = $row['username']; + } + } + if ( !$username ) + return $errors; + + // Generate a totally random activation key + $actkey = sha1 ( microtime() . mt_rand() ); + $a = $this->send_activation_mail($username, $actkey); + if(!$a) + { + $this->admin_activation_request($username); + } + // Deactivate the account until e-mail is confirmed + $q = $db->sql_query('UPDATE '.table_prefix.'users SET account_active=0,activation_key=\'' . $actkey . '\' WHERE user_id=' . $user_id . ';'); + break; + } + } + + // Yay! We're done + return 'success'; + } + + # + # Access Control Lists + # + + /** + * Creates a new permission field in memory. If the permissions are set in the database, they are used. Otherwise, $default_perm is used. + * @param string $acl_type An identifier for this field + * @param int $default_perm Whether permission should be granted or not if it's not specified in the ACLs. + * @param string $desc A human readable name for the permission type + * @param array $deps The list of dependencies - this should be an array of ACL types + * @param string $scope Which namespaces this field should apply to. This should be either a pipe-delimited list of namespace IDs or just "All". + */ + + function register_acl_type($acl_type, $default_perm = AUTH_DISALLOW, $desc = false, $deps = Array(), $scope = 'All') + { + if(isset($this->acl_types[$acl_type])) + return false; + else + { + if(!$desc) + { + $desc = capitalize_first_letter(str_replace('_', ' ', $acl_type)); + } + $this->acl_types[$acl_type] = $default_perm; + $this->acl_descs[$acl_type] = $desc; + $this->acl_deps[$acl_type] = $deps; + $this->acl_scope[$acl_type] = explode('|', $scope); + } + return true; + } + + /** + * Tells us whether permission $type is allowed or not based on the current rules. + * @param string $type The permission identifier ($acl_type passed to sessionManager::register_acl_type()) + * @param bool $no_deps If true, disables dependency checking + * @return bool True if allowed, false if denied or if an error occured + */ + + function get_permissions($type, $no_deps = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if ( isset( $this->perms[$type] ) ) + { + if ( $this->perms[$type] == AUTH_DENY ) + $ret = false; + else if ( $this->perms[$type] == AUTH_WIKIMODE && $paths->wiki_mode ) + $ret = true; + else if ( $this->perms[$type] == AUTH_WIKIMODE && !$paths->wiki_mode ) + $ret = false; + else if ( $this->perms[$type] == AUTH_ALLOW ) + $ret = true; + else if ( $this->perms[$type] == AUTH_DISALLOW ) + $ret = false; + } + else if(isset($this->acl_types[$type])) + { + if ( $this->acl_types[$type] == AUTH_DENY ) + $ret = false; + else if ( $this->acl_types[$type] == AUTH_WIKIMODE && $paths->wiki_mode ) + $ret = true; + else if ( $this->acl_types[$type] == AUTH_WIKIMODE && !$paths->wiki_mode ) + $ret = false; + else if ( $this->acl_types[$type] == AUTH_ALLOW ) + $ret = true; + else if ( $this->acl_types[$type] == AUTH_DISALLOW ) + $ret = false; + } + else + { + // ACL type is undefined + trigger_error('Unknown access type "' . $type . '"', E_USER_WARNING); + return false; // Be on the safe side and deny access + } + if ( !$no_deps ) + { + if ( !$this->acl_check_deps($type) ) + return false; + } + return $ret; + } + + /** + * Fetch the permissions that apply to the current user for the page specified. The object you get will have the get_permissions method + * and several other abilities. + * @param string $page_id + * @param string $namespace + * @return object + */ + + function fetch_page_acl($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( count ( $this->acl_base_cache ) < 1 ) + { + // Permissions table not yet initialized + return false; + } + + //if ( !isset( $paths->pages[$paths->nslist[$namespace] . $page_id] ) ) + //{ + // // Page does not exist + // return false; + //} + + $object = new Session_ACLPageInfo( $page_id, $namespace, $this->acl_types, $this->acl_descs, $this->acl_deps, $this->acl_base_cache ); + + return $object; + + } + + /** + * Read all of our permissions from the database and process/apply them. This should be called after the page is determined. + * @access private + */ + + function init_permissions() + { + global $db, $session, $paths, $template, $plugins; // Common objects + // Initialize the permissions list with some defaults + $this->perms = $this->acl_types; + $this->acl_defaults_used = $this->perms; + + // Fetch sitewide defaults from the permissions table + $bs = 'SELECT rules FROM '.table_prefix.'acl WHERE page_id IS NULL AND namespace IS NULL AND ( '; + + $q = Array(); + $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )'; + if(count($this->groups) > 0) + { + foreach($this->groups as $g_id => $g_name) + { + $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )'; + } + } + $bs .= implode(' OR ', $q) . ' ) ORDER BY target_type ASC, target_id ASC;'; + $q = $this->sql($bs); + if ( $row = $db->fetchrow() ) + { + do { + $rules = $this->string_to_perm($row['rules']); + $is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 ); + $this->acl_merge_with_current($rules, $is_everyone); + } while ( $row = $db->fetchrow() ); + } + + // Eliminate types that don't apply to this namespace + foreach ( $this->perms AS $i => $perm ) + { + if ( !in_array ( $paths->namespace, $this->acl_scope[$i] ) && !in_array('All', $this->acl_scope[$i]) ) + { + unset($this->perms[$i]); + } + } + + // Cache the sitewide permissions for later use + $this->acl_base_cache = $this->perms; + + // Build a query to grab ACL info + $bs = 'SELECT rules,target_type,target_id FROM '.table_prefix.'acl WHERE ( '; + $q = Array(); + $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )'; + if(count($this->groups) > 0) + { + foreach($this->groups as $g_id => $g_name) + { + $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )'; + } + } + // The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual + // permissions to override group permissions. + $bs .= implode(' OR ', $q) . ' ) AND ( page_id=\''.$db->escape($paths->cpage['urlname_nons']).'\' AND namespace=\''.$db->escape($paths->namespace).'\' ) + ORDER BY target_type ASC, page_id ASC, namespace ASC;'; + $q = $this->sql($bs); + if ( $row = $db->fetchrow() ) + { + do { + $rules = $this->string_to_perm($row['rules']); + $is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 ); + $this->acl_merge_with_current($rules, $is_everyone); + } while ( $row = $db->fetchrow() ); + } + + } + + /** + * Extends the scope of a permission type. + * @param string The name of the permission type + * @param string The namespace(s) that should be covered. This can be either one namespace ID or a pipe-delimited list. + * @param object Optional - the current $paths object, in case we're doing this from the acl_rule_init hook + */ + + function acl_extend_scope($perm_type, $namespaces, &$p_in) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $p_obj = ( is_object($p_in) ) ? $p_in : $paths; + $nslist = explode('|', $namespaces); + foreach ( $nslist as $i => $ns ) + { + if ( !isset($p_obj->nslist[$ns]) ) + { + unset($nslist[$i]); + } + else + { + $this->acl_scope[$perm_type][] = $ns; + if ( isset($this->acl_types[$perm_type]) && !isset($this->perms[$perm_type]) ) + { + $this->perms[$perm_type] = $this->acl_types[$perm_type]; + } + } + } + } + + /** + * Converts a permissions field into a string for database insertion. Similar in spirit to serialize(). + * @param array $perms An associative array with only integers as values + * @return string + */ + + function perm_to_string($perms) + { + $s = ''; + foreach($perms as $perm => $ac) + { + $s .= "$perm=$ac;"; + } + return $s; + } + + /** + * Converts a permissions string back to an array. + * @param string $perms The result from sessionManager::perm_to_string() + * @return array + */ + + function string_to_perm($perms) + { + $ret = Array(); + preg_match_all('#([a-z0-9_-]+)=([0-9]+);#i', $perms, $matches); + foreach($matches[1] as $i => $t) + { + $ret[$t] = intval($matches[2][$i]); + } + return $ret; + } + + /** + * Merges two ACL arrays. Both parameters should be permission list arrays. The second group takes precedence over the first, but AUTH_DENY always prevails. + * @param array $perm1 The first set of permissions + * @param array $perm2 The second set of permissions + * @return array + */ + + function acl_merge($perm1, $perm2) + { + $ret = $perm1; + foreach ( $perm2 as $type => $level ) + { + if ( isset( $ret[$type] ) ) + { + if ( $ret[$type] != AUTH_DENY ) + $ret[$type] = $level; + } + // else + // { + // $ret[$type] = $level; + // } + } + return $ret; + } + + /** + * Merges the ACL array sent with the current permissions table, deciding precedence based on whether defaults are in effect or not. + * @param array The array to merge into the master ACL list + * @param bool If true, $perm is treated as the "new default" + * @param int 1 if this is a site-wide ACL, 2 if page-specific. Defaults to 2. + */ + + function acl_merge_with_current($perm, $is_everyone = false, $scope = 2) + { + foreach ( $this->perms as $i => $p ) + { + if ( isset($perm[$i]) ) + { + if ( $is_everyone && !$this->acl_defaults_used[$i] ) + continue; + // Decide precedence + if ( isset($this->acl_defaults_used[$i]) ) + { + //echo "$i: default in use, overriding to: {$perm[$i]}
"; + // Defaults are in use, override + $this->perms[$i] = $perm[$i]; + $this->acl_defaults_used[$i] = ( $is_everyone ); + } + else + { + //echo "$i: default NOT in use"; + // Defaults are not in use, merge as normal + if ( $this->perms[$i] != AUTH_DENY ) + { + //echo ", but overriding"; + $this->perms[$i] = $perm[$i]; + } + //echo "
"; + } + } + } + } + + /** + * Merges two ACL arrays. Both parameters should be permission list arrays. The second group takes precedence + * over the first, without exceptions. This is used to merge the hardcoded defaults with admin-specified + * defaults, which take precedence. + * @param array $perm1 The first set of permissions + * @param array $perm2 The second set of permissions + * @return array + */ + + function acl_merge_complete($perm1, $perm2) + { + $ret = $perm1; + foreach ( $perm2 as $type => $level ) + { + $ret[$type] = $level; + } + return $ret; + } + + /** + * Tell us if the dependencies for a given permission are met. + * @param string The ACL permission ID + * @return bool + */ + + function acl_check_deps($type) + { + if(!isset($this->acl_deps[$type])) // This will only happen if the permissions table is hacked or improperly accessed + return true; + if(sizeof($this->acl_deps[$type]) < 1) + return true; + $deps = $this->acl_deps[$type]; + while(true) + { + $full_resolved = true; + $j = sizeof($deps); + for ( $i = 0; $i < $j; $i++ ) + { + $b = $deps; + $deps = array_merge($deps, $this->acl_deps[$deps[$i]]); + if( $b == $deps ) + { + break 2; + } + $j = sizeof($deps); + } + } + //die('
'.print_r($deps, true).'
'); + foreach($deps as $d) + { + if ( !$this->get_permissions($d) ) + { + return false; + } + } + return true; + } + + /** + * Makes a CAPTCHA code and caches the code in the database + * @param int $len The length of the code, in bytes + * @return string A unique identifier assigned to the code. This hash should be passed to sessionManager::getCaptcha() to retrieve the code. + */ + + function make_captcha($len = 7) + { + $chars = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9'); + $s = ''; + for($i=0;$i<$len;$i++) $s .= $chars[mt_rand(0, count($chars)-1)]; + $hash = md5(microtime() . mt_rand()); + $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key,salt,auth_level,source_ip,user_id) VALUES(\''.$hash.'\', \''.$s.'\', -1, \''.ip2hex($_SERVER['REMOTE_ADDR']).'\', -2);'); + return $hash; + } + + /** + * For the given code ID, returns the correct CAPTCHA code, or false on failure + * @param string $hash The unique ID assigned to the code + * @return string The correct confirmation code + */ + + function get_captcha($hash) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $s = $this->sql('SELECT salt FROM '.table_prefix.'session_keys WHERE session_key=\''.$db->escape($hash).'\' AND source_ip=\''.ip2hex($_SERVER['REMOTE_ADDR']).'\';'); + if($db->numrows() < 1) return false; + $r = $db->fetchrow(); + return $r['salt']; + } + + /** + * Deletes all CAPTCHA codes cached in the DB for this user. + */ + + function kill_captcha() + { + $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE user_id=-2 AND source_ip=\''.ip2hex($_SERVER['REMOTE_ADDR']).'\';'); + } + + /** + * Generates a random password. + * @param int $length Optional - length of password + * @return string + */ + + function random_pass($length = 10) + { + $valid_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_+@#%&<>'; + $valid_chars = enano_str_split($valid_chars); + $ret = ''; + for ( $i = 0; $i < $length; $i++ ) + { + $ret .= $valid_chars[mt_rand(0, count($valid_chars)-1)]; + } + return $ret; + } + + /** + * Generates some Javascript that calls the AES encryption library. + * @param string The name of the form + * @param string The name of the password field + * @param string The name of the field that switches encryption on or off + * @param string The name of the field that contains the encryption key + * @param string The name of the field that will contain the encrypted password + * @param string The name of the field that handles MD5 challenge data + * @return string + */ + + function aes_javascript($form_name, $pw_field, $use_crypt, $crypt_key, $crypt_data, $challenge) + { + $code = ' + + '; + return $code; + } + +} + +/** + * Class used to fetch permissions for a specific page. Used internally by SessionManager. + * @package Enano + * @subpackage Session manager + * @license http://www.gnu.org/copyleft/gpl.html + * @access private + */ + +class Session_ACLPageInfo { + + /** + * The page ID of this ACL info package + * @var string + */ + + var $page_id; + + /** + * The namespace of the page being checked + * @var string + */ + + var $namespace; + + /** + * Our list of permission types. + * @access private + * @var array + */ + + var $acl_types = Array(); + + /** + * The list of descriptions for the permission types + * @var array + */ + + var $acl_descs = Array(); + + /** + * A list of dependencies for ACL types. + * @var array + */ + + var $acl_deps = Array(); + + /** + * Our tell-all list of permissions. + * @access private - or, preferably, protected...too bad this has to be PHP4 compatible + * @var array + */ + + var $perms = Array(); + + /** + * Constructor. + * @param string $page_id The ID of the page to check + * @param string $namespace The namespace of the page to check. + * @param array $acl_types List of ACL types + * @param array $acl_descs List of human-readable descriptions for permissions (associative) + * @param array $acl_deps List of dependencies for permissions. For example, viewing history/diffs depends on the ability to read the page. + * @param array $base What to start with - this is an attempt to reduce the number of SQL queries. + */ + + function Session_ACLPageInfo($page_id, $namespace, $acl_types, $acl_descs, $acl_deps, $base) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $this->perms = $session->acl_merge_complete($acl_types, $base); + $this->acl_deps = $acl_deps; + $this->acl_types = $acl_types; + $this->acl_descs = $acl_descs; + + // Build a query to grab ACL info + $bs = 'SELECT rules FROM '.table_prefix.'acl WHERE ( '; + $q = Array(); + $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$session->user_id.' )'; + if(count($session->groups) > 0) + { + foreach($session->groups as $g_id => $g_name) + { + $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )'; + } + } + // The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual + // permissions to override group permissions. + $bs .= implode(' OR ', $q) . ' ) AND ( page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\' ) + ORDER BY target_type ASC, page_id ASC, namespace ASC;'; + $q = $session->sql($bs); + if ( $row = $db->fetchrow() ) + { + do { + $rules = $session->string_to_perm($row['rules']); + $this->perms = $session->acl_merge($this->perms, $rules); + } while ( $row = $db->fetchrow() ); + } + + $this->page_id = $page_id; + $this->namespace = $namespace; + } + + /** + * Tells us whether permission $type is allowed or not based on the current rules. + * @param string $type The permission identifier ($acl_type passed to sessionManager::register_acl_type()) + * @param bool $no_deps If true, disables dependency checking + * @return bool True if allowed, false if denied or if an error occured + */ + + function get_permissions($type, $no_deps = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if ( isset( $this->perms[$type] ) ) + { + if ( $this->perms[$type] == AUTH_DENY ) + $ret = false; + else if ( $this->perms[$type] == AUTH_WIKIMODE && + ( isset($paths->pages[$paths->nslist[$this->namespace].$this->page_id]) && + ( $paths->pages[$paths->nslist[$this->namespace].$this->page_id]['wiki_mode'] == '1' || + ( $paths->pages[$paths->nslist[$this->namespace].$this->page_id]['wiki_mode'] == '2' + && getConfig('wiki_mode') == '1' + ) ) ) ) + $ret = true; + else if ( $this->perms[$type] == AUTH_WIKIMODE && ( + !isset($paths->pages[$paths->nslist[$this->namespace].$this->page_id]) + || ( + isset($paths->pages[$paths->nslist[$this->namespace].$this->page_id]) && ( + $paths->pages[$paths->nslist[$this->namespace].$this->page_id]['wiki_mode'] == '0' + || ( + $paths->pages[$paths->nslist[$this->namespace].$this->page_id]['wiki_mode'] == '2' && getConfig('wiki_mode') != '1' + ) ) ) ) ) + $ret = false; + else if ( $this->perms[$type] == AUTH_ALLOW ) + $ret = true; + else if ( $this->perms[$type] == AUTH_DISALLOW ) + $ret = false; + } + else if(isset($this->acl_types[$type])) + { + if ( $this->acl_types[$type] == AUTH_DENY ) + $ret = false; + else if ( $this->acl_types[$type] == AUTH_WIKIMODE && $paths->wiki_mode ) + $ret = true; + else if ( $this->acl_types[$type] == AUTH_WIKIMODE && !$paths->wiki_mode ) + $ret = false; + else if ( $this->acl_types[$type] == AUTH_ALLOW ) + $ret = true; + else if ( $this->acl_types[$type] == AUTH_DISALLOW ) + $ret = false; + } + else + { + // ACL type is undefined + trigger_error('Unknown access type "' . $type . '"', E_USER_WARNING); + return false; // Be on the safe side and deny access + } + if ( !$no_deps ) + { + if ( !$this->acl_check_deps($type) ) + return false; + } + return $ret; + } + + /** + * Tell us if the dependencies for a given permission are met. + * @param string The ACL permission ID + * @return bool + */ + + function acl_check_deps($type) + { + if(!isset($this->acl_deps[$type])) // This will only happen if the permissions table is hacked or improperly accessed + return true; + if(sizeof($this->acl_deps[$type]) < 1) + return true; + $deps = $this->acl_deps[$type]; + while(true) + { + $full_resolved = true; + $j = sizeof($deps); + for ( $i = 0; $i < $j; $i++ ) + { + $b = $deps; + $deps = array_merge($deps, $this->acl_deps[$deps[$i]]); + if( $b == $deps ) + { + break 2; + } + $j = sizeof($deps); + } + } + //die('
'.print_r($deps, true).'
'); + foreach($deps as $d) + { + if ( !$this->get_permissions($d) ) + { + return false; + } + } + return true; + } + +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/sessions.php~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/sessions.php~ Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,2425 @@ +escape($str); } + +/** + * Anything and everything related to security and user management. This includes AES encryption, which is illegal in some countries. + * Documenting the API was not easy - I hope you folks enjoy it. + * @package Enano + * @subpackage Session manager + * @category security, user management, logins, etc. + */ + +class sessionManager { + + # Variables + + /** + * Whether we're logged in or not + * @var bool + */ + + var $user_logged_in = false; + + /** + * Our current low-privilege session key + * @var string + */ + + var $sid; + + /** + * Username of currently logged-in user, or IP address if not logged in + * @var string + */ + + var $username; + + /** + * User ID of currently logged-in user, or -1 if not logged in + * @var int + */ + + var $user_id; + + /** + * Real name of currently logged-in user, or blank if not logged in + * @var string + */ + + var $real_name; + + /** + * E-mail address of currently logged-in user, or blank if not logged in + * @var string + */ + + var $email; + + /** + * User level of current user + * USER_LEVEL_GUEST: guest + * USER_LEVEL_MEMBER: regular user + * USER_LEVEL_CHPREF: default - pseudo-level that allows changing password and e-mail address (requires re-authentication) + * USER_LEVEL_MOD: moderator + * USER_LEVEL_ADMIN: administrator + * @var int + */ + + var $user_level; + + /** + * High-privilege session key + * @var string or false if not running on high-level authentication + */ + + var $sid_super; + + /** + * The user's theme preference, defaults to $template->default_theme + * @var string + */ + + var $theme; + + /** + * The user's style preference, or style auto-detected based on theme if not logged in + * @var string + */ + + var $style; + + /** + * Signature of current user - appended to comments, etc. + * @var string + */ + + var $signature; + + /** + * UNIX timestamp of when we were registered, or 0 if not logged in + * @var int + */ + + var $reg_time; + + /** + * MD5 hash of the current user's password, if applicable + * @var string OR bool false + */ + + var $password_hash; + + /** + * The number of unread private messages this user has. + * @var int + */ + + var $unread_pms = 0; + + /** + * AES key used to encrypt passwords and session key info - irreversibly destroyed when disallow_password_grab() is called + * @var string + */ + + var $private_key; + + /** + * Regex that defines a valid username, minus the ^ and $, these are added later + * @var string + */ + + var $valid_username = '([A-Za-z0-9 \!\@\(\)-]+)'; + + /** + * What we're allowed to do as far as permissions go. This changes based on the value of the "auth" URI param. + * @var string + */ + + var $auth_level = -1; + + /** + * State variable to track if a session timed out + * @var bool + */ + + var $sw_timed_out = false; + + /** + * Switch to track if we're started or not. + * @access private + * @var bool + */ + + var $started = false; + + /** + * Switch to control compatibility mode (for older Enano websites being upgraded) + * @access private + * @var bool + */ + + var $compat = false; + + /** + * Our list of permission types. + * @access private + * @var array + */ + + var $acl_types = Array(); + + /** + * The list of descriptions for the permission types + * @var array + */ + + var $acl_descs = Array(); + + /** + * A list of dependencies for ACL types. + * @var array + */ + + var $acl_deps = Array(); + + /** + * Our tell-all list of permissions. + * @access private - or, preferably, protected + * @var array + */ + + var $perms = Array(); + + /** + * A cache variable - saved after sitewide permissions are checked but before page-specific permissions. + * @var array + * @access private + */ + + var $acl_base_cache = Array(); + + /** + * Stores the scope information for ACL types. + * @var array + * @access private + */ + + var $acl_scope = Array(); + + /** + * Array to track which default permissions are being used + * @var array + * @access private + */ + + var $acl_defaults_used = Array(); + + /** + * Array to track group membership. + * @var array + */ + + var $groups = Array(); + + /** + * Associative array to track group modship. + * @var array + */ + + var $group_mod = Array(); + + # Basic functions + + /** + * Constructor. + */ + + function __construct() + { + global $db, $session, $paths, $template, $plugins; // Common objects + include(ENANO_ROOT.'/config.php'); + unset($dbhost, $dbname, $dbuser, $dbpasswd); + if(isset($crypto_key)) + { + $this->private_key = $crypto_key; + $this->private_key = hexdecode($this->private_key); + } + else + { + if(is_writable(ENANO_ROOT.'/config.php')) + { + // Generate and stash a private key + // This should only happen during an automated silent gradual migration to the new encryption platform. + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + $this->private_key = $aes->gen_readymade_key(); + + $config = file_get_contents(ENANO_ROOT.'/config.php'); + if(!$config) + { + die('$session->__construct(): can\'t get the contents of config.php'); + } + + $config = str_replace("?>", "\$crypto_key = '{$this->private_key}';\n?>", $config); + // And while we're at it... + $config = str_replace('MIDGET_INSTALLED', 'ENANO_INSTALLED', $config); + $fh = @fopen(ENANO_ROOT.'/config.php', 'w'); + if ( !$fh ) + { + die('$session->__construct(): Couldn\'t open config file for writing to store the private key, I tried to avoid something like this...'); + } + + fwrite($fh, $config); + fclose($fh); + } + else + { + die_semicritical('Crypto error', '

No private key was found in the config file, and we can\'t generate one because we don\'t have write access to the config file. Please CHMOD config.php to 666 or 777 and reload this page.

'); + } + } + // Check for compatibility mode + if(defined('IN_ENANO_INSTALL')) + { + $q = $db->sql_query('SELECT old_encryption FROM '.table_prefix.'users LIMIT 1;'); + if(!$q) + { + $error = mysql_error(); + if(strstr($error, "Unknown column 'old_encryption'")) + $this->compat = true; + else + $db->_die('This should never happen and is a bug - the only error that was supposed to happen here didn\'t happen. (sessions.php in constructor, during compat mode check)'); + } + $db->free_result(); + } + } + + /** + * PHP 4 compatible constructor. + */ + + function sessionManager() + { + $this->__construct(); + } + + /** + * Wrapper function to sanitize strings for MySQL and HTML + * @param string $text The text to sanitize + * @return string + */ + + function prepare_text($text) + { + global $db; + return $db->escape(htmlspecialchars($text)); + } + + /** + * Makes a SQL query and handles error checking + * @param string $query The SQL query to make + * @return resource + */ + + function sql($query) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $result = $db->sql_query($query); + if(!$result) + { + $db->_die('The error seems to have occurred somewhere in the session management code.'); + } + return $result; + } + + # Session restoration and permissions + + /** + * Initializes the basic state of things, including most user prefs, login data, cookie stuff + */ + + function start() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if($this->started) return; + $this->started = true; + $user = false; + if(isset($_COOKIE['sid'])) + { + if($this->compat) + { + $userdata = $this->compat_validate_session($_COOKIE['sid']); + } + else + { + $userdata = $this->validate_session($_COOKIE['sid']); + } + if(is_array($userdata)) + { + $data = RenderMan::strToPageID($paths->get_pageid_from_url()); + + if(!$this->compat && $userdata['account_active'] != 1 && $data[1] != 'Special' && $data[1] != 'Admin') + { + $this->logout(); + $a = getConfig('account_activation'); + switch($a) + { + case 'none': + default: + $solution = 'Your account was most likely deactivated by an administrator. Please contact the site administration for further assistance.'; + break; + case 'user': + $solution = 'Please check your e-mail; you should have been sent a message with instructions on how to activate your account. If you do not receive an e-mail from this site within 24 hours, please contact the site administration for further assistance.'; + break; + case 'admin': + $solution = 'This website has been configured so that all user accounts must be activated by the administrator before they can be used, so your account will most likely be activated the next time the one of the administrators visits the site.'; + break; + } + die_semicritical('Account error', '

It appears that your user account has not yet been activated. '.$solution.'

'); + } + + $this->sid = $_COOKIE['sid']; + $this->user_logged_in = true; + $this->user_id = intval($userdata['user_id']); + $this->username = $userdata['username']; + $this->password_hash = $userdata['password']; + $this->user_level = intval($userdata['user_level']); + $this->real_name = $userdata['real_name']; + $this->email = $userdata['email']; + $this->unread_pms = $userdata['num_pms']; + if(!$this->compat) + { + $this->theme = $userdata['theme']; + $this->style = $userdata['style']; + $this->signature = $userdata['signature']; + $this->reg_time = $userdata['reg_time']; + } + // Small security risk here - it allows someone who has already authenticated as an administrator to store the "super" key in + // the cookie. Change this to USER_LEVEL_MEMBER to override that. The same 15-minute restriction applies to this "exploit". + $this->auth_level = $userdata['auth_level']; + if(!isset($template->named_theme_list[$this->theme])) + { + if($this->compat || !is_object($template)) + { + $this->theme = 'oxygen'; + $this->style = 'bleu'; + } + else + { + $this->theme = $template->default_theme; + $this->style = $template->default_style; + } + } + $user = true; + + if(isset($_REQUEST['auth']) && !$this->sid_super) + { + // Now he thinks he's a moderator. Or maybe even an administrator. Let's find out if he's telling the truth. + if($this->compat) + { + $key = $_REQUEST['auth']; + $super = $this->compat_validate_session($key); + } + else + { + $key = strrev($_REQUEST['auth']); + $super = $this->validate_session($key); + } + if(is_array($super)) + { + $this->auth_level = intval($super['auth_level']); + $this->sid_super = $_REQUEST['auth']; + } + } + } + } + if(!$user) + { + //exit; + $this->register_guest_session(); + } + if(!$this->compat) + { + // init groups + $q = $this->sql('SELECT g.group_name,g.group_id,m.is_mod FROM '.table_prefix.'groups AS g + LEFT JOIN '.table_prefix.'group_members AS m + ON g.group_id=m.group_id + WHERE ( m.user_id='.$this->user_id.' + OR g.group_name=\'Everyone\') + ' . ( enano_version() == '1.0RC1' ? '' : 'AND ( m.pending != 1 OR m.pending IS NULL )' ) . ' + ORDER BY group_id ASC;'); // Make sure "Everyone" comes first so the permissions can be overridden + if($row = $db->fetchrow()) + { + do { + $this->groups[$row['group_id']] = $row['group_name']; + $this->group_mod[$row['group_id']] = ( intval($row['is_mod']) == 1 ); + } while($row = $db->fetchrow()); + } + else + { + die('No group info'); + } + } + $this->check_banlist(); + + if ( isset ( $_GET['printable'] ) ) + { + $this->theme = 'printable'; + $this->style = 'default'; + } + + } + + # Logins + + /** + * Attempts to perform a login using crypto functions + * @param string $username The username + * @param string $aes_data The encrypted password, hex-encoded + * @param string $aes_key The MD5 hash of the encryption key, hex-encoded + * @param string $challenge The 256-bit MD5 challenge string - first 128 bits should be the hash, the last 128 should be the challenge salt + * @param int $level The privilege level we're authenticating for, defaults to 0 + * @return string 'success' on success, or error string on failure + */ + + function login_with_crypto($username, $aes_data, $aes_key, $challenge, $level = USER_LEVEL_MEMBER) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $privcache = $this->private_key; + + // Instanciate the Rijndael encryption object + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + + // Fetch our decryption key + + $aes_key = $this->fetch_public_key($aes_key); + if(!$aes_key) + return 'Couldn\'t look up public key "'.$aes_key.'" for decryption'; + + // Convert the key to a binary string + $bin_key = hexdecode($aes_key); + + if(strlen($bin_key) != AES_BITS / 8) + return 'The decryption key is the wrong length'; + + // Decrypt our password + $password = $aes->decrypt($aes_data, $bin_key, ENC_HEX); + + // Initialize our success switch + $success = false; + + // Select the user data from the table, and decrypt that so we can verify the password + $this->sql('SELECT password,old_encryption,user_id,user_level,theme,style,temp_password,temp_password_time FROM '.table_prefix.'users WHERE lcase(username)=\''.$this->prepare_text(strtolower($username)).'\';'); + if($db->numrows() < 1) + return 'The username and/or password is incorrect.'; + $row = $db->fetchrow(); + + // Check to see if we're logging in using a temporary password + + if((intval($row['temp_password_time']) + 3600*24) > time() ) + { + $temp_pass = $aes->decrypt( $row['temp_password'], $this->private_key, ENC_HEX ); + if( $temp_pass == $password ) + { + $url = makeUrlComplete('Special', 'PasswordReset/stage2/' . $row['user_id'] . '/' . $row['temp_password']); + redirect($url, 'Login sucessful', 'Please wait while you are transferred to the Password Reset form.'); + exit; + } + } + + if($row['old_encryption'] == 1) + { + // The user's password is stored using the obsolete and insecure MD5 algorithm, so we'll update the field with the new password + if(md5($password) == $row['password']) + { + $pass_stashed = $aes->encrypt($password, $this->private_key, ENC_HEX); + $this->sql('UPDATE '.table_prefix.'users SET password=\''.$pass_stashed.'\',old_encryption=0 WHERE user_id='.$row['user_id'].';'); + $success = true; + } + } + else + { + // Our password field is up-to-date with the >=1.0RC1 encryption standards, so decrypt the password in the table and see if we have a match; if so then do challenge authentication + $real_pass = $aes->decrypt(hexdecode($row['password']), $this->private_key, ENC_BINARY); + if($password == $real_pass) + { + // Yay! We passed AES authentication, now do an MD5 challenge check to make sure we weren't spoofed + $chal = substr($challenge, 0, 32); + $salt = substr($challenge, 32, 32); + $correct_challenge = md5( $real_pass . $salt ); + if($chal == $correct_challenge) + $success = true; + } + } + if($success) + { + if($level > $row['user_level']) + return 'You are not authorized for this level of access.'; + + $sess = $this->register_session(intval($row['user_id']), $username, $password, $level); + if($sess) + { + $this->username = $username; + $this->user_id = intval($row['user_id']); + $this->theme = $row['theme']; + $this->style = $row['style']; + + if($level > USER_LEVEL_MEMBER) + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); + else + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')'); + + $code = $plugins->setHook('login_success'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + return 'success'; + } + else + return 'Your login credentials were correct, but an internal error occurred while registering the session key in the database.'; + } + else + { + if($level > USER_LEVEL_MEMBER) + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); + else + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')'); + + return 'The username and/or password is incorrect.'; + } + } + + /** + * Attempts to login without using crypto stuff, mainly for use when the other side doesn't like Javascript + * This method of authentication is inherently insecure, there's really nothing we can do about it except hope and pray that everyone moves to Firefox + * Technically it still uses crypto, but it only decrypts the password already stored, which is (obviously) required for authentication + * @param string $username The username + * @param string $password The password -OR- the MD5 hash of the password if $already_md5ed is true + * @param bool $already_md5ed This should be set to true if $password is an MD5 hash, and should be false if it's plaintext. Defaults to false. + * @param int $level The privilege level we're authenticating for, defaults to 0 + */ + + function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $pass_hashed = ( $already_md5ed ) ? $password : md5($password); + + // Perhaps we're upgrading Enano? + if($this->compat) + { + return $this->login_compat($username, $pass_hashed, $level); + } + + // Instanciate the Rijndael encryption object + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + + // Initialize our success switch + $success = false; + + // Retrieve the real password from the database + $this->sql('SELECT password,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix.'users WHERE lcase(username)=\''.$this->prepare_text(strtolower($username)).'\';'); + if($db->numrows() < 1) + return 'The username and/or password is incorrect.'; + $row = $db->fetchrow(); + + // Check to see if we're logging in using a temporary password + + if((intval($row['temp_password_time']) + 3600*24) > time() ) + { + $temp_pass = $aes->decrypt( $row['temp_password'], $this->private_key, ENC_HEX ); + if( md5($temp_pass) == $pass_hashed ) + { + header('Location: ' . makeUrlComplete('Special', 'PasswordReset/stage2/' . $row['user_id'] . '/' . $row['temp_password']) ); + exit; + } + } + + if($row['old_encryption'] == 1) + { + // The user's password is stored using the obsolete and insecure MD5 algorithm - we'll update the field with the new password + if($pass_hashed == $row['password'] && !$already_md5ed) + { + $pass_stashed = $aes->encrypt($password, $this->private_key, ENC_HEX); + $this->sql('UPDATE '.table_prefix.'users SET password=\''.$pass_stashed.'\',old_encryption=0 WHERE user_id='.$row['user_id'].';'); + $success = true; + } + elseif($pass_hashed == $row['password'] && $already_md5ed) + { + // We don't have the real password so don't bother with encrypting it, just call it success and get out of here + $success = true; + } + } + else + { + // Our password field is up-to-date with the >=1.0RC1 encryption standards, so decrypt the password in the table and see if we have a match + $real_pass = $aes->decrypt($row['password'], $this->private_key); + if($pass_hashed == md5($real_pass)) + { + $success = true; + } + } + if($success) + { + if((int)$level > (int)$row['user_level']) + return 'You are not authorized for this level of access.'; + $sess = $this->register_session(intval($row['user_id']), $username, $real_pass, $level); + if($sess) + { + if($level > USER_LEVEL_MEMBER) + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); + else + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')'); + + $code = $plugins->setHook('login_success'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + return 'success'; + } + else + return 'Your login credentials were correct, but an internal error occured while registering the session key in the database.'; + } + else + { + if($level > USER_LEVEL_MEMBER) + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); + else + $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')'); + + return 'The username and/or password is incorrect.'; + } + } + + /** + * Attempts to log in using the old table structure and algorithm. + * @param string $username + * @param string $password This should be an MD5 hash + * @return string 'success' if successful, or error message on failure + */ + + function login_compat($username, $password, $level = 0) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $pass_hashed =& $password; + $this->sql('SELECT password,user_id,user_level FROM '.table_prefix.'users WHERE username=\''.$this->prepare_text($username).'\';'); + if($db->numrows() < 1) + return 'The username and/or password is incorrect.'; + $row = $db->fetchrow(); + if($row['password'] == $password) + { + if((int)$level > (int)$row['user_level']) + return 'You are not authorized for this level of access.'; + $sess = $this->register_session_compat(intval($row['user_id']), $username, $password, $level); + if($sess) + return 'success'; + else + return 'Your login credentials were correct, but an internal error occured while registering the session key in the database.'; + } + else + { + return 'The username and/or password is incorrect.'; + } + } + + /** + * Registers a session key in the database. This function *ASSUMES* that the username and password have already been validated! + * Basically the session key is a base64-encoded cookie (encrypted with the site's private key) that says "u=[username];p=[sha1 of password]" + * @param int $user_id + * @param string $username + * @param string $password + * @param int $level The level of access to grant, defaults to USER_LEVEL_MEMBER + * @return bool + */ + + function register_session($user_id, $username, $password, $level = USER_LEVEL_MEMBER) + { + $salt = md5(microtime() . mt_rand()); + $passha1 = sha1($password); + $session_key = "u=$username;p=$passha1;s=$salt"; + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + $session_key = $aes->encrypt($session_key, $this->private_key, ENC_HEX); + if($level > USER_LEVEL_MEMBER) + { + $hexkey = strrev($session_key); + $this->sid_super = $hexkey; + $_GET['auth'] = $hexkey; + } + else + { + setcookie( 'sid', $session_key, time()+315360000, scriptPath.'/' ); + $_COOKIE['sid'] = $session_key; + } + $keyhash = md5($session_key); + $ip = ip2hex($_SERVER['REMOTE_ADDR']); + if(!$ip) + die('$session->register_session: Remote-Addr was spoofed'); + $time = time(); + if(!is_int($user_id)) + die('Somehow an SQL injection attempt crawled into our session registrar! (1)'); + if(!is_int($level)) + die('Somehow an SQL injection attempt crawled into our session registrar! (2)'); + + $query = $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$keyhash.'\', \''.$salt.'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');'); + return true; + } + + /** + * Identical to register_session in nature, but uses the old login/table structure. DO NOT use this. + * @see sessionManager::register_session() + * @access private + */ + + function register_session_compat($user_id, $username, $password, $level = 0) + { + $salt = md5(microtime() . mt_rand()); + $thekey = md5($password . $salt); + if($level > 0) + { + $this->sid_super = $thekey; + } + else + { + setcookie( 'sid', $thekey, time()+315360000, scriptPath.'/' ); + $_COOKIE['sid'] = $thekey; + } + $ip = ip2hex($_SERVER['REMOTE_ADDR']); + if(!$ip) + die('$session->register_session: Remote-Addr was spoofed'); + $time = time(); + if(!is_int($user_id)) + die('Somehow an SQL injection attempt crawled into our session registrar! (1)'); + if(!is_int($level)) + die('Somehow an SQL injection attempt crawled into our session registrar! (2)'); + $query = $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$thekey.'\', \''.$salt.'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');'); + return true; + } + + /** + * Creates/restores a guest session + * @todo implement real session management for guests + */ + + function register_guest_session() + { + global $db, $session, $paths, $template, $plugins; // Common objects + $this->username = $_SERVER['REMOTE_ADDR']; + $this->user_level = USER_LEVEL_GUEST; + if($this->compat || defined('IN_ENANO_INSTALL')) + { + $this->theme = 'oxygen'; + $this->style = 'bleu'; + } + else + { + $this->theme = ( isset($_GET['theme']) && isset($template->named_theme_list[$_GET['theme']])) ? $_GET['theme'] : $template->default_theme; + $this->style = ( isset($_GET['style']) && file_exists(ENANO_ROOT.'/themes/'.$this->theme . '/css/'.$_GET['style'].'.css' )) ? $_GET['style'] : substr($template->named_theme_list[$this->theme]['default_style'], 0, strlen($template->named_theme_list[$this->theme]['default_style'])-4); + } + $this->user_id = 1; + } + + /** + * Validates a session key, and returns the userdata associated with the key or false + * @param string $key The session key to validate + * @return array Keys are 'user_id', 'username', 'email', 'real_name', 'user_level', 'theme', 'style', 'signature', 'reg_time', 'account_active', 'activation_key', and 'auth_level' or bool false if validation failed. The key 'auth_level' is the maximum authorization level that this key provides. + */ + + function validate_session($key) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE, true); + $decrypted_key = $aes->decrypt($key, $this->private_key, ENC_HEX); + + if ( !$decrypted_key ) + { + die_semicritical('AES encryption error', '

Something went wrong during the AES decryption process.

'.print_r($decrypted_key, true).'
'); + } + + $n = preg_match('/^u='.$this->valid_username.';p=([A-Fa-f0-9]+?);s=([A-Fa-f0-9]+?)$/', $decrypted_key, $keydata); + if($n < 1) + { + // echo '(debug) $session->validate_session: Key does not match regex
Decrypted key: '.$decrypted_key; + return false; + } + $keyhash = md5($key); + $salt = $db->escape($keydata[3]); + $query = $this->sql('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms,x.* FROM '.table_prefix.'session_keys AS k + LEFT JOIN '.table_prefix.'users AS u + ON ( u.user_id=k.user_id ) + LEFT JOIN '.table_prefix.'users_extra AS x + ON ( u.user_id=x.user_id OR x.user_id IS NULL ) + LEFT JOIN '.table_prefix.'privmsgs AS p + ON ( p.message_to=u.username AND p.message_read=0 ) + WHERE k.session_key=\''.$keyhash.'\' + AND k.salt=\''.$salt.'\' + GROUP BY u.user_id;'); + if($db->numrows() < 1) + { + // echo '(debug) $session->validate_session: Key was not found in database
'; + return false; + } + $row = $db->fetchrow(); + $row['user_id'] =& $row['uid']; + $ip = ip2hex($_SERVER['REMOTE_ADDR']); + if($row['auth_level'] > $row['user_level']) + { + // Failed authorization check + // echo '(debug) $session->validate_session: access to this auth level denied
'; + return false; + } + if($ip != $row['source_ip']) + { + // Failed IP address check + // echo '(debug) $session->validate_session: IP address mismatch
'; + return false; + } + + // Do the password validation + $real_pass = $aes->decrypt($row['password'], $this->private_key, ENC_HEX); + + //die('
'.print_r($keydata, true).'
'); + if(sha1($real_pass) != $keydata[2]) + { + // Failed password check + // echo '(debug) $session->validate_session: encrypted password is wrong
Real password: '.$real_pass.'
Real hash: '.sha1($real_pass).'
User hash: '.$keydata[2]; + return false; + } + + $time_now = time(); + $time_key = $row['time'] + 900; + if($time_now > $time_key && $row['auth_level'] > USER_LEVEL_MEMBER) + { + // Session timed out + // echo '(debug) $session->validate_session: super session timed out
'; + $this->sw_timed_out = true; + return false; + } + + // If this is an elevated-access session key, update the time + if( $row['auth_level'] > USER_LEVEL_MEMBER ) + { + $this->sql('UPDATE '.table_prefix.'session_keys SET time='.time().' WHERE session_key=\''.$keyhash.'\';'); + } + + $row['password'] = md5($real_pass); + return $row; + } + + /** + * Validates a session key, and returns the userdata associated with the key or false. Optimized for compatibility with the old MD5-based auth system. + * @param string $key The session key to validate + * @return array Keys are 'user_id', 'username', 'email', 'real_name', 'user_level', 'theme', 'style', 'signature', 'reg_time', 'account_active', 'activation_key', and 'auth_level' or bool false if validation failed. The key 'auth_level' is the maximum authorization level that this key provides. + */ + + function compat_validate_session($key) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $key = $db->escape($key); + + $query = $this->sql('SELECT u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,k.source_ip,k.salt,k.time,k.auth_level FROM '.table_prefix.'session_keys AS k + LEFT JOIN '.table_prefix.'users AS u + ON u.user_id=k.user_id + WHERE k.session_key=\''.$key.'\';'); + if($db->numrows() < 1) + { + // echo '(debug) $session->validate_session: Key '.$key.' was not found in database
'; + return false; + } + $row = $db->fetchrow(); + $ip = ip2hex($_SERVER['REMOTE_ADDR']); + if($row['auth_level'] > $row['user_level']) + { + // Failed authorization check + // echo '(debug) $session->validate_session: user not authorized for this access level'; + return false; + } + if($ip != $row['source_ip']) + { + // Failed IP address check + // echo '(debug) $session->validate_session: IP address mismatch; IP in table: '.$row['source_ip'].'; reported IP: '.$ip.''; + return false; + } + + // Do the password validation + $real_key = md5($row['password'] . $row['salt']); + + //die('
'.print_r($keydata, true).'
'); + if($real_key != $key) + { + // Failed password check + // echo '(debug) $session->validate_session: supplied password is wrong
Real key: '.$real_key.'
User key: '.$key; + return false; + } + + $time_now = time(); + $time_key = $row['time'] + 900; + if($time_now > $time_key && $row['auth_level'] >= 1) + { + $this->sw_timed_out = true; + // Session timed out + // echo '(debug) $session->validate_session: super session timed out
'; + return false; + } + + return $row; + } + + /** + * Demotes us to one less than the specified auth level. AKA destroys elevated authentication and/or logs out the user, depending on $level + * @param int $level How low we should go - USER_LEVEL_MEMBER means demote to USER_LEVEL_GUEST, and anything more powerful than USER_LEVEL_MEMBER means demote to USER_LEVEL_MEMBER + * @return string 'success' if successful, or error on failure + */ + + function logout($level = USER_LEVEL_MEMBER) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $ou = $this->username; + $oid = $this->user_id; + if($level > USER_LEVEL_CHPREF) + { + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + if(!$this->user_logged_in || $this->auth_level < USER_LEVEL_MOD) return 'success'; + // Destroy elevated privileges + $keyhash = md5(strrev($this->sid_super)); + $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.$keyhash.'\' AND user_id=\'' . $this->user_id . '\';'); + $this->sid_super = false; + $this->auth_level = USER_LEVEL_MEMBER; + } + else + { + if($this->user_logged_in) + { + // Completely destroy our session + if($this->auth_level > USER_LEVEL_CHPREF) + { + $this->logout(USER_LEVEL_ADMIN); + } + $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.md5($this->sid).'\';'); + setcookie( 'sid', '', time()-(3600*24), scriptPath.'/' ); + } + } + $code = $plugins->setHook('logout_success'); // , Array('level'=>$level,'old_username'=>$ou,'old_user_id'=>$oid)); + foreach ( $code as $cmd ) + { + eval($cmd); + } + return 'success'; + } + + # Miscellaneous stuff + + /** + * Appends the high-privilege session key to the URL if we are authorized to do high-privilege stuff + * @param string $url The URL to add session data to + * @return string + */ + + function append_sid($url) + { + $sep = ( strstr($url, '?') ) ? '&' : '?'; + if ( $this->sid_super ) + { + $url = $url . $sep . 'auth=' . urlencode($this->sid_super); + // echo($this->sid_super.'
'); + } + return $url; + } + + /** + * Grabs the user's password MD5 + * @return string, or bool false if access denied + */ + + function grab_password_hash() + { + if(!$this->password_hash) return false; + return $this->password_hash; + } + + /** + * Destroys the user's password MD5 in memory + */ + + function disallow_password_grab() + { + $this->password_hash = false; + return false; + } + + /** + * Generates an AES key and stashes it in the database + * @return string Hex-encoded AES key + */ + + function rijndael_genkey() + { + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + $key = $aes->gen_readymade_key(); + $keys = getConfig('login_key_cache'); + if(is_string($keys)) + $keys .= $key; + else + $keys = $key; + setConfig('login_key_cache', $keys); + return $key; + } + + /** + * Generate a totally random 128-bit value for MD5 challenges + * @return string + */ + + function dss_rand() + { + $aes = new AESCrypt(); + $random = $aes->randkey(128); + unset($aes); + return md5(microtime() . $random); + } + + /** + * Fetch a cached login public key using the MD5sum as an identifier. Each key can only be fetched once before it is destroyed. + * @param string $md5 The MD5 sum of the key + * @return string, or bool false on failure + */ + + function fetch_public_key($md5) + { + $keys = getConfig('login_key_cache'); + $keys = enano_str_split($keys, AES_BITS / 4); + + foreach($keys as $i => $k) + { + if(md5($k) == $md5) + { + unset($keys[$i]); + if(count($keys) > 0) + { + if ( strlen(getConfig('login_key_cache') ) > 64000 ) + { + // This should only need to be done once every month or so for an average-size site + setConfig('login_key_cache', ''); + } + else + { + $keys = implode('', array_values($keys)); + setConfig('login_key_cache', $keys); + } + } + else + { + setConfig('login_key_cache', ''); + } + return $k; + } + } + // Couldn't find the key... + return false; + } + + /** + * Adds a user to a group. + * @param int User ID + * @param int Group ID + * @param bool Group moderator - defaults to false + * @return bool True on success, false on failure + */ + + function add_user_to_group($user_id, $group_id, $is_mod = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + // Validation + if ( !is_int($user_id) || !is_int($group_id) || !is_bool($is_mod) ) + return false; + if ( $user_id < 1 || $group_id < 1 ) + return false; + + $mod_switch = ( $is_mod ) ? '1' : '0'; + $q = $this->sql('SELECT member_id,is_mod FROM '.table_prefix.'group_members WHERE user_id=' . $user_id . ' AND group_id=' . $group_id . ';'); + if ( !$q ) + $db->_die(); + if ( $db->numrows() < 1 ) + { + // User is not in group + $this->sql('INSERT INTO '.table_prefix.'group_members(user_id,group_id,is_mod) VALUES(' . $user_id . ', ' . $group_id . ', ' . $mod_switch . ');'); + return true; + } + else + { + $row = $db->fetchrow(); + // Update modship status + if ( strval($row['is_mod']) == $mod_switch ) + { + // Modship unchanged + return true; + } + else + { + // Modship changed + $this->sql('UPDATE '.table_prefix.'group_members SET is_mod=' . $mod_switch . ' WHERE member_id=' . $row['member_id'] . ';'); + return true; + } + } + return false; + } + + /** + * Removes a user from a group. + * @param int User ID + * @param int Group ID + * @return bool True on success, false on failure + * @todo put a little more error checking in... + */ + + function remove_user_from_group($user_id, $group_id) + { + if ( !is_int($user_id) || !is_int($group_id) ) + return false; + $this->sql('DELETE FROM '.table_prefix."group_members WHERE user_id=$user_id AND group_id=$group_id;"); + return true; + } + + /** + * Checks the banlist to ensure that we're an allowed user. Doesn't return anything because it dies if the user is banned. + */ + + function check_banlist() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if($this->compat) + $q = $this->sql('SELECT ban_id,ban_type,ban_value,is_regex FROM '.table_prefix.'banlist ORDER BY ban_type;'); + else + $q = $this->sql('SELECT ban_id,ban_type,ban_value,is_regex,reason FROM '.table_prefix.'banlist ORDER BY ban_type;'); + if(!$q) $db->_die('The banlist data could not be selected.'); + $banned = false; + while($row = $db->fetchrow()) + { + if($this->compat) + $row['reason'] = 'None available - session manager is in compatibility mode'; + switch($row['ban_type']) + { + case BAN_IP: + if(intval($row['is_regex'])==1) { + if(preg_match('#'.$row['ban_value'].'#i', $_SERVER['REMOTE_ADDR'])) + { + $banned = true; + $reason = $row['reason']; + } + } + else { + if($row['ban_value']==$_SERVER['REMOTE_ADDR']) { $banned = true; $reason = $row['reason']; } + } + break; + case BAN_USER: + if(intval($row['is_regex'])==1) { + if(preg_match('#'.$row['ban_value'].'#i', $this->username)) + { + $banned = true; + $reason = $row['reason']; + } + } + else { + if($row['ban_value']==$this->username) { $banned = true; $reason = $row['reason']; } + } + break; + case BAN_EMAIL: + if(intval($row['is_regex'])==1) { + if(preg_match('#'.$row['ban_value'].'#i', $this->email)) + { + $banned = true; + $reason = $row['reason']; + } + } + else { + if($row['ban_value']==$this->email) { $banned = true; $reason = $row['reason']; } + } + break; + default: + die('Ban error: rule "'.$row['ban_value'].'" has an invalid type ('.$row['ban_type'].')'); + } + } + if($banned && $paths->get_pageid_from_url() != $paths->nslist['Special'].'CSS') + { + // This guy is banned - kill the session, kill the database connection, bail out, and be pretty about it + die_semicritical('Ban notice', '
You have been banned from this website. Please contact the site administrator for more information.

Reason:
'.$reason.'
'); + exit; + } + } + + # Registration + + /** + * Registers a user. This does not perform any type of login. + * @param string $username + * @param string $password This should be unencrypted. + * @param string $email + * @param string $real_name Optional, defaults to ''. + */ + + function create_user($username, $password, $email, $real_name = '') { + global $db, $session, $paths, $template, $plugins; // Common objects + + // Initialize AES + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + + if(!preg_match('#^'.$this->valid_username.'$#', $username)) return 'The username you chose contains invalid characters.'; + $username = $this->prepare_text($username); + $email = $this->prepare_text($email); + $real_name = $this->prepare_text($real_name); + $password = $aes->encrypt($password, $this->private_key, ENC_HEX); + + $nameclause = ( $real_name != '' ) ? ' OR real_name=\''.$real_name.'\'' : ''; + $q = $this->sql('SELECT * FROM '.table_prefix.'users WHERE lcase(username)=\''.strtolower($username).'\' OR email=\''.$email.'\''.$nameclause.';'); + if($db->numrows() > 0) { + $r = 'The '; + $i=0; + $row = $db->fetchrow(); + // Wow! An error checker that actually speaks English with the properest grammar! :-P + if($row['username'] == $username) { $r .= 'username'; $i++; } + if($row['email'] == $email) { if($i) $r.=', '; $r .= 'e-mail address'; $i++; } + if($row['real_name'] == $real_name && $real_name != '') { if($i) $r.=', and '; $r .= 'real name'; $i++; } + $r .= ' that you entered '; + $r .= ( $i == 1 ) ? 'is' : 'are'; + $r .= ' already in use by another user.'; + return $r; + } + + // Require the account to be activated? + switch(getConfig('account_activation')) + { + case 'none': + default: + $active = '1'; + break; + case 'user': + $active = '0'; + break; + case 'admin': + $active = '0'; + break; + } + + // Generate a totally random activation key + $actkey = sha1 ( microtime() . mt_rand() ); + + // We good, create the user + $this->sql('INSERT INTO '.table_prefix.'users ( username, password, email, real_name, theme, style, reg_time, account_active, activation_key, user_level ) VALUES ( \''.$username.'\', \''.$password.'\', \''.$email.'\', \''.$real_name.'\', \''.$template->default_theme.'\', \''.$template->default_style.'\', '.time().', '.$active.', \''.$actkey.'\', '.USER_LEVEL_CHPREF.' )'); + + // Require the account to be activated? + switch(getConfig('account_activation')) + { + case 'none': + default: + break; + case 'user': + $a = $this->send_activation_mail($username); + if(!$a) + { + $this->admin_activation_request($username); + return 'The activation e-mail could not be sent due to an internal error. This could possibly be due to an incorrect SMTP configuration. A request has been sent to the administrator to activate your account for you. ' . $a; + } + break; + case 'admin': + $this->admin_activation_request($username); + break; + } + + // Leave some data behind for the hook + $code = $plugins->setHook('user_registered'); // , Array('username'=>$username)); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + // $this->register_session($username, $password); + return 'success'; + } + + /** + * Attempts to send an e-mail to the specified user with activation instructions. + * @param string $u The usernamd of the user requesting activation + * @return bool true on success, false on failure + */ + + function send_activation_mail($u, $actkey = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $q = $this->sql('SELECT username,email FROM '.table_prefix.'users WHERE user_id=1 OR user_level=' . USER_LEVEL_ADMIN . ' ORDER BY user_id ASC;'); + $un = $db->fetchrow(); + $admin_user = $un['username']; + $q = $this->sql('SELECT username,activation_key,account_active,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($u).'\';'); + $r = $db->fetchrow(); + if ( empty($r['email']) ) + $db->_die('BUG: $session->send_activation_mail(): no e-mail address in row'); + $message = 'Dear '.$u.', +Thank you for registering on '.getConfig('site_name').'. Your account creation is almost complete. To complete the registration process, please click the following link or paste it into your web browser: + +'; + if(isset($_SERVER['HTTPS'])) $prot = 'https'; + else $prot = 'http'; + if($_SERVER['SERVER_PORT'] == '80') $p = ''; + else $p = ':'.$_SERVER['SERVER_PORT']; + $sidbak = false; + if($this->sid_super) + $sidbak = $this->sid_super; + $this->sid_super = false; + $aklink = makeUrlNS('Special', 'ActivateAccount/'.str_replace(' ', '_', $u).'/'. ( ( is_string($actkey) ) ? $actkey : $r['activation_key'] ) ); + if($sidbak) + $this->sid_super = $sidbak; + unset($sidbak); + $message .= "$prot://".$_SERVER['HTTP_HOST'].$p.$aklink; + $message .= "\n\nSincerely yours, \n$admin_user and the ".$_SERVER['HTTP_HOST']." administration team"; + error_reporting(E_ALL); + dc_dump($r, 'session: about to send activation e-mail to '.$r['email']); + if(getConfig('smtp_enabled') == '1') + { + $result = smtp_send_email($r['email'], getConfig('site_name').' website account activation', preg_replace("#(?sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE user_id='.$user.';'); // This is SAFE! This is only called if $user is an integer + } + elseif(is_string($user)) + { + $q = $this->sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($user).'\';'); + } + else + { + return false; + } + + $row = $db->fetchrow(); + $temp_pass = $this->random_pass(); + + $this->register_temp_password($row['user_id'], $temp_pass); + + $site_name = getConfig('site_name'); + + $message = "Dear {$row['username']}, + +Someone (hopefully you) on the {$site_name} website requested that a new password be created. + +The request was sent from the IP address {$_SERVER['REMOTE_ADDR']}. + +If you did not request the new password, then you do not need to do anything; the password will be invalidated after 24 hours. + +If you did request this password, then please log in using the password shown below: + + Password: {$temp_pass} + +After you log in using this password, you will be able to reset your real password. You can only log in using this temporary password once. + +Sincerely yours, +The {$site_name} administration team +"; + + if(getConfig('smtp_enabled') == '1') + { + $result = smtp_send_email($row['email'], getConfig('site_name').' password reset', preg_replace("#(?'.$result.'

'; + $result = false; + } + } else { + $result = mail($row['email'], getConfig('site_name').' password reset', preg_replace("#(?encrypt($password, $this->private_key, ENC_HEX); + $this->sql('UPDATE '.table_prefix.'users SET temp_password=\'' . $temp_pass . '\',temp_password_time='.time().' WHERE user_id='.intval($user_id).';'); + } + + /** + * Sends a request to the admin panel to have the username $u activated. + * @param string $u The username of the user requesting activation + */ + + function admin_activation_request($u) + { + global $db; + $this->sql('INSERT INTO '.table_prefix.'logs(log_type, action, time_id, date_string, author, edit_summary) VALUES(\'admin\', \'activ_req\', '.time().', \''.date('d M Y h:i a').'\', \''.$this->username.'\', \''.$db->escape($u).'\');'); + } + + /** + * Activates a user account. If the action fails, a report is sent to the admin. + * @param string $user The username of the user requesting activation + * @param string $key The activation key + */ + + function activate_account($user, $key) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $this->sql('UPDATE '.table_prefix.'users SET account_active=1 WHERE username=\''.$db->escape($user).'\' AND activation_key=\''.$db->escape($key).'\';'); + $r = mysql_affected_rows(); + if ( $r > 0 ) + { + $e = $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'activ_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($user).'\', \''.$_SERVER['REMOTE_ADDR'].'\')'); + } + else + { + $e = $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'activ_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($user).'\', \''.$_SERVER['REMOTE_ADDR'].'\')'); + } + return $r; + } + + /** + * For a given user level identifier (USER_LEVEL_*), returns a string describing that user level. + * @param int User level + * @return string + */ + + function userlevel_to_string($user_level) + { + switch ( $user_level ) + { + case USER_LEVEL_GUEST: + return 'Low - guest privileges'; + case USER_LEVEL_MEMBER: + return 'Standard - normal member level'; + case USER_LEVEL_CHPREF: + return 'Medium - user can change his/her own e-mail address and password'; + case USER_LEVEL_MOD: + return 'High - moderator privileges'; + case USER_LEVEL_ADMIN: + return 'Highest - administrative privileges'; + default: + return "Unknown ($user_level)"; + } + } + + /** + * Updates a user's information in the database. Note that any of the values except $user_id can be false if you want to preserve the old values. + * @param int $user_id The user ID of the user to update - this cannot be changed + * @param string $username The new username + * @param string $old_pass The current password - only required if sessionManager::$user_level < USER_LEVEL_ADMIN. This should usually be an UNENCRYPTED string. This can also be an array - if it is, key 0 is treated as data AES-encrypted with key 1 + * @param string $password The new password + * @param string $email The new e-mail address + * @param string $realname The new real name + * @param string $signature The updated forum/comment signature + * @param int $user_level The updated user level + * @return string 'success' if successful, or array of error strings on failure + */ + + function update_user($user_id, $username = false, $old_pass = false, $password = false, $email = false, $realname = false, $signature = false, $user_level = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + // Create some arrays + + $errors = Array(); // Used to hold error strings + $strs = Array(); // Sub-query statements + + // Scan the user ID for problems + if(intval($user_id) < 1) $errors[] = 'SQL injection attempt'; + + // Instanciate the AES encryption class + $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); + + // If all of our input vars are false, then we've effectively done our job so get out of here + if($username === false && $password === false && $email === false && $realname === false && $signature === false && $user_level === false) + { + // echo 'debug: $session->update_user(): success (no changes requested)'; + return 'success'; + } + + // Initialize our authentication check + $authed = false; + + // Verify the inputted password + if(is_string($old_pass)) + { + $q = $this->sql('SELECT password FROM '.table_prefix.'users WHERE user_id='.$user_id.';'); + if($db->numrows() < 1) + { + $errors[] = 'The password data could not be selected for verification.'; + } + else + { + $row = $db->fetchrow(); + $real = $aes->decrypt($row['password'], $this->private_key, ENC_HEX); + if($real == $old_pass) + $authed = true; + } + } + + elseif(is_array($old_pass)) + { + $old_pass = $aes->decrypt($old_pass[0], $old_pass[1]); + $q = $this->sql('SELECT password FROM '.table_prefix.'users WHERE user_id='.$user_id.';'); + if($db->numrows() < 1) + { + $errors[] = 'The password data could not be selected for verification.'; + } + else + { + $row = $db->fetchrow(); + $real = $aes->decrypt($row['password'], $this->private_key, ENC_HEX); + if($real == $old_pass) + $authed = true; + } + } + + // Initialize our query + $q = 'UPDATE '.table_prefix.'users SET '; + + if($this->auth_level >= USER_LEVEL_ADMIN || $authed) // Need the current password in order to update the e-mail address, change the username, or reset the password + { + // Username + if(is_string($username)) + { + // Check the username for problems + if(!preg_match('#^'.$this->valid_username.'$#', $username)) + $errors[] = 'The username you entered contains invalid characters.'; + $strs[] = 'username=\''.$db->escape($username).'\''; + } + // Password + if(is_string($password) && strlen($password) >= 6) + { + // Password needs to be encrypted before being stashed + $encpass = $aes->encrypt($password, $this->private_key, ENC_HEX); + if(!$encpass) + $errors[] = 'The password could not be encrypted due to an internal error.'; + $strs[] = 'password=\''.$encpass.'\''; + } + // E-mail addy + if(is_string($email)) + { + // I didn't write this regex. + if(!preg_match('/^(?:[\w\d]+\.?)+@(?:(?:[\w\d]\-?)+\.)+\w{2,4}$/', $email)) + $errors[] = 'The e-mail address you entered is invalid.'; + $strs[] = 'email=\''.$db->escape($email).'\''; + } + } + // Real name + if(is_string($realname)) + { + $strs[] = 'real_name=\''.$db->escape($realname).'\''; + } + // Forum/comment signature + if(is_string($signature)) + { + $strs[] = 'signature=\''.$db->escape($signature).'\''; + } + // User level + if(is_int($user_level)) + { + $strs[] = 'user_level='.$user_level; + } + + // Add our generated query to the query string + $q .= implode(',', $strs); + + // One last error check + if(sizeof($strs) < 1) $errors[] = 'An internal error occured building the SQL query, this is a bug'; + if(sizeof($errors) > 0) return $errors; + + // Free our temp arrays + unset($strs, $errors); + + // Finalize the query and run it + $q .= ' WHERE user_id='.$user_id.';'; + $this->sql($q); + + // We also need to trigger re-activation. + if ( is_string($email) ) + { + switch(getConfig('account_activation')) + { + case 'user': + case 'admin': + + if ( $session->user_level >= USER_LEVEL_MOD && getConfig('account_activation') == 'admin' ) + // Don't require re-activation by admins for admins + break; + + // retrieve username + if ( !$username ) + { + $q = $this->sql('SELECT username FROM '.table_prefix.'users WHERE user_id='.$user_id.';'); + if($db->numrows() < 1) + { + $errors[] = 'The username could not be selected.'; + } + else + { + $row = $db->fetchrow(); + $username = $row['username']; + } + } + if ( !$username ) + return $errors; + + // Generate a totally random activation key + $actkey = sha1 ( microtime() . mt_rand() ); + $a = $this->send_activation_mail($username, $actkey); + if(!$a) + { + $this->admin_activation_request($username); + } + // Deactivate the account until e-mail is confirmed + $q = $db->sql_query('UPDATE '.table_prefix.'users SET account_active=0,activation_key=\'' . $actkey . '\' WHERE user_id=' . $user_id . ';'); + break; + } + } + + // Yay! We're done + return 'success'; + } + + # + # Access Control Lists + # + + /** + * Creates a new permission field in memory. If the permissions are set in the database, they are used. Otherwise, $default_perm is used. + * @param string $acl_type An identifier for this field + * @param int $default_perm Whether permission should be granted or not if it's not specified in the ACLs. + * @param string $desc A human readable name for the permission type + * @param array $deps The list of dependencies - this should be an array of ACL types + * @param string $scope Which namespaces this field should apply to. This should be either a pipe-delimited list of namespace IDs or just "All". + */ + + function register_acl_type($acl_type, $default_perm = AUTH_DISALLOW, $desc = false, $deps = Array(), $scope = 'All') + { + if(isset($this->acl_types[$acl_type])) + return false; + else + { + if(!$desc) + { + $desc = capitalize_first_letter(str_replace('_', ' ', $acl_type)); + } + $this->acl_types[$acl_type] = $default_perm; + $this->acl_descs[$acl_type] = $desc; + $this->acl_deps[$acl_type] = $deps; + $this->acl_scope[$acl_type] = explode('|', $scope); + } + return true; + } + + /** + * Tells us whether permission $type is allowed or not based on the current rules. + * @param string $type The permission identifier ($acl_type passed to sessionManager::register_acl_type()) + * @param bool $no_deps If true, disables dependency checking + * @return bool True if allowed, false if denied or if an error occured + */ + + function get_permissions($type, $no_deps = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if ( isset( $this->perms[$type] ) ) + { + if ( $this->perms[$type] == AUTH_DENY ) + $ret = false; + else if ( $this->perms[$type] == AUTH_WIKIMODE && $paths->wiki_mode ) + $ret = true; + else if ( $this->perms[$type] == AUTH_WIKIMODE && !$paths->wiki_mode ) + $ret = false; + else if ( $this->perms[$type] == AUTH_ALLOW ) + $ret = true; + else if ( $this->perms[$type] == AUTH_DISALLOW ) + $ret = false; + } + else if(isset($this->acl_types[$type])) + { + if ( $this->acl_types[$type] == AUTH_DENY ) + $ret = false; + else if ( $this->acl_types[$type] == AUTH_WIKIMODE && $paths->wiki_mode ) + $ret = true; + else if ( $this->acl_types[$type] == AUTH_WIKIMODE && !$paths->wiki_mode ) + $ret = false; + else if ( $this->acl_types[$type] == AUTH_ALLOW ) + $ret = true; + else if ( $this->acl_types[$type] == AUTH_DISALLOW ) + $ret = false; + } + else + { + // ACL type is undefined + trigger_error('Unknown access type "' . $type . '"', E_USER_WARNING); + return false; // Be on the safe side and deny access + } + if ( !$no_deps ) + { + if ( !$this->acl_check_deps($type) ) + return false; + } + return $ret; + } + + /** + * Fetch the permissions that apply to the current user for the page specified. The object you get will have the get_permissions method + * and several other abilities. + * @param string $page_id + * @param string $namespace + * @return object + */ + + function fetch_page_acl($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( count ( $this->acl_base_cache ) < 1 ) + { + // Permissions table not yet initialized + return false; + } + + //if ( !isset( $paths->pages[$paths->nslist[$namespace] . $page_id] ) ) + //{ + // // Page does not exist + // return false; + //} + + $object = new Session_ACLPageInfo( $page_id, $namespace, $this->acl_types, $this->acl_descs, $this->acl_deps, $this->acl_base_cache ); + + return $object; + + } + + /** + * Read all of our permissions from the database and process/apply them. This should be called after the page is determined. + * @access private + */ + + function init_permissions() + { + global $db, $session, $paths, $template, $plugins; // Common objects + // Initialize the permissions list with some defaults + $this->perms = $this->acl_types; + $this->acl_defaults_used = $this->perms; + + // Fetch sitewide defaults from the permissions table + $bs = 'SELECT rules FROM '.table_prefix.'acl WHERE page_id IS NULL AND namespace IS NULL AND ( '; + + $q = Array(); + $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )'; + if(count($this->groups) > 0) + { + foreach($this->groups as $g_id => $g_name) + { + $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )'; + } + } + $bs .= implode(' OR ', $q) . ' ) ORDER BY target_type ASC, target_id ASC;'; + $q = $this->sql($bs); + if ( $row = $db->fetchrow() ) + { + do { + $rules = $this->string_to_perm($row['rules']); + $is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 ); + $this->acl_merge_with_current($rules, $is_everyone); + } while ( $row = $db->fetchrow() ); + } + + // Eliminate types that don't apply to this namespace + foreach ( $this->perms AS $i => $perm ) + { + if ( !in_array ( $paths->namespace, $this->acl_scope[$i] ) && !in_array('All', $this->acl_scope[$i]) ) + { + unset($this->perms[$i]); + } + } + + // Cache the sitewide permissions for later use + $this->acl_base_cache = $this->perms; + + // Build a query to grab ACL info + $bs = 'SELECT rules,target_type,target_id FROM '.table_prefix.'acl WHERE ( '; + $q = Array(); + $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )'; + if(count($this->groups) > 0) + { + foreach($this->groups as $g_id => $g_name) + { + $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )'; + } + } + // The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual + // permissions to override group permissions. + $bs .= implode(' OR ', $q) . ' ) AND ( page_id=\''.$db->escape($paths->cpage['urlname_nons']).'\' AND namespace=\''.$db->escape($paths->namespace).'\' ) + ORDER BY target_type ASC, page_id ASC, namespace ASC;'; + $q = $this->sql($bs); + if ( $row = $db->fetchrow() ) + { + do { + $rules = $this->string_to_perm($row['rules']); + $is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 ); + $this->acl_merge_with_current($rules, $is_everyone); + } while ( $row = $db->fetchrow() ); + } + + } + + /** + * Extends the scope of a permission type. + * @param string The name of the permission type + * @param string The namespace(s) that should be covered. This can be either one namespace ID or a pipe-delimited list. + * @param object Optional - the current $paths object, in case we're doing this from the acl_rule_init hook + */ + + function acl_extend_scope($perm_type, $namespaces, &$p_in) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $p_obj = ( is_object($p_in) ) ? $p_in : $paths; + $nslist = explode('|', $namespaces); + foreach ( $nslist as $i => $ns ) + { + if ( !isset($p_obj->nslist[$ns]) ) + { + unset($nslist[$i]); + } + else + { + $this->acl_scope[$perm_type][] = $ns; + if ( isset($this->acl_types[$perm_type]) && !isset($this->perms[$perm_type]) ) + { + $this->perms[$perm_type] = $this->acl_types[$perm_type]; + } + } + } + } + + /** + * Converts a permissions field into a string for database insertion. Similar in spirit to serialize(). + * @param array $perms An associative array with only integers as values + * @return string + */ + + function perm_to_string($perms) + { + $s = ''; + foreach($perms as $perm => $ac) + { + $s .= "$perm=$ac;"; + } + return $s; + } + + /** + * Converts a permissions string back to an array. + * @param string $perms The result from sessionManager::perm_to_string() + * @return array + */ + + function string_to_perm($perms) + { + $ret = Array(); + preg_match_all('#([a-z0-9_-]+)=([0-9]+);#i', $perms, $matches); + foreach($matches[1] as $i => $t) + { + $ret[$t] = intval($matches[2][$i]); + } + return $ret; + } + + /** + * Merges two ACL arrays. Both parameters should be permission list arrays. The second group takes precedence over the first, but AUTH_DENY always prevails. + * @param array $perm1 The first set of permissions + * @param array $perm2 The second set of permissions + * @return array + */ + + function acl_merge($perm1, $perm2) + { + $ret = $perm1; + foreach ( $perm2 as $type => $level ) + { + if ( isset( $ret[$type] ) ) + { + if ( $ret[$type] != AUTH_DENY ) + $ret[$type] = $level; + } + // else + // { + // $ret[$type] = $level; + // } + } + return $ret; + } + + /** + * Merges the ACL array sent with the current permissions table, deciding precedence based on whether defaults are in effect or not. + * @param array The array to merge into the master ACL list + * @param bool If true, $perm is treated as the "new default" + * @param int 1 if this is a site-wide ACL, 2 if page-specific. Defaults to 2. + */ + + function acl_merge_with_current($perm, $is_everyone = false, $scope = 2) + { + foreach ( $this->perms as $i => $p ) + { + if ( isset($perm[$i]) ) + { + if ( $is_everyone && !$this->acl_defaults_used[$i] ) + continue; + // Decide precedence + if ( isset($this->acl_defaults_used[$i]) ) + { + //echo "$i: default in use, overriding to: {$perm[$i]}
"; + // Defaults are in use, override + $this->perms[$i] = $perm[$i]; + $this->acl_defaults_used[$i] = ( $is_everyone ); + } + else + { + //echo "$i: default NOT in use"; + // Defaults are not in use, merge as normal + if ( $this->perms[$i] != AUTH_DENY ) + { + //echo ", but overriding"; + $this->perms[$i] = $perm[$i]; + } + //echo "
"; + } + } + } + } + + /** + * Merges two ACL arrays. Both parameters should be permission list arrays. The second group takes precedence + * over the first, without exceptions. This is used to merge the hardcoded defaults with admin-specified + * defaults, which take precedence. + * @param array $perm1 The first set of permissions + * @param array $perm2 The second set of permissions + * @return array + */ + + function acl_merge_complete($perm1, $perm2) + { + $ret = $perm1; + foreach ( $perm2 as $type => $level ) + { + $ret[$type] = $level; + } + return $ret; + } + + /** + * Tell us if the dependencies for a given permission are met. + * @param string The ACL permission ID + * @return bool + */ + + function acl_check_deps($type) + { + if(!isset($this->acl_deps[$type])) // This will only happen if the permissions table is hacked or improperly accessed + return true; + if(sizeof($this->acl_deps[$type]) < 1) + return true; + $deps = $this->acl_deps[$type]; + while(true) + { + $full_resolved = true; + $j = sizeof($deps); + for ( $i = 0; $i < $j; $i++ ) + { + $b = $deps; + $deps = array_merge($deps, $this->acl_deps[$deps[$i]]); + if( $b == $deps ) + { + break 2; + } + $j = sizeof($deps); + } + } + //die('
'.print_r($deps, true).'
'); + foreach($deps as $d) + { + if ( !$this->get_permissions($d) ) + { + return false; + } + } + return true; + } + + /** + * Makes a CAPTCHA code and caches the code in the database + * @param int $len The length of the code, in bytes + * @return string A unique identifier assigned to the code. This hash should be passed to sessionManager::getCaptcha() to retrieve the code. + */ + + function make_captcha($len = 7) + { + $chars = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9'); + $s = ''; + for($i=0;$i<$len;$i++) $s .= $chars[mt_rand(0, count($chars)-1)]; + $hash = md5(microtime() . mt_rand()); + $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key,salt,auth_level,source_ip,user_id) VALUES(\''.$hash.'\', \''.$s.'\', -1, \''.ip2hex($_SERVER['REMOTE_ADDR']).'\', -2);'); + return $hash; + } + + /** + * For the given code ID, returns the correct CAPTCHA code, or false on failure + * @param string $hash The unique ID assigned to the code + * @return string The correct confirmation code + */ + + function get_captcha($hash) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $s = $this->sql('SELECT salt FROM '.table_prefix.'session_keys WHERE session_key=\''.$db->escape($hash).'\' AND source_ip=\''.ip2hex($_SERVER['REMOTE_ADDR']).'\';'); + if($db->numrows() < 1) return false; + $r = $db->fetchrow(); + return $r['salt']; + } + + /** + * Deletes all CAPTCHA codes cached in the DB for this user. + */ + + function kill_captcha() + { + $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE user_id=-2 AND source_ip=\''.ip2hex($_SERVER['REMOTE_ADDR']).'\';'); + } + + /** + * Generates a random password. + * @param int $length Optional - length of password + * @return string + */ + + function random_pass($length = 10) + { + $valid_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_+@#%&<>'; + $valid_chars = enano_str_split($valid_chars); + $ret = ''; + for ( $i = 0; $i < $length; $i++ ) + { + $ret .= $valid_chars[mt_rand(0, count($valid_chars)-1)]; + } + return $ret; + } + + /** + * Generates some Javascript that calls the AES encryption library. + * @param string The name of the form + * @param string The name of the password field + * @param string The name of the field that switches encryption on or off + * @param string The name of the field that contains the encryption key + * @param string The name of the field that will contain the encrypted password + * @param string The name of the field that handles MD5 challenge data + * @return string + */ + + function aes_javascript($form_name, $pw_field, $use_crypt, $crypt_key, $crypt_data, $challenge) + { + $code = ' + + '; + return $code; + } + +} + +/** + * Class used to fetch permissions for a specific page. Used internally by SessionManager. + * @package Enano + * @subpackage Session manager + * @license http://www.gnu.org/copyleft/gpl.html + * @access private + */ + +class Session_ACLPageInfo { + + /** + * The page ID of this ACL info package + * @var string + */ + + var $page_id; + + /** + * The namespace of the page being checked + * @var string + */ + + var $namespace; + + /** + * Our list of permission types. + * @access private + * @var array + */ + + var $acl_types = Array(); + + /** + * The list of descriptions for the permission types + * @var array + */ + + var $acl_descs = Array(); + + /** + * A list of dependencies for ACL types. + * @var array + */ + + var $acl_deps = Array(); + + /** + * Our tell-all list of permissions. + * @access private - or, preferably, protected...too bad this has to be PHP4 compatible + * @var array + */ + + var $perms = Array(); + + /** + * Constructor. + * @param string $page_id The ID of the page to check + * @param string $namespace The namespace of the page to check. + * @param array $acl_types List of ACL types + * @param array $acl_descs List of human-readable descriptions for permissions (associative) + * @param array $acl_deps List of dependencies for permissions. For example, viewing history/diffs depends on the ability to read the page. + * @param array $base What to start with - this is an attempt to reduce the number of SQL queries. + */ + + function Session_ACLPageInfo($page_id, $namespace, $acl_types, $acl_descs, $acl_deps, $base) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $this->perms = $session->acl_merge_complete($acl_types, $base); + $this->acl_deps = $acl_deps; + $this->acl_types = $acl_types; + $this->acl_descs = $acl_descs; + + // Build a query to grab ACL info + $bs = 'SELECT rules FROM '.table_prefix.'acl WHERE ( '; + $q = Array(); + $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$session->user_id.' )'; + if(count($session->groups) > 0) + { + foreach($session->groups as $g_id => $g_name) + { + $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )'; + } + } + // The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual + // permissions to override group permissions. + $bs .= implode(' OR ', $q) . ' ) AND ( page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\' ) + ORDER BY target_type ASC, page_id ASC, namespace ASC;'; + $q = $session->sql($bs); + if ( $row = $db->fetchrow() ) + { + do { + $rules = $session->string_to_perm($row['rules']); + $this->perms = $session->acl_merge($this->perms, $rules); + } while ( $row = $db->fetchrow() ); + } + + $this->page_id = $page_id; + $this->namespace = $namespace; + } + + /** + * Tells us whether permission $type is allowed or not based on the current rules. + * @param string $type The permission identifier ($acl_type passed to sessionManager::register_acl_type()) + * @param bool $no_deps If true, disables dependency checking + * @return bool True if allowed, false if denied or if an error occured + */ + + function get_permissions($type, $no_deps = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if ( isset( $this->perms[$type] ) ) + { + if ( $this->perms[$type] == AUTH_DENY ) + $ret = false; + else if ( $this->perms[$type] == AUTH_WIKIMODE && + ( isset($paths->pages[$paths->nslist[$this->namespace].$this->page_id]) && + ( $paths->pages[$paths->nslist[$this->namespace].$this->page_id]['wiki_mode'] == '1' || + ( $paths->pages[$paths->nslist[$this->namespace].$this->page_id]['wiki_mode'] == '2' + && getConfig('wiki_mode') == '1' + ) ) ) ) + $ret = true; + else if ( $this->perms[$type] == AUTH_WIKIMODE && ( + !isset($paths->pages[$paths->nslist[$this->namespace].$this->page_id]) + || ( + isset($paths->pages[$paths->nslist[$this->namespace].$this->page_id]) && ( + $paths->pages[$paths->nslist[$this->namespace].$this->page_id]['wiki_mode'] == '0' + || ( + $paths->pages[$paths->nslist[$this->namespace].$this->page_id]['wiki_mode'] == '2' && getConfig('wiki_mode') != '1' + ) ) ) ) ) + $ret = false; + else if ( $this->perms[$type] == AUTH_ALLOW ) + $ret = true; + else if ( $this->perms[$type] == AUTH_DISALLOW ) + $ret = false; + } + else if(isset($this->acl_types[$type])) + { + if ( $this->acl_types[$type] == AUTH_DENY ) + $ret = false; + else if ( $this->acl_types[$type] == AUTH_WIKIMODE && $paths->wiki_mode ) + $ret = true; + else if ( $this->acl_types[$type] == AUTH_WIKIMODE && !$paths->wiki_mode ) + $ret = false; + else if ( $this->acl_types[$type] == AUTH_ALLOW ) + $ret = true; + else if ( $this->acl_types[$type] == AUTH_DISALLOW ) + $ret = false; + } + else + { + // ACL type is undefined + trigger_error('Unknown access type "' . $type . '"', E_USER_WARNING); + return false; // Be on the safe side and deny access + } + if ( !$no_deps ) + { + if ( !$this->acl_check_deps($type) ) + return false; + } + return $ret; + } + + /** + * Tell us if the dependencies for a given permission are met. + * @param string The ACL permission ID + * @return bool + */ + + function acl_check_deps($type) + { + if(!isset($this->acl_deps[$type])) // This will only happen if the permissions table is hacked or improperly accessed + return true; + if(sizeof($this->acl_deps[$type]) < 1) + return true; + $deps = $this->acl_deps[$type]; + while(true) + { + $full_resolved = true; + $j = sizeof($deps); + for ( $i = 0; $i < $j; $i++ ) + { + $b = $deps; + $deps = array_merge($deps, $this->acl_deps[$deps[$i]]); + if( $b == $deps ) + { + break 2; + } + $j = sizeof($deps); + } + } + //die('
'.print_r($deps, true).'
'); + foreach($deps as $d) + { + if ( !$this->get_permissions($d) ) + { + return false; + } + } + return true; + } + +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/stats.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/stats.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,83 @@ +cpage['urlname_nons']; + $namespace = $paths->namespace; + } + if($namespace == 'Special' || $namespace == 'Admin') return false; + static $stats_done = false; + static $stats_data = Array(); + if(!$stats_done) + { + $q = $db->sql_query('INSERT INTO '.table_prefix.'hits (username,time,page_id,namespace) VALUES(\''.$db->escape($session->username).'\', '.time().', \''.$db->escape($page_id).'\', \''.$db->escape($namespace).'\')'); + if(!$q) + { + echo $db->get_error(); + return false; + } + $db->free_result(); + $stats_done = true; + return true; + } + } +} + +/** + * Fetch a list of the most-viewed pages + * @param int the number of pages to return, send -1 to get all pages (suicide for large sites) + * @return array key names are a string set to the page ID/namespace, and values are an int with the number of hits + */ + +function stats_top_pages($num = 5) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + if(!is_int($num)) return false; + $q = $db->sql_query('SELECT page_id,namespace FROM '.table_prefix.'hits ORDER BY page_id ASC, namespace ASC;'); + if(!$q) + { + echo $db->get_error(); + return false; + } + $counter = Array(); + while ( $row = $db->fetchrow() ) + { + $kname = $paths->nslist[$row['namespace']] . $row['page_id']; + if(isset($counter[$kname])) $counter[$kname]++; + else $counter[$kname] = 1; + } + $db->free_result(); + // Pure magic! At least I don't have to do the work... + arsort($counter); + // Can't use array_slice here because key names are only preserved in PHP5 + $k = array_keys($counter); + $final = Array(); + if(sizeof($counter) < $num || $num == -1) $num = sizeof($counter); + for ( $i = 0; $i < $num; $i++ ) + { + $final[$k[$i]] = $counter[$k[$i]]; + } + unset($counter, $k, $row); + return $final; +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/template.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/template.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1788 @@ +tpl_bool = Array(); + $this->tpl_strings = Array(); + $this->sidebar_extra = ''; + $this->toolbar_menu = ''; + $this->additional_headers = ''; + $this->plugin_blocks = Array(); + $this->theme_loaded = false; + + $this->theme_list = Array(); + $this->named_theme_list = Array(); + $e = $db->sql_query('SELECT theme_id,theme_name,enabled,default_style FROM '.table_prefix.'themes WHERE enabled=1 ORDER BY theme_order;'); + if(!$e) $db->_die('The list of themes could not be selected.'); + for($i=0;$i < $db->numrows(); $i++) + { + $this->theme_list[$i] = $db->fetchrow(); + $this->named_theme_list[$this->theme_list[$i]['theme_id']] = $this->theme_list[$i]; + } + $db->free_result(); + $this->default_theme = $this->theme_list[0]['theme_id']; + $dir = ENANO_ROOT.'/themes/'.$this->default_theme.'/css/'; + $list = Array(); + // Open a known directory, and proceed to read its contents + if (is_dir($dir)) { + if ($dh = opendir($dir)) { + while (($file = readdir($dh)) !== false) { + if(preg_match('#^(.*?)\.css$#i', $file) && $file != '_printable.css') { + $list[] = substr($file, 0, strlen($file)-4); + } + } + closedir($dh); + } + } + + $def = ENANO_ROOT.'/themes/'.$this->default_theme.'/css/'.$this->named_theme_list[$this->default_theme]['default_style']; + if(file_exists($def)) + { + $this->default_style = substr($this->named_theme_list[$this->default_theme]['default_style'], 0, strlen($this->named_theme_list[$this->default_theme]['default_style'])-4); + } else { + $this->default_style = $list[0]; + } + + $this->style_list = $list; + + } + function template() + { + $this->__construct(); + } + function sidebar_widget($t, $h) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!defined('ENANO_TEMPLATE_LOADED')) + { + $this->load_theme($session->theme, $session->style); + } + if(!$this->sidebar_widgets) + $this->sidebar_widgets = ''; + $tplvars = $this->extract_vars('elements.tpl'); + $parser = $this->makeParserText($tplvars['sidebar_section_raw']); + $parser->assign_vars(Array('TITLE'=>$t,'CONTENT'=>$h)); + $this->plugin_blocks[$t] = $h; + $this->sidebar_widgets .= $parser->run(); + } + function add_header($html) + { + $this->additional_headers .= "\n" . $html; + } + function get_css($s = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!defined('ENANO_TEMPLATE_LOADED')) + $this->load_theme($session->theme, $session->style); + $path = ( $s ) ? 'css/'.$s : 'css/'.$this->style.'.css'; + if ( !file_exists(ENANO_ROOT . '/themes/' . $this->theme . '/' . $path) ) + { + echo "/* WARNING: Falling back to default file because file $path does not exist */\n"; + $path = 'css/' . $this->style_list[0] . '.css'; + } + return $this->process_template($path); + } + function load_theme($name = false, $css = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $this->theme = ( $name ) ? $name : $session->theme; + $this->style = ( $css ) ? $css : $session->style; + if ( !$this->theme ) + { + $this->theme = $this->theme_list[0]['theme_id']; + $this->style = substr($this->theme_list[0]['default_style'], 0, strlen($this->theme_list[0]['default_style'])-4); + } + $this->theme_loaded = true; + } + + function init_vars() + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $email; + + dc_here("template: initializing all variables"); + + if(!$this->theme || !$this->style) + { + $this->load_theme(); + } + + if(defined('ENANO_TEMPLATE_LOADED')) + { + dc_here('template: access denied to call template::init_vars(), bailing out'); + die_semicritical('Illegal call', '

$template->load_theme was called multiple times, this is not supposed to happen. Exiting with fatal error.

'); + } + + define('ENANO_TEMPLATE_LOADED', ''); + + $tplvars = $this->extract_vars('elements.tpl'); + + dc_here('template: setting all template vars'); + + if(isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) + { + $this->add_header(' + + '); + } + + // Get the "article" button text (depends on namespace) + switch($paths->namespace) { + case "Article": + default: + $ns = 'article'; + break; + case "Admin": + $ns = 'administration page'; + break; + case "System": + $ns = 'system message'; + break; + case "File": + $ns = 'uploaded file'; + break; + case "Help": + $ns = 'documentation page'; + break; + case "User": + $ns = 'user page'; + break; + case "Special": + $ns = 'special page'; + break; + case "Template": + $ns = 'template'; + break; + case "Project": + $ns = 'project page'; + break; + case "Category": + $ns = 'category'; + break; + } + $this->namespace_string = $ns; + $code = $plugins->setHook('page_type_string_set'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + $ns =& $this->namespace_string; + + // Initialize the toolbar + $tb = ''; + + // Create "xx page" button + + $btn_selected = ( isset($tplvars['toolbar_button_selected'])) ? $tplvars['toolbar_button_selected'] : $tplvars['toolbar_button']; + $parser = $this->makeParserText($btn_selected); + + $parser->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxReset()); return false;" title="View the page contents, all of the page contents, and nothing but the page contents (alt-a)" accesskey="a"', + 'PARENTFLAGS' => 'id="mdgToolbar_article"', + 'HREF' => makeUrl($paths->page, null, true), + 'TEXT' => $this->namespace_string + )); + + $tb .= $parser->run(); + + $button = $this->makeParserText($tplvars['toolbar_button']); + + // Page toolbar + // Comments button + if ( $session->get_permissions('read') && getConfig('enable_comments')=='1' && $paths->namespace != 'Special' && $paths->namespace != 'Admin' && $paths->cpage['comments_on'] == 1 ) + { + + $e = $db->sql_query('SELECT approved FROM '.table_prefix.'comments WHERE page_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\''.$paths->namespace.'\';'); + if ( !$e ) + { + $db->_die(); + } + $nc = $db->numrows(); + $nu = 0; + $na = 0; + + while ( $r = $db->fetchrow() ) + { + if ( !$r['approved'] ) + { + $nu++; + } + else + { + $na++; + } + } + + $db->free_result(); + $n = ( $session->get_permissions('mod_comments') ) ? (string)$nc : (string)$na; + if ( $session->get_permissions('mod_comments') && $nu > 0 ) + { + $n .= ' total/'.$nu.' unapp.'; + } + + $button->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxComments()); return false;" title="View the comments that other users have posted about this page (alt-c)" accesskey="c"', + 'PARENTFLAGS' => 'id="mdgToolbar_discussion"', + 'HREF' => makeUrl($paths->page, 'do=comments', true), + 'TEXT' => 'discussion ('.$n.')', + )); + + $tb .= $button->run(); + } + // Edit button + if($session->get_permissions('read') && ($paths->namespace != 'Special' && $paths->namespace != 'Admin') && ( $session->get_permissions('edit_page') && ( ( $paths->page_protected && $session->get_permissions('even_when_protected') ) || !$paths->page_protected ) ) ) + { + $button->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxEditor()); return false;" title="Edit the contents of this page (alt-e)" accesskey="e"', + 'PARENTFLAGS' => 'id="mdgToolbar_edit"', + 'HREF' => makeUrl($paths->page, 'do=edit', true), + 'TEXT' => 'edit this page' + )); + $tb .= $button->run(); + // View source button + } + else if($session->get_permissions('view_source') && ( !$session->get_permissions('edit_page') || !$session->get_permissions('even_when_protected') && $paths->page_protected ) && $paths->namespace != 'Special' && $paths->namespace != 'Admin') + { + $button->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxViewSource()); return false;" title="View the source code (wiki markup) that this page uses (alt-e)" accesskey="e"', + 'PARENTFLAGS' => 'id="mdgToolbar_edit"', + 'HREF' => makeUrl($paths->page, 'do=viewsource', true), + 'TEXT' => 'view source' + )); + $tb .= $button->run(); + } + // History button + if ( $session->get_permissions('read') /* && $paths->wiki_mode */ && $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin' && $session->get_permissions('history_view') ) + { + $button->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxHistory()); return false;" title="View a log of actions taken on this page (alt-h)" accesskey="h"', + 'PARENTFLAGS' => 'id="mdgToolbar_history"', + 'HREF' => makeUrl($paths->page, 'do=history', true), + 'TEXT' => 'history' + )); + $tb .= $button->run(); + } + + $menubtn = $this->makeParserText($tplvars['toolbar_menu_button']); + + // Additional actions menu + // Rename button + if ( $session->get_permissions('read') && $paths->page_exists && ( $session->get_permissions('rename') && ( $paths->page_protected && $session->get_permissions('even_when_protected') || !$paths->page_protected ) ) && $paths->namespace != 'Special' && $paths->namespace != 'Admin' ) + { + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxRename()); return false;" title="Change the display name of this page (alt-r)" accesskey="r"', + 'HREF' => makeUrl($paths->page, 'do=rename', true), + 'TEXT' => 'rename', + )); + $this->toolbar_menu .= $menubtn->run(); + } + + // Vote-to-delete button + if ( $paths->wiki_mode && $session->get_permissions('vote_delete') && $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin') + { + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxDelVote()); return false;" title="Vote to have this page deleted (alt-d)" accesskey="d"', + 'HREF' => makeUrl($paths->page, 'do=delvote', true), + 'TEXT' => 'vote to delete this page', + )); + $this->toolbar_menu .= $menubtn->run(); + } + + // Clear-votes button + if ( $session->get_permissions('read') && $paths->wiki_mode && $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin' && $session->get_permissions('vote_reset') && $paths->cpage['delvotes'] > 0) + { + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxResetDelVotes()); return false;" title="Vote to have this page deleted (alt-y)" accesskey="y"', + 'HREF' => makeUrl($paths->page, 'do=resetvotes', true), + 'TEXT' => 'reset deletion votes', + )); + $this->toolbar_menu .= $menubtn->run(); + } + + // Printable page button + if ( $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin' ) + { + $menubtn->assign_vars(array( + 'FLAGS' => 'title="View a version of this page that is suitable for printing"', + 'HREF' => makeUrl($paths->page, 'printable=yes', true), + 'TEXT' => 'view printable version', + )); + $this->toolbar_menu .= $menubtn->run(); + } + + // Protect button + if($session->get_permissions('read') && $paths->wiki_mode && $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin' && $session->get_permissions('protect')) + { + + $label = $this->makeParserText($tplvars['toolbar_label']); + $label->assign_vars(array('TEXT' => 'protection:')); + $t0 = $label->run(); + + $ctmp = ''; + if ( $paths->cpage['protected'] == 1 ) + { + $ctmp=' style="text-decoration: underline;"'; + } + $menubtn->assign_vars(array( + 'FLAGS' => 'accesskey="i" onclick="ajaxProtect(1); return false;" id="protbtn_1" title="Prevents all non-administrators from editing this page. [alt-i]"'.$ctmp, + 'HREF' => makeUrl($paths->page, 'do=protect&level=1', true), + 'TEXT' => 'on' + )); + $t1 = $menubtn->run(); + + $ctmp = ''; + if ( $paths->cpage['protected'] == 0 ) + { + $ctmp=' style="text-decoration: underline;"'; + } + $menubtn->assign_vars(array( + 'FLAGS' => 'accesskey="o" onclick="ajaxProtect(0); return false;" id="protbtn_0" title="Allows everyone to edit this page. [alt-o]"'.$ctmp, + 'HREF' => makeUrl($paths->page, 'do=protect&level=0', true), + 'TEXT' => 'off' + )); + $t2 = $menubtn->run(); + + $ctmp = ''; + if ( $paths->cpage['protected'] == 2 ) + { + $ctmp = ' style="text-decoration: underline;"'; + } + $menubtn->assign_vars(array( + 'FLAGS' => 'accesskey="p" onclick="ajaxProtect(2); return false;" id="protbtn_2" title="Allows only users who have been registered for 4 days to edit this page. [alt-p]"'.$ctmp, + 'HREF' => makeUrl($paths->page, 'do=protect&level=2', true), + 'TEXT' => 'semi' + )); + $t3 = $menubtn->run(); + + $this->toolbar_menu .= ' + + + + + + +
'.$t0.''.$t1.''.$t2.''.$t3.'
'; + } + + // Wiki mode button + if($session->get_permissions('read') && $paths->page_exists && $session->get_permissions('set_wiki_mode') && $paths->namespace != 'Special' && $paths->namespace != 'Admin') + { + // label at start + $label = $this->makeParserText($tplvars['toolbar_label']); + $label->assign_vars(array('TEXT' => 'page wiki mode:')); + $t0 = $label->run(); + + // on button + $ctmp = ''; + if ( $paths->cpage['wiki_mode'] == 1 ) + { + $ctmp = ' style="text-decoration: underline;"'; + } + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="ajaxSetWikiMode(1); return false;" id="wikibtn_1" title="Forces wiki functions to be allowed on this page."'.$ctmp, + 'HREF' => makeUrl($paths->page, 'do=setwikimode&level=1', true), + 'TEXT' => 'on' + )); + $t1 = $menubtn->run(); + + // off button + $ctmp = ''; + if ( $paths->cpage['wiki_mode'] == 0 ) + { + $ctmp=' style="text-decoration: underline;"'; + } + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="ajaxSetWikiMode(0); return false;" id="wikibtn_0" title="Forces wiki functions to be disabled on this page."'.$ctmp, + 'HREF' => makeUrl($paths->page, 'do=setwikimode&level=0', true), + 'TEXT' => 'off' + )); + $t2 = $menubtn->run(); + + // global button + $ctmp = ''; + if ( $paths->cpage['wiki_mode'] == 2 ) + { + $ctmp=' style="text-decoration: underline;"'; + } + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="ajaxSetWikiMode(2); return false;" id="wikibtn_2" title="Causes this page to use the global wiki mode setting (default)"'.$ctmp, + 'HREF' => makeUrl($paths->page, 'do=setwikimode&level=2', true), + 'TEXT' => 'global' + )); + $t3 = $menubtn->run(); + + // Tack it onto the list of buttons that are already there... + $this->toolbar_menu .= ' + + + + + + +
'.$t0.''.$t1.''.$t2.''.$t3.'
'; + } + + // Clear logs button + if ( $session->get_permissions('read') && $session->get_permissions('clear_logs') && $paths->wiki_mode && $paths->namespace != 'Special' && $paths->namespace != 'Admin' ) + { + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxClearLogs()); return false;" title="Remove all edit and action logs for this page from the database. IRREVERSIBLE! (alt-l)" accesskey="l"', + 'HREF' => makeUrl($paths->page, 'do=flushlogs', true), + 'TEXT' => 'clear page logs', + )); + $this->toolbar_menu .= $menubtn->run(); + } + + // Delete page button + if ( $session->get_permissions('read') && $session->get_permissions('delete_page') && $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin' ) + { + $s = 'delete this page'; + if ( $paths->cpage['delvotes'] == 1 ) + { + $s .= ' ('.$paths->cpage['delvotes'].' vote)'; + } + else if ( $paths->cpage['delvotes'] > 1 ) + { + $s .= ' ('.$paths->cpage['delvotes'].' votes)'; + } + + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxDeletePage()); return false;" title="Delete this page. This is always reversible unless the logs are cleared. (alt-k)" accesskey="k"', + 'HREF' => makeUrl($paths->page, 'do=deletepage', true), + 'TEXT' => $s, + )); + $this->toolbar_menu .= $menubtn->run(); + + } + + // Password-protect button + if(isset($paths->cpage['password'])) + { + if ( $paths->cpage['password'] == '' ) + { + $a = $session->get_permissions('password_set'); + } + else + { + $a = $session->get_permissions('password_reset'); + } + } + else + { + $a = $session->get_permissions('password_set'); + } + if ( $a && $session->get_permissions('read') && $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin' ) + { + // label at start + $label = $this->makeParserText($tplvars['toolbar_label']); + $label->assign_vars(array('TEXT' => 'password:')); + $t0 = $label->run(); + + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxSetPassword()); return false;" title="Require a password in order for this page to be viewed"', + 'HREF' => '#', + 'TEXT' => 'set', + )); + $t = $menubtn->run(); + + $this->toolbar_menu .= '
'.$t0.''.$t.'
'; + } + + // Manage ACLs button + if($session->get_permissions('edit_acl') || $session->user_level >= USER_LEVEL_ADMIN) + { + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="return ajaxOpenACLManager();" title="Manage who can do what with this page (alt-m)" accesskey="m"', + 'HREF' => makeUrl($paths->page, 'do=aclmanager', true), + 'TEXT' => 'manage page access', + )); + $this->toolbar_menu .= $menubtn->run(); + } + + // Administer page button + if ( $session->user_level >= USER_LEVEL_ADMIN && $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin' ) + { + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxAdminPage()); return false;" title="Administrative options for this page" accesskey="g"', + 'HREF' => makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'PageManager', true), + 'TEXT' => 'administrative options', + )); + $this->toolbar_menu .= $menubtn->run(); + } + + if ( strlen($this->toolbar_menu) > 0 ) + { + $button->assign_vars(array( + 'FLAGS' => 'id="mdgToolbar_moreoptions" onclick="return false;" title="Additional options for working with this page"', + 'PARENTFLAGS' => '', + 'HREF' => makeUrl($paths->page, 'do=moreoptions', true), + 'TEXT' => 'more options' + )); + $tb .= $button->run(); + } + + $is_opera = (isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'Opera')) ? true : false; + + $this->tpl_bool = Array( + 'auth_admin'=>$session->user_level >= USER_LEVEL_ADMIN ? true : false, + 'user_logged_in'=>$session->user_logged_in, + 'opera'=>$is_opera, + ); + + if($session->sid_super) { $ash = '&auth='.$session->sid_super; $asq = "?auth=".$session->sid_super; $asa = "&auth=".$session->sid_super; $as2 = htmlspecialchars(urlSeparator).'auth='.$session->sid_super; } + else { $asq=''; $asa=''; $as2 = ''; $ash = ''; } + + $code = $plugins->setHook('compile_template'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + // Some additional sidebar processing + if($this->sidebar_extra != '') { + $se = $this->sidebar_extra; + $parser = $this->makeParserText($tplvars['sidebar_section_raw']); + $parser->assign_vars(Array('TITLE'=>'Links','CONTENT'=>$se)); + $this->sidebar_extra = $parser->run(); + } + + $this->sidebar_extra = $this->sidebar_extra.$this->sidebar_widgets; + + $this->tpl_bool['fixed_menus'] = false; + /* if($this->sidebar_extra == '') $this->tpl_bool['right_sidebar'] = false; + else */ $this->tpl_bool['right_sidebar'] = true; + + $this->tpl_bool['auth_rename'] = ( $paths->page_exists && ( $session->get_permissions('rename') && ( $paths->page_protected && $session->get_permissions('even_when_protected') || !$paths->page_protected ) ) && $paths->namespace != 'Special' && $paths->namespace != 'Admin'); + + $this->tpl_bool['enable_uploads'] = ( getConfig('enable_uploads') == '1' && $session->get_permissions('upload_files') ) ? true : false; + + $this->tpl_bool['stupid_mode'] = false; + + if($paths->page == $paths->nslist['Special'].'Administration') $this->tpl_bool['in_admin'] = true; + else $this->tpl_bool['in_admin'] = false; + + $p = ( isset($_GET['printable']) ) ? '/printable' : ''; + + // Add the e-mail address client code to the header + $this->add_header($email->jscode()); + + // Generate the code for the Log out and Change theme sidebar buttons + // Once again, the new template parsing system can be used here + + $parser = $this->makeParserText($tplvars['sidebar_button']); + + $parser->assign_vars(Array( + 'HREF'=>makeUrlNS('Special', 'Logout'), + 'FLAGS'=>'onclick="mb_logout(); return false;"', + 'TEXT'=>'Log out', + )); + + $logout_link = $parser->run(); + + $parser->assign_vars(Array( + 'HREF'=>makeUrlNS('Special', 'Login/' . $paths->page), + 'FLAGS'=>'onclick="ajaxStartLogin(); return false;"', + 'TEXT'=>'Log in', + )); + + $login_link = $parser->run(); + + $parser->assign_vars(Array( + 'HREF'=>makeUrlNS('Special', 'ChangeStyle/'.$paths->page), + 'FLAGS'=>'onclick="ajaxChangeStyle(); return false;"', + 'TEXT'=>'Change theme', + )); + + $theme_link = $parser->run(); + + $SID = ($session->sid_super) ? $session->sid_super : ''; + + // Generate the dynamic javascript vars + $js_dynamic = ' "; + + $tpl_strings = Array( + 'PAGE_NAME'=>$paths->cpage['name'], + 'PAGE_URLNAME'=>$paths->cpage['urlname'], + 'SITE_NAME'=>getConfig('site_name'), + 'USERNAME'=>$session->username, + 'SITE_DESC'=>getConfig('site_desc'), + 'TOOLBAR'=>$tb, + 'SCRIPTPATH'=>scriptPath, + 'CONTENTPATH'=>contentPath, + 'ADMIN_SID_QUES'=>$asq, + 'ADMIN_SID_AMP'=>$asa, + 'ADMIN_SID_AMP_HTML'=>$ash, + 'ADMIN_SID_AUTO'=>$as2, + 'ADDITIONAL_HEADERS'=>$this->additional_headers, + 'COPYRIGHT'=>getConfig('copyright_notice'), + 'TOOLBAR_EXTRAS'=>$this->toolbar_menu, + 'REQUEST_URI'=>$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'], + 'STYLE_LINK'=>makeUrlNS('Special', 'CSS'.$p, null, true), //contentPath.$paths->nslist['Special'].'CSS' . $p, + 'LOGIN_LINK'=>$login_link, + 'LOGOUT_LINK'=>$logout_link, + 'THEME_LINK'=>$theme_link, + 'TEMPLATE_DIR'=>scriptPath.'/themes/'.$this->theme, + 'THEME_ID'=>$this->theme, + 'STYLE_ID'=>$this->style, + 'JS_DYNAMIC_VARS'=>$js_dynamic, + 'UNREAD_PMS'=>$session->unread_pms + ); + + foreach ( $paths->nslist as $ns_id => $ns_prefix ) + { + $tpl_strings[ 'NS_' . strtoupper($ns_id) ] = $ns_prefix; + } + + $this->tpl_strings = array_merge($tpl_strings, $this->tpl_strings); + list($this->tpl_strings['SIDEBAR_LEFT'], $this->tpl_strings['SIDEBAR_RIGHT'], $min) = $this->fetch_sidebar(); + $this->tpl_bool['sidebar_left'] = ( $this->tpl_strings['SIDEBAR_LEFT'] != $min) ? true : false; + $this->tpl_bool['sidebar_right'] = ( $this->tpl_strings['SIDEBAR_RIGHT'] != $min) ? true : false; + $this->tpl_bool['right_sidebar'] = $this->tpl_bool['sidebar_right']; // backward compatibility + } + + function header($simple = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + ob_start(); + + if(!$this->theme_loaded) + { + $this->load_theme($session->theme, $session->style); + } + + $headers_sent = true; + dc_here('template: generating and sending the page header'); + if(!defined('ENANO_HEADERS_SENT')) + define('ENANO_HEADERS_SENT', ''); + if(!$this->no_headers) echo ( $simple ) ? $this->process_template('simple-header.tpl') : $this->process_template('header.tpl'); + if ( !$simple && $session->user_logged_in && $session->unread_pms > 0 ) + { + echo $this->notify_unread_pms(); + } + if ( !$simple && $session->sw_timed_out ) + { + $login_link = makeUrlNS('Special', 'Login/' . $paths->fullpage, 'level=' . $session->user_level, true); + echo '
'; + echo 'Your administrative session has timed out. Log in again'; + echo '
'; + } + } + function footer($simple = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + dc_here('template: generating and sending the page footer'); + if(!$this->no_headers) { + + if(!defined('ENANO_HEADERS_SENT')) + $this->header(); + + global $_starttime; + if(isset($_GET['sqldbg']) && $session->get_permissions('mod_misc')) + { + echo '

Query list as requested on URI

';
+        echo $db->sql_backtrace();
+        echo '
'; + } + + $f = microtime_float(); + $f = $f - $_starttime; + $f = round($f, 4); + $dbg = 'Time: '.$f.'s | Queries: '.$db->num_queries; + $t = ( $simple ) ? $this->process_template('simple-footer.tpl') : $this->process_template('footer.tpl'); + $t = str_replace('[[Stats]]', $dbg, $t); + $t = str_replace('[[NumQueries]]', (string)$db->num_queries, $t); + $t = str_replace('[[GenTime]]', (string)$f, $t); + echo $t; + + ob_end_flush(); + } + else return ''; + } + function getHeader() + { + $headers_sent = true; + dc_here('template: generating and sending the page header'); + if(!defined('ENANO_HEADERS_SENT')) + define('ENANO_HEADERS_SENT', ''); + if(!$this->no_headers) return $this->process_template('header.tpl'); + } + function getFooter() + { + global $db, $session, $paths, $template, $plugins; // Common objects + dc_here('template: generating and sending the page footer'); + if(!$this->no_headers) { + global $_starttime; + $t = ''; + + if(isset($_GET['sqldbg']) && $session->get_permissions('mod_misc')) + { + $t .= '

Query list as requested on URI

';
+        $t .= $db->sql_backtrace();
+        $t .= '
'; + } + + $f = microtime_float(); + $f = $f - $_starttime; + $f = round($f, 4); + $dbg = 'Time: '.$f.'s | Queries: '.$db->num_queries; + $t.= $this->process_template('footer.tpl'); + $t = str_replace('[[Stats]]', $dbg, $t); + $t = str_replace('[[NumQueries]]', (string)$db->num_queries, $t); + $t = str_replace('[[GenTime]]', (string)$f, $t); + return $t; + } + else return ''; + } + + function process_template($file) { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!defined('ENANO_TEMPLATE_LOADED')) + { + $this->load_theme(); + $this->init_vars(); + } + eval($this->compile_template($file)); + return $tpl_code; + } + + function extract_vars($file) { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$this->theme) + { + die('$template->extract_vars(): theme not yet loaded, so we can\'t open template files yet...this is a bug and should be reported.

Backtrace, most recent call first:
'.enano_debug_print_backtrace(true).'
'); + } + if(!is_file(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file)) die('Cannot find '.$file.' file for style "'.$this->theme.'", exiting'); + $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file); + preg_match_all('#<\!-- VAR ([A-z0-9_-]*) -->(.*?)<\!-- ENDVAR \\1 -->#is', $text, $matches); + $tplvars = Array(); + for($i=0;$itheme.'/'.$text)) die('Cannot find '.$text.' file for style, exiting'); + $n = $text; + $tpl_filename = ENANO_ROOT . '/cache/' . $this->theme . '-' . str_replace('/', '-', $n) . '.php'; + if(!is_file(ENANO_ROOT . '/themes/'.$this->theme.'/'.$text)) die('Cannot find '.$text.' file for style, exiting'); + if(file_exists($tpl_filename) && getConfig('cache_thumbs')=='1') + { + include($tpl_filename); + $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$text); + if(isset($md5) && $md5 == md5($text)) { + return str_replace('\\"', '"', $tpl_text); + } + } + $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$n); + + $md5 = md5($text); + + $seed = md5 ( microtime() . mt_rand() ); + preg_match_all("/<\?php(.*?)\?>/is", $text, $m); + //die('
'.htmlspecialchars(print_r($m, true)).'
'); + for($i = 0; $i < sizeof($m[1]); $i++) + { + $text = str_replace("", "{PHPCODE:{$i}:{$seed}}", $text); + } + //die('
'.htmlspecialchars($text).'
'); + $text = 'ob_start(); echo \''.str_replace('\'', '\\\'', $text).'\'; $tpl_code = ob_get_contents(); ob_end_clean();'; + $text = preg_replace('##is', '\'; if(isset($this->tpl_bool[\'\\1\']) && $this->tpl_bool[\'\\1\']) { echo \'', $text); + $text = preg_replace('##is', '\'; if(isset($this->tpl_strings[\'\\1\'])) { echo \'', $text); + $text = preg_replace('##is', '\'; if(getConfig(\'plugin_\\1\')==\'1\') { echo \'', $text); + $text = preg_replace('##is', '\'; echo $template->tplWikiFormat($paths->sysMsg(\'\\1\')); echo \'', $text); + $text = preg_replace('##is', '\'; if(!$this->tpl_bool[\'\\1\']) { echo \'', $text); + $text = preg_replace('##is', '\'; } else { echo \'', $text); + $text = preg_replace('##is', '\'; } echo \'', $text); + $text = preg_replace('#\{([A-z0-9]*)\}#is', '\'.$this->tpl_strings[\'\\1\'].\'', $text); + for($i = 0; $i < sizeof($m[1]); $i++) + { + $text = str_replace("{PHPCODE:{$i}:{$seed}}", "'; {$m[1][$i]} echo '", $text); + } + if(is_writable(ENANO_ROOT.'/cache/') && getConfig('cache_thumbs')=='1') + { + //die($tpl_filename); + $h = fopen($tpl_filename, 'w'); + if(!$h) return $text; + $t = addslashes($text); + fwrite($h, ''); + fclose($h); + } + return $text; //('
'.htmlspecialchars($text).'
'); + } + + function compile_template_text($text) { + $seed = md5 ( microtime() . mt_rand() ); + preg_match_all("/<\?php(.*?)\?>/is", $text, $m); + //die('
'.htmlspecialchars(print_r($m, true)).'
'); + for($i = 0; $i < sizeof($m[1]); $i++) + { + $text = str_replace("", "{PHPCODE:{$i}:{$seed}}", $text); + } + //die('
'.htmlspecialchars($text).'
'); + $text = 'ob_start(); echo \''.str_replace('\'', '\\\'', $text).'\'; $tpl_code = ob_get_contents(); ob_end_clean(); return $tpl_code;'; + $text = preg_replace('##is', '\'; if(isset($this->tpl_bool[\'\\1\']) && $this->tpl_bool[\'\\1\']) { echo \'', $text); + $text = preg_replace('##is', '\'; if(isset($this->tpl_strings[\'\\1\'])) { echo \'', $text); + $text = preg_replace('##is', '\'; if(getConfig(\'plugin_\\1\')==\'1\') { echo \'', $text); + $text = preg_replace('##is', '\'; echo $template->tplWikiFormat($paths->sysMsg(\'\\1\')); echo \'', $text); + $text = preg_replace('##is', '\'; if(!$this->tpl_bool[\'\\1\']) { echo \'', $text); + $text = preg_replace('##is', '\'; } else { echo \'', $text); + $text = preg_replace('##is', '\'; } echo \'', $text); + $text = preg_replace('#\{([A-z0-9]*)\}#is', '\'.$this->tpl_strings[\'\\1\'].\'', $text); + for($i = 0; $i < sizeof($m[1]); $i++) + { + $text = str_replace("{PHPCODE:{$i}:{$seed}}", "'; {$m[1][$i]} echo '", $text); + } + return $text; //('
'.htmlspecialchars($text).'
'); + } + + function parse($text) + { + $text = $this->compile_template_text($text); + return eval($text); + } + + // Steps to turn this: + // [[Project:Community Portal]] + // into this: + // Community Portal + // Must be done WITHOUT creating eval'ed code!!! + + // 1. preg_replace \[\[([a-zA-Z0-9 -_:]*?)\]\] with \\1 + // 2. preg_match_all + // 3. For each match, replace matches with identifiers + // 4. For each match, str_replace ' ' with '_' + // 5. For each match, str_replace match_id:random_val with $matches[$match_id] + + // The template language is really a miniature programming language; with variables, conditionals, everything! + // So you can implement custom logic into your sidebar if you wish. + // "Real" PHP support coming soon :-D + + function tplWikiFormat($message, $filter_links = false, $filename = 'elements.tpl') { + global $db, $session, $paths, $template, $plugins; // Common objects + $filter_links = false; + $tplvars = $this->extract_vars($filename); + if($session->sid_super) $as = htmlspecialchars(urlSeparator).'auth='.$session->sid_super; + else $as = ''; + error_reporting(E_ALL); + $random_id = sha1(microtime().''); // A temp value + + /* + * PREPROCESSOR + */ + + // Variables + + preg_match_all('#\$([A-Z_-]+)\$#', $message, $links); + $links = $links[1]; + + for($i=0;$itpl_strings[$links[$i]], $message); + } + + // Conditionals + + preg_match_all('#\{if ([A-Za-z0-9_ &\|\!-]*)\}(.*?)\{\/if\}#is', $message, $links); + + for($i=0;$itpl_bool['that']) && $this->tpl_bool['that'] ) && ... + // Method of attack: escape all variables, ignore all else. Non-valid code is filtered out by a regex above. + $in_var_now = true; + $in_var_last = false; + $current_var = ''; + $current_var_start_pos = 0; + $current_var_end_pos = 0; + $j = -1; + $links[1][$i] = $links[1][$i] . ' '; + $d = strlen($links[1][$i]); + while($j < $d) + { + $j++; + $in_var_last = $in_var_now; + + $char = substr($links[1][$i], $j, 1); + $in_var_now = ( preg_match('#^([A-z0-9_]*){1}$#', $char) ) ? true : false; + if(!$in_var_last && $in_var_now) + { + $current_var_start_pos = $j; + } + if($in_var_last && !$in_var_now) + { + $current_var_end_pos = $j; + } + if($in_var_now) + { + $current_var .= $char; + continue; + } + // OK we are not inside of a variable. That means that we JUST hit the end because the counter ($j) will be advanced to the beginning of the next variable once processing here is complete. + if($char != ' ' && $char != '(' && $char != ')' && $char != 'A' && $char != 'N' && $char != 'D' && $char != 'O' && $char != 'R' && $char != '&' && $char != '|' && $char != '!' && $char != '<' && $char != '>' && $char != '0' && $char != '1' && $char != '2' && $char != '3' && $char != '4' && $char != '5' && $char != '6' && $char != '7' && $char != '8' && $char != '9') + { + // XSS attack! Bail out + echo '

Error: Syntax error (possibly XSS attack) caught in template code:

'; + echo '
';
+          echo '{if '.$links[1][$i].'}';
+          echo "\n    ";
+          for($k=0;$k<$j;$k++) echo " ";
+          echo '^';
+          echo '
'; + continue 2; + } + if($current_var != '') + { + $cd = '( isset($this->tpl_bool[\''.$current_var.'\']) && $this->tpl_bool[\''.$current_var.'\'] )'; + $cvt = substr($links[1][$i], 0, $current_var_start_pos) . $cd . substr($links[1][$i], $current_var_end_pos, strlen($links[1][$i])); + $j = $j + strlen($cd) - strlen($current_var); + $current_var = ''; + $links[1][$i] = $cvt; + $d = strlen($links[1][$i]); + } + } + $links[1][$i] = substr($links[1][$i], 0, strlen($links[1][$i])-1); + $links[1][$i] = '$chk = ( '.$links[1][$i].' ) ? true : false;'; + eval($links[1][$i]); + + if($chk) { // isset($this->tpl_bool[$links[1][$i]]) && $this->tpl_bool[$links[1][$i]] + if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], 0, strpos($links[2][$i], '{else}')); + else $c = $links[2][$i]; + $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message); + } else { + if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], strpos($links[2][$i], '{else}')+6, strlen($links[2][$i])); + else $c = ''; + $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message); + } + } + + preg_match_all('#\{!if ([A-Za-z_-]*)\}(.*?)\{\/if\}#is', $message, $links); + + for($i=0;$itpl_bool[$links[1][$i]]) && $this->tpl_bool[$links[1][$i]]) { + if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], strpos($links[2][$i], '{else}')+6, strlen($links[2][$i])); + else $c = ''; + $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message); + } else { + if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], 0, strpos($links[2][$i], '{else}')); + else $c = $links[2][$i]; + $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message); + } + } + + /* + * HTML RENDERER + */ + + // Images + $j = preg_match_all('#\[\[:'.$paths->nslist['File'].'([\w\s0-9_\(\)!@%\^\+\|\.-]+?)\]\]#is', $message, $matchlist); + $matches = Array(); + $matches['images'] = $matchlist[1]; + for($i=0;$inslist['File'].$matches['images'][$i])) + { + $message = str_replace('[[:'.$paths->nslist['File'].$matches['images'][$i].']]', + ''.$matches['images'][$i].'', + $message); + } + } + + // Internal links + + $text_parser = $this->makeParserText($tplvars['sidebar_button']); + + preg_match_all('#\[\[([a-zA-Z0-9 -_]*?)\]\]#is', $message, $il); + for($i=0;$iassign_vars(Array( + 'HREF' => $href, + 'FLAGS' => '', + 'TEXT' => $il[1][$i] + )); + $message = str_replace("[[{$il[1][$i]}]]", $text_parser->run(), $message); + } + + preg_match_all('#\[\[([a-zA-Z0-9 -_]*?)\|([a-zA-Z0-9!@\#\$%\^&\*\(\)\{\} -_]*?)\]\]#is', $message, $il); + for($i=0;$iassign_vars(Array( + 'HREF' => $href, + 'FLAGS' => '', + 'TEXT' => $il[2][$i] + )); + $message = str_replace("[[{$il[1][$i]}|{$il[2][$i]}]]", $text_parser->run(), $message); + } + + // External links + $message = preg_replace('#\[(http|ftp|irc):\/\/([a-z0-9\/:_\.\?&%\#@_\\\\-]+?)\\ ([^\]]+)]#', '
\\3
', $message); + $message = preg_replace('#\[(http|ftp|irc):\/\/([a-z0-9\/:_\.\?&%\#@_\\\\-]+?)\\]#', '\\1://\\2
', $message); + + $parser1 = $this->makeParserText($tplvars['sidebar_section']); + $parser2 = $this->makeParserText($tplvars['sidebar_section_raw']); + + preg_match_all('#\{slider(2|)=(.*?)\}(.*?)\{\/slider(2|)\}#is', $message, $sb); + + // Modified to support the sweet new template var system + for($i=0;$iassign_vars(Array('TITLE'=>$sb[2][$i],'CONTENT'=>$sb[3][$i])); + $message = str_replace("{slider{$sb[1][$i]}={$sb[2][$i]}}{$sb[3][$i]}{/slider{$sb[4][$i]}}", $p->run(), $message); + } + + /* + Extras ;-) + $message = preg_replace('##is', '', $message); + $message = preg_replace('##is', '', $message); + $message = preg_replace('##is', '', $message); + $message = preg_replace('##is', '', $message); + $message = preg_replace('##is', '', $message); + */ + + //die('
'.htmlspecialchars($message).'
'); + //eval($message); exit; + return $message; + } + + /** + * Print a text field that auto-completes a username entered into it. + * @param string $name - the name of the form field + * @return string + */ + + function username_field($name, $value = false) + { + $randomid = md5( time() . microtime() . mt_rand() ); + $text = ''; + return $text; + } + + /** + * Sends a textarea that can be converted to and from a TinyMCE widget on the fly. + * @param string The name of the form element + * @param string The initial content. Optional, defaults to blank + * @param int Rows in textarea + * @param int Columns in textarea + * @return string HTML and Javascript code. + */ + + function tinymce_textarea($name, $content = '', $rows = 20, $cols = 60) + { + $randomid = md5(microtime() . mt_rand()); + $html = ''; + $html .= ''; + $html .= '
text editor  |  graphical editor
'; + $html .= ''; + return $html; + } + + /** + * Allows individual parsing of template files. Similar to phpBB but follows the spirit of object-oriented programming ;) + * Returns on object of class templateIndividual. Usage instructions can be found in the inline docs for that class. + * @param $filename the filename of the template to be parsed + * @return object + */ + + function makeParser($filename) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $filename = ENANO_ROOT.'/themes/'.$template->theme.'/'.$filename; + if(!file_exists($filename)) die('templateIndividual: file '.$filename.' does not exist'); + $code = file_get_contents($filename); + $parser = new templateIndividual($code); + return $parser; + } + + /** + * Same as $template->makeParser(), but takes a string instead of a filename. + * @param $text the text to parse + * @return object + */ + + function makeParserText($code) + { + $parser = new templateIndividual($code); + return $parser; + } + + /** + * Fetch the HTML for a plugin-added sidebar block + * @param $name the plugin name + * @return string + */ + + function fetch_block($id) + { + if(isset($this->plugin_blocks[$id])) return $this->plugin_blocks[$id]; + else return false; + } + + /** + * Fetches the contents of both sidebars. + * @return array - key 0 is left, key 1 is right + * @example list($left, $right) = $template->fetch_sidebar(); + */ + + function fetch_sidebar() + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $left = ''; + $right = ''; + + if ( !$this->fetch_block('Links') ) + $this->initLinksWidget(); + + $q = $db->sql_query('SELECT item_id,sidebar_id,block_name,block_type,block_content FROM '.table_prefix.'sidebar WHERE item_enabled=1 ORDER BY sidebar_id ASC, item_order ASC;'); + if(!$q) $db->_die('The sidebar text data could not be selected.'); + + $vars = $this->extract_vars('elements.tpl'); + + if(isset($vars['sidebar_top'])) + { + $left .= $this->parse($vars['sidebar_top']); + $right .= $this->parse($vars['sidebar_top']); + } + while($row = $db->fetchrow()) + { + switch($row['block_type']) + { + case BLOCK_WIKIFORMAT: + default: + $parser = $this->makeParserText($vars['sidebar_section']); + $c = RenderMan::render($row['block_content']); + break; + case BLOCK_TEMPLATEFORMAT: + $parser = $this->makeParserText($vars['sidebar_section']); + $c = $this->tplWikiFormat($row['block_content']); + break; + case BLOCK_HTML: + $parser = $this->makeParserText($vars['sidebar_section_raw']); + $c = $row['block_content']; + break; + case BLOCK_PHP: + $parser = $this->makeParserText($vars['sidebar_section_raw']); + ob_start(); + @eval($row['block_content']); + $c = ob_get_contents(); + ob_end_clean(); + break; + case BLOCK_PLUGIN: + $parser = $this->makeParserText($vars['sidebar_section_raw']); + $c = (gettype($this->fetch_block($row['block_content'])) == 'string') ? $this->fetch_block($row['block_content']) : 'Can\'t find plugin block'; + break; + } + $parser->assign_vars(Array( 'TITLE'=>$this->tplWikiFormat($row['block_name']), 'CONTENT'=>$c )); + if ($row['sidebar_id'] == SIDEBAR_LEFT ) $left .= $parser->run(); + elseif($row['sidebar_id'] == SIDEBAR_RIGHT) $right .= $parser->run(); + unset($parser); + } + $db->free_result(); + if(isset($vars['sidebar_bottom'])) + { + $left .= $this->parse($vars['sidebar_bottom']); + $right .= $this->parse($vars['sidebar_bottom']); + } + $min = ''; + if(isset($vars['sidebar_top'])) + { + $min .= $this->parse($vars['sidebar_top']); + } + if(isset($vars['sidebar_bottom'])) + { + $min .= $this->parse($vars['sidebar_bottom']); + } + return Array($left, $right, $min); + } + + function initLinksWidget() + { + global $db, $session, $paths, $template, $plugins; // Common objects + // SourceForge/W3C buttons + $ob = Array(); + if(getConfig('powered_btn') =='1') $ob[] = 'Powered by Enano'; + if(getConfig('sflogo_enabled')=='1') + { + $ob[] = 'SourceForge.net Logo'; + } + if(getConfig('w3c_v32') =='1') $ob[] = 'Valid HTML 3.2'; + if(getConfig('w3c_v40') =='1') $ob[] = 'Valid HTML 4.0'; + if(getConfig('w3c_v401') =='1') $ob[] = 'Valid HTML 4.01'; + if(getConfig('w3c_vxhtml10')=='1') $ob[] = 'Valid XHTML 1.0'; + if(getConfig('w3c_vxhtml11')=='1') $ob[] = 'Valid XHTML 1.1'; + if(getConfig('w3c_vcss') =='1') $ob[] = 'Valid CSS'; + if(getConfig('dbd_button') =='1') $ob[] = 'DRM technology restricts what you can do with your computer
Protect your freedom >>
'; + + $code = $plugins->setHook('links_widget'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + if(count($ob) > 0) $sb_links = '
'.implode('
', $ob).'
'; + else $sb_links = ''; + + $this->sidebar_widget('Links', $sb_links); + } + + /** + * Builds a box showing unread private messages. + */ + + function notify_unread_pms() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if ( ( $paths->cpage['urlname_nons'] == 'PrivateMessages' || $paths->cpage['urlname_nons'] == 'Preferences' ) && $paths->namespace == 'Special' ) + { + return ''; + } + $ob = '
'."\n"; + $s = ( $session->unread_pms == 1 ) ? '' : 's'; + $ob .= " You have $session->unread_pms unread private message$s.
\n Messages: "; + $q = $db->sql_query('SELECT message_id,message_from,subject,date FROM '.table_prefix.'privmsgs WHERE message_to=\'' . $session->username . '\' AND message_read=0 ORDER BY date DESC;'); + if ( !$q ) + $db->_die(); + $messages = array(); + while ( $row = $db->fetchrow() ) + { + $messages[] = '' . $row['subject'] . ''; + } + $ob .= implode(",\n " , $messages)."\n"; + $ob .= '
'."\n"; + return $ob; + } + +} // class template + +/** + * Handles parsing of an individual template file. Instances should only be created through $template->makeParser(). To use: + * - Call $template->makeParser(template file name) - file name should be something.tpl, css/whatever.css, etc. + * - Make an array of strings you want the template to access. $array['STRING'] would be referenced in the template like {STRING} + * - Make an array of boolean values. These can be used for conditionals in the template ( whatever ) + * - Call assign_vars() to pass the strings to the template parser. Same thing with assign_bool(). + * - Call run() to parse the template and get your fully compiled HTML. + * @access private + */ + +class templateIndividual extends template { + var $tpl_strings, $tpl_bool, $tpl_code; + var $compiled = false; + /** + * Constructor. + */ + function __construct($text) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $this->tpl_code = $text; + $this->tpl_strings = $template->tpl_strings; + $this->tpl_bool = $template->tpl_bool; + } + /** + * PHP 4 constructor. + */ + function templateIndividual($text) + { + $this->__construct($text); + } + /** + * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file. + * @param $vars array + */ + function assign_vars($vars) + { + $this->tpl_strings = array_merge($this->tpl_strings, $vars); + } + /** + * Assigns an array of boolean values to the template. These can be used for statements. + * @param $vars array + */ + function assign_bool($vars) + { + $this->tpl_bool = array_merge($this->tpl_bool, $vars); + } + /** + * Compiles and executes the template code. + * @return string + */ + function run() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$this->compiled) + { + $this->tpl_code = $this->compile_template_text($this->tpl_code); + $this->compiled = true; + } + return eval($this->tpl_code); + } +} + +/** + * A version of the template compiler that does not rely at all on the other parts of Enano. Used during installation and for showing + * "critical error" messages. ** REQUIRES ** the Oxygen theme. + */ + +class template_nodb { + var $tpl_strings, $tpl_bool, $theme, $style, $no_headers, $additional_headers, $sidebar_extra, $sidebar_widgets, $toolbar_menu, $theme_list; + function __construct() { + + $this->tpl_bool = Array(); + $this->tpl_strings = Array(); + $this->sidebar_extra = ''; + $this->sidebar_widgets = ''; + $this->toolbar_menu = ''; + $this->additional_headers = ''; + + $this->theme_list = Array(Array( + 'theme_id'=>'oxygen', + 'theme_name'=>'Oxygen', + 'theme_order'=>1, + 'enabled'=>1, + )); + } + function template() { + $this->__construct(); + } + function get_css($s = false) { + if($s) + return $this->process_template('css/'.$s); + else + return $this->process_template('css/'.$this->style.'.css'); + } + function load_theme($name, $css, $auto_init = true) { + $this->theme = $name; + $this->style = $css; + + $this->tpl_strings['SCRIPTPATH'] = scriptPath; + if ( $auto_init ) + $this->init_vars(); + } + function init_vars() + { + global $sideinfo; + global $this_page; + global $db, $session, $paths, $template, $plugins; // Common objects + $tplvars = $this->extract_vars('elements.tpl'); + $tb = ''; + // Get the "article" button text (depends on namespace) + if(defined('IN_ENANO_INSTALL')) $ns = 'installation page'; + else $ns = 'system error page'; + $t = str_replace('{FLAGS}', 'onclick="return false;" title="Hey! A button that doesn\'t do anything. Clever..." accesskey="a"', $tplvars['toolbar_button']); + $t = str_replace('{HREF}', '#', $t); + $t = str_replace('{TEXT}', $ns, $t); + $tb .= $t; + + // Page toolbar + + $this->tpl_bool = Array( + 'auth_admin'=>true, + 'user_logged_in'=>true, + 'right_sidebar'=>false, + ); + $this->tpl_bool['in_sidebar_admin'] = false; + + $this->tpl_bool['auth_rename'] = false; + + $asq = $asa = ''; + + $this->tpl_bool['fixed_menus'] = false; + $slink = defined('IN_ENANO_INSTALL') ? scriptPath.'/install.php?mode=css' : makeUrlNS('Special', 'CSS'); + + $title = ( is_object($paths) ) ? $paths->page : 'Critical error'; + + // The rewritten template engine will process all required vars during the load_template stage instead of (cough) re-processing everything each time around. + $tpl_strings = Array( + 'PAGE_NAME'=>$this_page, + 'PAGE_URLNAME'=>'Null', + 'SITE_NAME'=>'Enano Installation', + 'USERNAME'=>'admin', + 'SITE_DESC'=>'Install Enano on your server.', + 'TOOLBAR'=>$tb, + 'SCRIPTPATH'=>scriptPath, + 'CONTENTPATH'=>contentPath, + 'ADMIN_SID_QUES'=>$asq, + 'ADMIN_SID_AMP'=>$asa, + 'ADMIN_SID_AMP_HTML'=>'', + 'ADDITIONAL_HEADERS'=>'', + 'SIDEBAR_EXTRA'=>'', + 'COPYRIGHT'=>'Enano and all of its code, graphics, and more code is copyright © 2006 Dan Fuhry.
This program is Free Software; see the file "GPL" included with this package for details.', + 'TOOLBAR_EXTRAS'=>'', + 'REQUEST_URI'=>$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'], + 'STYLE_LINK'=>$slink, + 'LOGOUT_LINK'=>'', + 'THEME_LINK'=>'', + 'TEMPLATE_DIR'=>scriptPath.'/themes/'.$this->theme, + 'THEME_ID'=>$this->theme, + 'STYLE_ID'=>$this->style, + 'JS_DYNAMIC_VARS'=>'', + 'SIDEBAR_RIGHT'=>'', + ); + $this->tpl_strings = array_merge($tpl_strings, $this->tpl_strings); + + $sidebar = ( gettype($sideinfo) == 'string' ) ? $sideinfo : ''; + if($sidebar != '') + { + if(isset($tplvars['sidebar_top'])) + { + $text = $this->makeParserText($tplvars['sidebar_top']); + $top = $text->run(); + } else { + $top = ''; + } + $p = $this->makeParserText($tplvars['sidebar_section']); + $p->assign_vars(Array( + 'TITLE'=>'Installation progress', + 'CONTENT'=>$sidebar, + )); + $sidebar = $p->run(); + if(isset($tplvars['sidebar_bottom'])) + { + $text = $this->makeParserText($tplvars['sidebar_bottom']); + $bottom = $text->run(); + } else { + $bottom = ''; + } + $sidebar = $top . $sidebar . $bottom; + } + $this->tpl_strings['SIDEBAR_LEFT'] = $sidebar; + + $this->tpl_bool['sidebar_left'] = ( $this->tpl_strings['SIDEBAR_LEFT'] != '') ? true : false; + $this->tpl_bool['sidebar_right'] = ( $this->tpl_strings['SIDEBAR_RIGHT'] != '') ? true : false; + $this->tpl_bool['right_sidebar'] = $this->tpl_bool['sidebar_right']; // backward compatibility + $this->tpl_bool['stupid_mode'] = true; + } + function header() + { + if(!$this->no_headers) echo $this->process_template('header.tpl'); + } + function footer() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$this->no_headers) { + global $_starttime; + $f = microtime(true); + $f = $f - $_starttime; + $f = round($f, 4); + if(defined('IN_ENANO_INSTALL')) $nq = 'N/A'; + else $nq = $db->num_queries; + if($nq == 0) $nq = 'N/A'; + $dbg = 'Time: '.$f.'s | Queries: '.$nq; + $t = $this->process_template('footer.tpl'); + $t = str_replace('[[Stats]]', $dbg, $t); + echo $t; + } + else return ''; + } + function getHeader() + { + if(!$this->no_headers) return $this->process_template('header.tpl'); + else return ''; + } + function getFooter() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$this->no_headers) { + global $_starttime; + $f = microtime(true); + $f = $f - $_starttime; + $f = round($f, 4); + if(defined('IN_ENANO_INSTALL')) $nq = 'N/A'; + else $nq = $db->num_queries; + if($nq == 0) $nq = 'N/A'; + $dbg = 'Time: '.$f.'s | Queries: '.$nq; + if($nq == 0) $nq = 'N/A'; + $t = $this->process_template('footer.tpl'); + $t = str_replace('[[Stats]]', $dbg, $t); + return $t; + } + else return ''; + } + + function process_template($file) { + + eval($this->compile_template($file)); + return $tpl_code; + } + + function extract_vars($file) { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!is_file(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file)) die('Cannot find '.$file.' file for style "'.$this->theme.'", exiting'); + $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file); + preg_match_all('#<\!-- VAR ([A-z0-9_-]*) -->(.*?)<\!-- ENDVAR \\1 -->#is', $text, $matches); + $tplvars = Array(); + for($i=0;$itheme.'/'.$text); + $text = str_replace('', '', $text); // Remove the AJAX code - we don't need it, and it requires a database connection + $text = '$tpl_code = \''.str_replace('\'', '\\\'', $text).'\'; return $tpl_code;'; + $text = preg_replace('##is', '\'; if($this->tpl_bool[\'\\1\']) { $tpl_code .= \'', $text); + $text = preg_replace('##is', '\'; if(getConfig(\'plugin_\\1\')==\'1\') { $tpl_code .= \'', $text); + if(defined('IN_ENANO_INSTALL')) $text = str_replace('', '', $text); + else $text = str_replace('', '', $text); + $text = preg_replace('##is', '', $text); + $text = preg_replace('##is', '\'; if(!$this->tpl_bool[\'\\1\']) { $tpl_code .= \'', $text); + $text = preg_replace('##is', '\'; } else { $tpl_code .= \'', $text); + $text = preg_replace('##is', '\'; } $tpl_code .= \'', $text); + $text = preg_replace('#{([A-z0-9]*)}#is', '\'.$this->tpl_strings[\'\\1\'].\'', $text); + return $text; //('
'.htmlspecialchars($text).'
'); + } + + function compile_template_text($text) { + global $sideinfo; + $text = str_replace('', '', $text); // Remove the AJAX code - we don't need it, and it requires a database connection + $text = '$tpl_code = \''.str_replace('\'', '\\\'', $text).'\'; return $tpl_code;'; + $text = preg_replace('##is', '\'; if($this->tpl_bool[\'\\1\']) { $tpl_code .= \'', $text); + $text = preg_replace('##is', '\'; if(getConfig(\'plugin_\\1\')==\'1\') { $tpl_code .= \'', $text); + if(defined('IN_ENANO_INSTALL')) $text = str_replace('', '', $text); + else $text = str_replace('', '', $text); + $text = preg_replace('##is', '', $text); + $text = preg_replace('##is', '\'; if(!$this->tpl_bool[\'\\1\']) { $tpl_code .= \'', $text); + $text = preg_replace('##is', '\'; } else { $tpl_code .= \'', $text); + $text = preg_replace('##is', '\'; } $tpl_code .= \'', $text); + $text = preg_replace('#{([A-z0-9]*)}#is', '\'.$this->tpl_strings[\'\\1\'].\'', $text); + return $text; //('
'.htmlspecialchars($text).'
'); + } + + /** + * Allows individual parsing of template files. Similar to phpBB but follows the spirit of object-oriented programming ;) + * Returns on object of class templateIndividual. Usage instructions can be found in the inline docs for that class. + * @param $filename the filename of the template to be parsed + * @return object + */ + + function makeParser($filename) + { + $filename = ENANO_ROOT.'/themes/'.$this->theme.'/'.$filename; + if(!file_exists($filename)) die('templateIndividual: file '.$filename.' does not exist'); + $code = file_get_contents($filename); + $parser = new templateIndividualSafe($code, $this); + return $parser; + } + + /** + * Same as $template->makeParser(), but takes a string instead of a filename. + * @param $text the text to parse + * @return object + */ + + function makeParserText($code) + { + $parser = new templateIndividualSafe($code, $this); + return $parser; + } + +} // class template_nodb + +/** + * Identical to templateIndividual, except extends template_nodb instead of template + * @see class template + */ + +class templateIndividualSafe extends template_nodb { + var $tpl_strings, $tpl_bool, $tpl_code; + var $compiled = false; + /** + * Constructor. + */ + function __construct($text, $parent) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $this->tpl_code = $text; + $this->tpl_strings = $parent->tpl_strings; + $this->tpl_bool = $parent->tpl_bool; + } + /** + * PHP 4 constructor. + */ + function templateIndividual($text) + { + $this->__construct($text); + } + /** + * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file. + * @param $vars array + */ + function assign_vars($vars) + { + if(is_array($this->tpl_strings)) + $this->tpl_strings = array_merge($this->tpl_strings, $vars); + else + $this->tpl_strings = $vars; + } + /** + * Assigns an array of boolean values to the template. These can be used for statements. + * @param $vars array + */ + function assign_bool($vars) + { + $this->tpl_bool = array_merge($this->tpl_bool, $vars); + } + /** + * Compiles and executes the template code. + * @return string + */ + function run() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$this->compiled) + { + $this->tpl_code = $this->compile_template_text($this->tpl_code); + $this->compiled = true; + } + return eval($this->tpl_code); + } +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/template.php~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/template.php~ Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1785 @@ +tpl_bool = Array(); + $this->tpl_strings = Array(); + $this->sidebar_extra = ''; + $this->toolbar_menu = ''; + $this->additional_headers = ''; + $this->plugin_blocks = Array(); + $this->theme_loaded = false; + + $this->theme_list = Array(); + $this->named_theme_list = Array(); + $e = $db->sql_query('SELECT theme_id,theme_name,enabled,default_style FROM '.table_prefix.'themes WHERE enabled=1 ORDER BY theme_order;'); + if(!$e) $db->_die('The list of themes could not be selected.'); + for($i=0;$i < $db->numrows(); $i++) + { + $this->theme_list[$i] = $db->fetchrow(); + $this->named_theme_list[$this->theme_list[$i]['theme_id']] = $this->theme_list[$i]; + } + $db->free_result(); + $this->default_theme = $this->theme_list[0]['theme_id']; + $dir = ENANO_ROOT.'/themes/'.$this->default_theme.'/css/'; + $list = Array(); + // Open a known directory, and proceed to read its contents + if (is_dir($dir)) { + if ($dh = opendir($dir)) { + while (($file = readdir($dh)) !== false) { + if(preg_match('#^(.*?)\.css$#i', $file) && $file != '_printable.css') { + $list[] = substr($file, 0, strlen($file)-4); + } + } + closedir($dh); + } + } + + $def = ENANO_ROOT.'/themes/'.$this->default_theme.'/css/'.$this->named_theme_list[$this->default_theme]['default_style']; + if(file_exists($def)) + { + $this->default_style = substr($this->named_theme_list[$this->default_theme]['default_style'], 0, strlen($this->named_theme_list[$this->default_theme]['default_style'])-4); + } else { + $this->default_style = $list[0]; + } + + $this->style_list = $list; + + } + function template() + { + $this->__construct(); + } + function sidebar_widget($t, $h) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!defined('ENANO_TEMPLATE_LOADED')) + { + $this->load_theme($session->theme, $session->style); + } + if(!$this->sidebar_widgets) + $this->sidebar_widgets = ''; + $tplvars = $this->extract_vars('elements.tpl'); + $parser = $this->makeParserText($tplvars['sidebar_section_raw']); + $parser->assign_vars(Array('TITLE'=>$t,'CONTENT'=>$h)); + $this->plugin_blocks[$t] = $h; + $this->sidebar_widgets .= $parser->run(); + } + function add_header($html) + { + $this->additional_headers .= "\n" . $html; + } + function get_css($s = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!defined('ENANO_TEMPLATE_LOADED')) + $this->load_theme($session->theme, $session->style); + $path = ( $s ) ? 'css/'.$s : 'css/'.$this->style.'.css'; + if ( !file_exists(ENANO_ROOT . '/themes/' . $this->theme . '/' . $path) ) + { + echo "/* WARNING: Falling back to default file because file $path does not exist */\n"; + $path = 'css/' . $this->style_list[0] . '.css'; + } + return $this->process_template($path); + } + function load_theme($name = false, $css = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $this->theme = ( $name ) ? $name : $session->theme; + $this->style = ( $css ) ? $css : $session->style; + if ( !$this->theme ) + { + $this->theme = $this->theme_list[0]['theme_id']; + $this->style = substr($this->theme_list[0]['default_style'], 0, strlen($this->theme_list[0]['default_style'])-4); + } + $this->theme_loaded = true; + } + + function init_vars() + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $email; + + dc_here("template: initializing all variables"); + + if(!$this->theme || !$this->style) + { + $this->load_theme(); + } + + if(defined('ENANO_TEMPLATE_LOADED')) + { + dc_here('template: access denied to call template::init_vars(), bailing out'); + die_semicritical('Illegal call', '

$template->load_theme was called multiple times, this is not supposed to happen. Exiting with fatal error.

'); + } + + define('ENANO_TEMPLATE_LOADED', ''); + + $tplvars = $this->extract_vars('elements.tpl'); + + dc_here('template: setting all template vars'); + + if(isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) + { + $this->add_header(' + + '); + } + + // Get the "article" button text (depends on namespace) + switch($paths->namespace) { + case "Article": + default: + $ns = 'article'; + break; + case "Admin": + $ns = 'administration page'; + break; + case "System": + $ns = 'system message'; + break; + case "File": + $ns = 'uploaded file'; + break; + case "Help": + $ns = 'documentation page'; + break; + case "User": + $ns = 'user page'; + break; + case "Special": + $ns = 'special page'; + break; + case "Template": + $ns = 'template'; + break; + case "Project": + $ns = 'project page'; + break; + case "Category": + $ns = 'category'; + break; + } + $this->namespace_string = $ns; + $code = $plugins->setHook('page_type_string_set'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + $ns =& $this->namespace_string; + + // Initialize the toolbar + $tb = ''; + + // Create "xx page" button + + $btn_selected = ( isset($tplvars['toolbar_button_selected'])) ? $tplvars['toolbar_button_selected'] : $tplvars['toolbar_button']; + $parser = $this->makeParserText($btn_selected); + + $parser->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxReset()); return false;" title="View the page contents, all of the page contents, and nothing but the page contents (alt-a)" accesskey="a"', + 'PARENTFLAGS' => 'id="mdgToolbar_article"', + 'HREF' => makeUrl($paths->page, null, true), + 'TEXT' => $this->namespace_string + )); + + $tb .= $parser->run(); + + $button = $this->makeParserText($tplvars['toolbar_button']); + + // Page toolbar + // Comments button + if ( $session->get_permissions('read') && getConfig('enable_comments')=='1' && $paths->namespace != 'Special' && $paths->namespace != 'Admin' && $paths->cpage['comments_on'] == 1 ) + { + + $e = $db->sql_query('SELECT approved FROM '.table_prefix.'comments WHERE page_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\''.$paths->namespace.'\';'); + if ( !$e ) + { + $db->_die(); + } + $nc = $db->numrows(); + $nu = 0; + $na = 0; + + while ( $r = $db->fetchrow() ) + { + if ( !$r['approved'] ) + { + $nu++; + } + else + { + $na++; + } + } + + $db->free_result(); + $n = ( $session->get_permissions('mod_comments') ) ? (string)$nc : (string)$na; + if ( $session->get_permissions('mod_comments') && $nu > 0 ) + { + $n .= ' total/'.$nu.' unapp.'; + } + + $button->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxComments()); return false;" title="View the comments that other users have posted about this page (alt-c)" accesskey="c"', + 'PARENTFLAGS' => 'id="mdgToolbar_discussion"', + 'HREF' => makeUrl($paths->page, 'do=comments', true), + 'TEXT' => 'discussion ('.$n.')', + )); + + $tb .= $button->run(); + } + // Edit button + if($session->get_permissions('read') && ($paths->namespace != 'Special' && $paths->namespace != 'Admin') && ( $session->get_permissions('edit_page') && ( ( $paths->page_protected && $session->get_permissions('even_when_protected') ) || !$paths->page_protected ) ) ) + { + $button->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxEditor()); return false;" title="Edit the contents of this page (alt-e)" accesskey="e"', + 'PARENTFLAGS' => 'id="mdgToolbar_edit"', + 'HREF' => makeUrl($paths->page, 'do=edit', true), + 'TEXT' => 'edit this page' + )); + $tb .= $button->run(); + // View source button + } + else if($session->get_permissions('view_source') && ( !$session->get_permissions('edit_page') || !$session->get_permissions('even_when_protected') && $paths->page_protected ) && $paths->namespace != 'Special' && $paths->namespace != 'Admin') + { + $button->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxViewSource()); return false;" title="View the source code (wiki markup) that this page uses (alt-e)" accesskey="e"', + 'PARENTFLAGS' => 'id="mdgToolbar_edit"', + 'HREF' => makeUrl($paths->page, 'do=viewsource', true), + 'TEXT' => 'view source' + )); + $tb .= $button->run(); + } + // History button + if ( $session->get_permissions('read') /* && $paths->wiki_mode */ && $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin' && $session->get_permissions('history_view') ) + { + $button->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxHistory()); return false;" title="View a log of actions taken on this page (alt-h)" accesskey="h"', + 'PARENTFLAGS' => 'id="mdgToolbar_history"', + 'HREF' => makeUrl($paths->page, 'do=history', true), + 'TEXT' => 'history' + )); + $tb .= $button->run(); + } + + $menubtn = $this->makeParserText($tplvars['toolbar_menu_button']); + + // Additional actions menu + // Rename button + if ( $session->get_permissions('read') && $paths->page_exists && ( $session->get_permissions('rename') && ( $paths->page_protected && $session->get_permissions('even_when_protected') || !$paths->page_protected ) ) && $paths->namespace != 'Special' && $paths->namespace != 'Admin' ) + { + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxRename()); return false;" title="Change the display name of this page (alt-r)" accesskey="r"', + 'HREF' => makeUrl($paths->page, 'do=rename', true), + 'TEXT' => 'rename', + )); + $this->toolbar_menu .= $menubtn->run(); + } + + // Vote-to-delete button + if ( $paths->wiki_mode && $session->get_permissions('vote_delete') && $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin') + { + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxDelVote()); return false;" title="Vote to have this page deleted (alt-d)" accesskey="d"', + 'HREF' => makeUrl($paths->page, 'do=delvote', true), + 'TEXT' => 'vote to delete this page', + )); + $this->toolbar_menu .= $menubtn->run(); + } + + // Clear-votes button + if ( $session->get_permissions('read') && $paths->wiki_mode && $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin' && $session->get_permissions('vote_reset') && $paths->cpage['delvotes'] > 0) + { + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxResetDelVotes()); return false;" title="Vote to have this page deleted (alt-y)" accesskey="y"', + 'HREF' => makeUrl($paths->page, 'do=resetvotes', true), + 'TEXT' => 'reset deletion votes', + )); + $this->toolbar_menu .= $menubtn->run(); + } + + // Printable page button + if ( $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin' ) + { + $menubtn->assign_vars(array( + 'FLAGS' => 'title="View a version of this page that is suitable for printing"', + 'HREF' => makeUrl($paths->page, 'printable=yes', true), + 'TEXT' => 'view printable version', + )); + $this->toolbar_menu .= $menubtn->run(); + } + + // Protect button + if($session->get_permissions('read') && $paths->wiki_mode && $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin' && $session->get_permissions('protect')) + { + + $label = $this->makeParserText($tplvars['toolbar_label']); + $label->assign_vars(array('TEXT' => 'protection:')); + $t0 = $label->run(); + + $ctmp = ''; + if ( $paths->cpage['protected'] == 1 ) + { + $ctmp=' style="text-decoration: underline;"'; + } + $menubtn->assign_vars(array( + 'FLAGS' => 'accesskey="i" onclick="ajaxProtect(1); return false;" id="protbtn_1" title="Prevents all non-administrators from editing this page. [alt-i]"'.$ctmp, + 'HREF' => makeUrl($paths->page, 'do=protect&level=1', true), + 'TEXT' => 'on' + )); + $t1 = $menubtn->run(); + + $ctmp = ''; + if ( $paths->cpage['protected'] == 0 ) + { + $ctmp=' style="text-decoration: underline;"'; + } + $menubtn->assign_vars(array( + 'FLAGS' => 'accesskey="o" onclick="ajaxProtect(0); return false;" id="protbtn_0" title="Allows everyone to edit this page. [alt-o]"'.$ctmp, + 'HREF' => makeUrl($paths->page, 'do=protect&level=0', true), + 'TEXT' => 'off' + )); + $t2 = $menubtn->run(); + + $ctmp = ''; + if ( $paths->cpage['protected'] == 2 ) + { + $ctmp = ' style="text-decoration: underline;"'; + } + $menubtn->assign_vars(array( + 'FLAGS' => 'accesskey="p" onclick="ajaxProtect(2); return false;" id="protbtn_2" title="Allows only users who have been registered for 4 days to edit this page. [alt-p]"'.$ctmp, + 'HREF' => makeUrl($paths->page, 'do=protect&level=2', true), + 'TEXT' => 'semi' + )); + $t3 = $menubtn->run(); + + $this->toolbar_menu .= ' + + + + + + +
'.$t0.''.$t1.''.$t2.''.$t3.'
'; + } + + // Wiki mode button + if($session->get_permissions('read') && $paths->page_exists && $session->get_permissions('set_wiki_mode') && $paths->namespace != 'Special' && $paths->namespace != 'Admin') + { + // label at start + $label = $this->makeParserText($tplvars['toolbar_label']); + $label->assign_vars(array('TEXT' => 'page wiki mode:')); + $t0 = $label->run(); + + // on button + $ctmp = ''; + if ( $paths->cpage['wiki_mode'] == 1 ) + { + $ctmp = ' style="text-decoration: underline;"'; + } + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="ajaxSetWikiMode(1); return false;" id="wikibtn_1" title="Forces wiki functions to be allowed on this page."'.$ctmp, + 'HREF' => makeUrl($paths->page, 'do=setwikimode&level=1', true), + 'TEXT' => 'on' + )); + $t1 = $menubtn->run(); + + // off button + $ctmp = ''; + if ( $paths->cpage['wiki_mode'] == 0 ) + { + $ctmp=' style="text-decoration: underline;"'; + } + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="ajaxSetWikiMode(0); return false;" id="wikibtn_0" title="Forces wiki functions to be disabled on this page."'.$ctmp, + 'HREF' => makeUrl($paths->page, 'do=setwikimode&level=0', true), + 'TEXT' => 'off' + )); + $t2 = $menubtn->run(); + + // global button + $ctmp = ''; + if ( $paths->cpage['wiki_mode'] == 2 ) + { + $ctmp=' style="text-decoration: underline;"'; + } + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="ajaxSetWikiMode(2); return false;" id="wikibtn_2" title="Causes this page to use the global wiki mode setting (default)"'.$ctmp, + 'HREF' => makeUrl($paths->page, 'do=setwikimode&level=2', true), + 'TEXT' => 'global' + )); + $t3 = $menubtn->run(); + + // Tack it onto the list of buttons that are already there... + $this->toolbar_menu .= ' + + + + + + +
'.$t0.''.$t1.''.$t2.''.$t3.'
'; + } + + // Clear logs button + if ( $session->get_permissions('read') && $session->get_permissions('clear_logs') && $paths->wiki_mode && $paths->namespace != 'Special' && $paths->namespace != 'Admin' ) + { + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxClearLogs()); return false;" title="Remove all edit and action logs for this page from the database. IRREVERSIBLE! (alt-l)" accesskey="l"', + 'HREF' => makeUrl($paths->page, 'do=flushlogs', true), + 'TEXT' => 'clear page logs', + )); + $this->toolbar_menu .= $menubtn->run(); + } + + // Delete page button + if ( $session->get_permissions('read') && $session->get_permissions('delete_page') && $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin' ) + { + $s = 'delete this page'; + if ( $paths->cpage['delvotes'] == 1 ) + { + $s .= ' ('.$paths->cpage['delvotes'].' vote)'; + } + else if ( $paths->cpage['delvotes'] > 1 ) + { + $s .= ' ('.$paths->cpage['delvotes'].' votes)'; + } + + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxDeletePage()); return false;" title="Delete this page. This is always reversible unless the logs are cleared. (alt-k)" accesskey="k"', + 'HREF' => makeUrl($paths->page, 'do=deletepage', true), + 'TEXT' => $s, + )); + $this->toolbar_menu .= $menubtn->run(); + + } + + // Password-protect button + if(isset($paths->cpage['password'])) + { + if ( $paths->cpage['password'] == '' ) + { + $a = $session->get_permissions('password_set'); + } + else + { + $a = $session->get_permissions('password_reset'); + } + } + else + { + $a = $session->get_permissions('password_set'); + } + if ( $a && $session->get_permissions('read') && $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin' ) + { + // label at start + $label = $this->makeParserText($tplvars['toolbar_label']); + $label->assign_vars(array('TEXT' => 'password:')); + $t0 = $label->run(); + + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxSetPassword()); return false;" title="Require a password in order for this page to be viewed"', + 'HREF' => '#', + 'TEXT' => 'set', + )); + $t = $menubtn->run(); + + $this->toolbar_menu .= '
'.$t0.''.$t.'
'; + } + + // Manage ACLs button + if($session->get_permissions('edit_acl') || $session->user_level >= USER_LEVEL_ADMIN) + { + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="return ajaxOpenACLManager();" title="Manage who can do what with this page (alt-m)" accesskey="m"', + 'HREF' => makeUrl($paths->page, 'do=aclmanager', true), + 'TEXT' => 'manage page access', + )); + $this->toolbar_menu .= $menubtn->run(); + } + + // Administer page button + if ( $session->user_level >= USER_LEVEL_ADMIN && $paths->page_exists && $paths->namespace != 'Special' && $paths->namespace != 'Admin' ) + { + $menubtn->assign_vars(array( + 'FLAGS' => 'onclick="void(ajaxAdminPage()); return false;" title="Administrative options for this page" accesskey="g"', + 'HREF' => makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'PageManager', true), + 'TEXT' => 'administrative options', + )); + $this->toolbar_menu .= $menubtn->run(); + } + + if ( strlen($this->toolbar_menu) > 0 ) + { + $button->assign_vars(array( + 'FLAGS' => 'id="mdgToolbar_moreoptions" onclick="return false;" title="Additional options for working with this page"', + 'PARENTFLAGS' => '', + 'HREF' => makeUrl($paths->page, 'do=moreoptions', true), + 'TEXT' => 'more options' + )); + $tb .= $button->run(); + } + + $is_opera = (isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'Opera')) ? true : false; + + $this->tpl_bool = Array( + 'auth_admin'=>$session->user_level >= USER_LEVEL_ADMIN ? true : false, + 'user_logged_in'=>$session->user_logged_in, + 'opera'=>$is_opera, + ); + + if($session->sid_super) { $ash = '&auth='.$session->sid_super; $asq = "?auth=".$session->sid_super; $asa = "&auth=".$session->sid_super; $as2 = htmlspecialchars(urlSeparator).'auth='.$session->sid_super; } + else { $asq=''; $asa=''; $as2 = ''; $ash = ''; } + + $code = $plugins->setHook('compile_template'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + // Some additional sidebar processing + if($this->sidebar_extra != '') { + $se = $this->sidebar_extra; + $parser = $this->makeParserText($tplvars['sidebar_section_raw']); + $parser->assign_vars(Array('TITLE'=>'Links','CONTENT'=>$se)); + $this->sidebar_extra = $parser->run(); + } + + $this->sidebar_extra = $this->sidebar_extra.$this->sidebar_widgets; + + $this->tpl_bool['fixed_menus'] = false; + /* if($this->sidebar_extra == '') $this->tpl_bool['right_sidebar'] = false; + else */ $this->tpl_bool['right_sidebar'] = true; + + $this->tpl_bool['auth_rename'] = ( $paths->page_exists && ( $session->get_permissions('rename') && ( $paths->page_protected && $session->get_permissions('even_when_protected') || !$paths->page_protected ) ) && $paths->namespace != 'Special' && $paths->namespace != 'Admin'); + + $this->tpl_bool['enable_uploads'] = ( getConfig('enable_uploads') == '1' && $session->get_permissions('upload_files') ) ? true : false; + + $this->tpl_bool['stupid_mode'] = false; + + if($paths->page == $paths->nslist['Special'].'Administration') $this->tpl_bool['in_admin'] = true; + else $this->tpl_bool['in_admin'] = false; + + $p = ( isset($_GET['printable']) ) ? '/printable' : ''; + + // Add the e-mail address client code to the header + $this->add_header($email->jscode()); + + // Generate the code for the Log out and Change theme sidebar buttons + // Once again, the new template parsing system can be used here + + $parser = $this->makeParserText($tplvars['sidebar_button']); + + $parser->assign_vars(Array( + 'HREF'=>makeUrlNS('Special', 'Logout'), + 'FLAGS'=>'onclick="mb_logout(); return false;"', + 'TEXT'=>'Log out', + )); + + $logout_link = $parser->run(); + + $parser->assign_vars(Array( + 'HREF'=>makeUrlNS('Special', 'Login/' . $paths->page), + 'FLAGS'=>'onclick="ajaxStartLogin(); return false;"', + 'TEXT'=>'Log in', + )); + + $login_link = $parser->run(); + + $parser->assign_vars(Array( + 'HREF'=>makeUrlNS('Special', 'ChangeStyle/'.$paths->page), + 'FLAGS'=>'onclick="ajaxChangeStyle(); return false;"', + 'TEXT'=>'Change theme', + )); + + $theme_link = $parser->run(); + + $SID = ($session->sid_super) ? $session->sid_super : ''; + + // Generate the dynamic javascript vars + $js_dynamic = ' "; + + $tpl_strings = Array( + 'PAGE_NAME'=>$paths->cpage['name'], + 'PAGE_URLNAME'=>$paths->cpage['urlname'], + 'SITE_NAME'=>getConfig('site_name'), + 'USERNAME'=>$session->username, + 'SITE_DESC'=>getConfig('site_desc'), + 'TOOLBAR'=>$tb, + 'SCRIPTPATH'=>scriptPath, + 'CONTENTPATH'=>contentPath, + 'ADMIN_SID_QUES'=>$asq, + 'ADMIN_SID_AMP'=>$asa, + 'ADMIN_SID_AMP_HTML'=>$ash, + 'ADMIN_SID_AUTO'=>$as2, + 'ADDITIONAL_HEADERS'=>$this->additional_headers, + 'COPYRIGHT'=>getConfig('copyright_notice'), + 'TOOLBAR_EXTRAS'=>$this->toolbar_menu, + 'REQUEST_URI'=>$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'], + 'STYLE_LINK'=>makeUrlNS('Special', 'CSS'.$p, null, true), //contentPath.$paths->nslist['Special'].'CSS' . $p, + 'LOGIN_LINK'=>$login_link, + 'LOGOUT_LINK'=>$logout_link, + 'THEME_LINK'=>$theme_link, + 'TEMPLATE_DIR'=>scriptPath.'/themes/'.$this->theme, + 'THEME_ID'=>$this->theme, + 'STYLE_ID'=>$this->style, + 'JS_DYNAMIC_VARS'=>$js_dynamic, + 'UNREAD_PMS'=>$session->unread_pms + ); + + foreach ( $paths->nslist as $ns_id => $ns_prefix ) + { + $tpl_strings[ 'NS_' . strtoupper($ns_id) ] = $ns_prefix; + } + + $this->tpl_strings = array_merge($tpl_strings, $this->tpl_strings); + list($this->tpl_strings['SIDEBAR_LEFT'], $this->tpl_strings['SIDEBAR_RIGHT'], $min) = $this->fetch_sidebar(); + $this->tpl_bool['sidebar_left'] = ( $this->tpl_strings['SIDEBAR_LEFT'] != $min) ? true : false; + $this->tpl_bool['sidebar_right'] = ( $this->tpl_strings['SIDEBAR_RIGHT'] != $min) ? true : false; + $this->tpl_bool['right_sidebar'] = $this->tpl_bool['sidebar_right']; // backward compatibility + } + + function header($simple = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + ob_start(); + + if(!$this->theme_loaded) + { + $this->load_theme($session->theme, $session->style); + } + + $headers_sent = true; + dc_here('template: generating and sending the page header'); + if(!defined('ENANO_HEADERS_SENT')) + define('ENANO_HEADERS_SENT', ''); + if(!$this->no_headers) echo ( $simple ) ? $this->process_template('simple-header.tpl') : $this->process_template('header.tpl'); + if ( !$simple && $session->user_logged_in && $session->unread_pms > 0 ) + { + echo $this->notify_unread_pms(); + } + if ( !$simple && $session->sw_timed_out ) + { + $login_link = makeUrlNS('Special', 'Login/' . $paths->fullpage, 'level=' . $session->user_level, true); + echo '
'; + echo 'Your administrative session has timed out. Log in again'; + echo '
'; + } + } + function footer($simple = false) + { + global $db, $session, $paths, $template, $plugins; // Common objects + dc_here('template: generating and sending the page footer'); + if(!$this->no_headers) { + + if(!defined('ENANO_HEADERS_SENT')) + $this->header(); + + global $_starttime; + if(isset($_GET['sqldbg']) && $session->get_permissions('mod_misc')) + { + echo '

Query list as requested on URI

';
+        echo $db->sql_backtrace();
+        echo '
'; + } + + $f = microtime_float(); + $f = $f - $_starttime; + $f = round($f, 4); + $dbg = 'Time: '.$f.'s | Queries: '.$db->num_queries; + $t = ( $simple ) ? $this->process_template('simple-footer.tpl') : $this->process_template('footer.tpl'); + $t = str_replace('[[Stats]]', $dbg, $t); + $t = str_replace('[[NumQueries]]', (string)$db->num_queries, $t); + $t = str_replace('[[GenTime]]', (string)$f, $t); + echo $t; + + ob_end_flush(); + } + else return ''; + } + function getHeader() + { + $headers_sent = true; + dc_here('template: generating and sending the page header'); + if(!defined('ENANO_HEADERS_SENT')) + define('ENANO_HEADERS_SENT', ''); + if(!$this->no_headers) return $this->process_template('header.tpl'); + } + function getFooter() + { + global $db, $session, $paths, $template, $plugins; // Common objects + dc_here('template: generating and sending the page footer'); + if(!$this->no_headers) { + global $_starttime; + $t = ''; + + if(isset($_GET['sqldbg']) && $session->get_permissions('mod_misc')) + { + $t .= '

Query list as requested on URI

';
+        $t .= $db->sql_backtrace();
+        $t .= '
'; + } + + $f = microtime_float(); + $f = $f - $_starttime; + $f = round($f, 4); + $dbg = 'Time: '.$f.'s | Queries: '.$db->num_queries; + $t.= $this->process_template('footer.tpl'); + $t = str_replace('[[Stats]]', $dbg, $t); + $t = str_replace('[[NumQueries]]', (string)$db->num_queries, $t); + $t = str_replace('[[GenTime]]', (string)$f, $t); + return $t; + } + else return ''; + } + + function process_template($file) { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!defined('ENANO_TEMPLATE_LOADED')) + { + $this->load_theme(); + $this->init_vars(); + } + eval($this->compile_template($file)); + return $tpl_code; + } + + function extract_vars($file) { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$this->theme) + { + die('$template->extract_vars(): theme not yet loaded, so we can\'t open template files yet...this is a bug and should be reported.

Backtrace, most recent call first:
'.enano_debug_print_backtrace(true).'
'); + } + if(!is_file(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file)) die('Cannot find '.$file.' file for style "'.$this->theme.'", exiting'); + $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file); + preg_match_all('#<\!-- VAR ([A-z0-9_-]*) -->(.*?)<\!-- ENDVAR \\1 -->#is', $text, $matches); + $tplvars = Array(); + for($i=0;$itheme.'/'.$text)) die('Cannot find '.$text.' file for style, exiting'); + $n = $text; + $tpl_filename = ENANO_ROOT . '/cache/' . $this->theme . '-' . str_replace('/', '-', $n) . '.php'; + if(!is_file(ENANO_ROOT . '/themes/'.$this->theme.'/'.$text)) die('Cannot find '.$text.' file for style, exiting'); + if(file_exists($tpl_filename) && getConfig('cache_thumbs')=='1') + { + include($tpl_filename); + $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$text); + if(isset($md5) && $md5 == md5($text)) { + return str_replace('\\"', '"', $tpl_text); + } + } + $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$n); + + $md5 = md5($text); + + $seed = md5 ( microtime() . mt_rand() ); + preg_match_all("/<\?php(.*?)\?>/is", $text, $m); + //die('
'.htmlspecialchars(print_r($m, true)).'
'); + for($i = 0; $i < sizeof($m[1]); $i++) + { + $text = str_replace("", "{PHPCODE:{$i}:{$seed}}", $text); + } + //die('
'.htmlspecialchars($text).'
'); + $text = 'ob_start(); echo \''.str_replace('\'', '\\\'', $text).'\'; $tpl_code = ob_get_contents(); ob_end_clean();'; + $text = preg_replace('##is', '\'; if(isset($this->tpl_bool[\'\\1\']) && $this->tpl_bool[\'\\1\']) { echo \'', $text); + $text = preg_replace('##is', '\'; if(isset($this->tpl_strings[\'\\1\'])) { echo \'', $text); + $text = preg_replace('##is', '\'; if(getConfig(\'plugin_\\1\')==\'1\') { echo \'', $text); + $text = preg_replace('##is', '\'; echo $template->tplWikiFormat($paths->sysMsg(\'\\1\')); echo \'', $text); + $text = preg_replace('##is', '\'; if(!$this->tpl_bool[\'\\1\']) { echo \'', $text); + $text = preg_replace('##is', '\'; } else { echo \'', $text); + $text = preg_replace('##is', '\'; } echo \'', $text); + $text = preg_replace('#\{([A-z0-9]*)\}#is', '\'.$this->tpl_strings[\'\\1\'].\'', $text); + for($i = 0; $i < sizeof($m[1]); $i++) + { + $text = str_replace("{PHPCODE:{$i}:{$seed}}", "'; {$m[1][$i]} echo '", $text); + } + if(is_writable(ENANO_ROOT.'/cache/') && getConfig('cache_thumbs')=='1') + { + //die($tpl_filename); + $h = fopen($tpl_filename, 'w'); + if(!$h) return $text; + $t = addslashes($text); + fwrite($h, ''); + fclose($h); + } + return $text; //('
'.htmlspecialchars($text).'
'); + } + + function compile_template_text($text) { + $seed = md5 ( microtime() . mt_rand() ); + preg_match_all("/<\?php(.*?)\?>/is", $text, $m); + //die('
'.htmlspecialchars(print_r($m, true)).'
'); + for($i = 0; $i < sizeof($m[1]); $i++) + { + $text = str_replace("", "{PHPCODE:{$i}:{$seed}}", $text); + } + //die('
'.htmlspecialchars($text).'
'); + $text = 'ob_start(); echo \''.str_replace('\'', '\\\'', $text).'\'; $tpl_code = ob_get_contents(); ob_end_clean(); return $tpl_code;'; + $text = preg_replace('##is', '\'; if(isset($this->tpl_bool[\'\\1\']) && $this->tpl_bool[\'\\1\']) { echo \'', $text); + $text = preg_replace('##is', '\'; if(isset($this->tpl_strings[\'\\1\'])) { echo \'', $text); + $text = preg_replace('##is', '\'; if(getConfig(\'plugin_\\1\')==\'1\') { echo \'', $text); + $text = preg_replace('##is', '\'; echo $template->tplWikiFormat($paths->sysMsg(\'\\1\')); echo \'', $text); + $text = preg_replace('##is', '\'; if(!$this->tpl_bool[\'\\1\']) { echo \'', $text); + $text = preg_replace('##is', '\'; } else { echo \'', $text); + $text = preg_replace('##is', '\'; } echo \'', $text); + $text = preg_replace('#\{([A-z0-9]*)\}#is', '\'.$this->tpl_strings[\'\\1\'].\'', $text); + for($i = 0; $i < sizeof($m[1]); $i++) + { + $text = str_replace("{PHPCODE:{$i}:{$seed}}", "'; {$m[1][$i]} echo '", $text); + } + return $text; //('
'.htmlspecialchars($text).'
'); + } + + function parse($text) + { + $text = $this->compile_template_text($text); + return eval($text); + } + + // Steps to turn this: + // [[Project:Community Portal]] + // into this: + // Community Portal + // Must be done WITHOUT creating eval'ed code!!! + + // 1. preg_replace \[\[([a-zA-Z0-9 -_:]*?)\]\] with \\1 + // 2. preg_match_all + // 3. For each match, replace matches with identifiers + // 4. For each match, str_replace ' ' with '_' + // 5. For each match, str_replace match_id:random_val with $matches[$match_id] + + // The template language is really a miniature programming language; with variables, conditionals, everything! + // So you can implement custom logic into your sidebar if you wish. + // "Real" PHP support coming soon :-D + + function tplWikiFormat($message, $filter_links = false, $filename = 'elements.tpl') { + global $db, $session, $paths, $template, $plugins; // Common objects + $filter_links = false; + $tplvars = $this->extract_vars($filename); + if($session->sid_super) $as = htmlspecialchars(urlSeparator).'auth='.$session->sid_super; + else $as = ''; + error_reporting(E_ALL); + $random_id = sha1(microtime().''); // A temp value + + /* + * PREPROCESSOR + */ + + // Variables + + preg_match_all('#\$([A-Z_-]+)\$#', $message, $links); + $links = $links[1]; + + for($i=0;$itpl_strings[$links[$i]], $message); + } + + // Conditionals + + preg_match_all('#\{if ([A-Za-z0-9_ &\|\!-]*)\}(.*?)\{\/if\}#is', $message, $links); + + for($i=0;$itpl_bool['that']) && $this->tpl_bool['that'] ) && ... + // Method of attack: escape all variables, ignore all else. Non-valid code is filtered out by a regex above. + $in_var_now = true; + $in_var_last = false; + $current_var = ''; + $current_var_start_pos = 0; + $current_var_end_pos = 0; + $j = -1; + $links[1][$i] = $links[1][$i] . ' '; + $d = strlen($links[1][$i]); + while($j < $d) + { + $j++; + $in_var_last = $in_var_now; + + $char = substr($links[1][$i], $j, 1); + $in_var_now = ( preg_match('#^([A-z0-9_]*){1}$#', $char) ) ? true : false; + if(!$in_var_last && $in_var_now) + { + $current_var_start_pos = $j; + } + if($in_var_last && !$in_var_now) + { + $current_var_end_pos = $j; + } + if($in_var_now) + { + $current_var .= $char; + continue; + } + // OK we are not inside of a variable. That means that we JUST hit the end because the counter ($j) will be advanced to the beginning of the next variable once processing here is complete. + if($char != ' ' && $char != '(' && $char != ')' && $char != 'A' && $char != 'N' && $char != 'D' && $char != 'O' && $char != 'R' && $char != '&' && $char != '|' && $char != '!' && $char != '<' && $char != '>' && $char != '0' && $char != '1' && $char != '2' && $char != '3' && $char != '4' && $char != '5' && $char != '6' && $char != '7' && $char != '8' && $char != '9') + { + // XSS attack! Bail out + echo '

Error: Syntax error (possibly XSS attack) caught in template code:

'; + echo '
';
+          echo '{if '.$links[1][$i].'}';
+          echo "\n    ";
+          for($k=0;$k<$j;$k++) echo " ";
+          echo '^';
+          echo '
'; + continue 2; + } + if($current_var != '') + { + $cd = '( isset($this->tpl_bool[\''.$current_var.'\']) && $this->tpl_bool[\''.$current_var.'\'] )'; + $cvt = substr($links[1][$i], 0, $current_var_start_pos) . $cd . substr($links[1][$i], $current_var_end_pos, strlen($links[1][$i])); + $j = $j + strlen($cd) - strlen($current_var); + $current_var = ''; + $links[1][$i] = $cvt; + $d = strlen($links[1][$i]); + } + } + $links[1][$i] = substr($links[1][$i], 0, strlen($links[1][$i])-1); + $links[1][$i] = '$chk = ( '.$links[1][$i].' ) ? true : false;'; + eval($links[1][$i]); + + if($chk) { // isset($this->tpl_bool[$links[1][$i]]) && $this->tpl_bool[$links[1][$i]] + if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], 0, strpos($links[2][$i], '{else}')); + else $c = $links[2][$i]; + $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message); + } else { + if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], strpos($links[2][$i], '{else}')+6, strlen($links[2][$i])); + else $c = ''; + $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message); + } + } + + preg_match_all('#\{!if ([A-Za-z_-]*)\}(.*?)\{\/if\}#is', $message, $links); + + for($i=0;$itpl_bool[$links[1][$i]]) && $this->tpl_bool[$links[1][$i]]) { + if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], strpos($links[2][$i], '{else}')+6, strlen($links[2][$i])); + else $c = ''; + $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message); + } else { + if(strstr($links[2][$i], '{else}')) $c = substr($links[2][$i], 0, strpos($links[2][$i], '{else}')); + else $c = $links[2][$i]; + $message = str_replace('{CONDITIONAL:'.$i.':'.$random_id.'}', $c, $message); + } + } + + /* + * HTML RENDERER + */ + + // Images + $j = preg_match_all('#\[\[:'.$paths->nslist['File'].'([\w\s0-9_\(\)!@%\^\+\|\.-]+?)\]\]#is', $message, $matchlist); + $matches = Array(); + $matches['images'] = $matchlist[1]; + for($i=0;$inslist['File'].$matches['images'][$i])) + { + $message = str_replace('[[:'.$paths->nslist['File'].$matches['images'][$i].']]', + ''.$matches['images'][$i].'', + $message); + } + } + + // Internal links + + $text_parser = $this->makeParserText($tplvars['sidebar_button']); + + preg_match_all('#\[\[([a-zA-Z0-9 -_]*?)\]\]#is', $message, $il); + for($i=0;$iassign_vars(Array( + 'HREF' => $href, + 'FLAGS' => '', + 'TEXT' => $il[1][$i] + )); + $message = str_replace("[[{$il[1][$i]}]]", $text_parser->run(), $message); + } + + preg_match_all('#\[\[([a-zA-Z0-9 -_]*?)\|([a-zA-Z0-9!@\#\$%\^&\*\(\)\{\} -_]*?)\]\]#is', $message, $il); + for($i=0;$iassign_vars(Array( + 'HREF' => $href, + 'FLAGS' => '', + 'TEXT' => $il[2][$i] + )); + $message = str_replace("[[{$il[1][$i]}|{$il[2][$i]}]]", $text_parser->run(), $message); + } + + // External links + $message = preg_replace('#\[(http|ftp|irc):\/\/([a-z0-9\/:_\.\?&%\#@_\\\\-]+?)\\ ([^\]]+)]#', '
\\3
', $message); + $message = preg_replace('#\[(http|ftp|irc):\/\/([a-z0-9\/:_\.\?&%\#@_\\\\-]+?)\\]#', '\\1://\\2
', $message); + + $parser1 = $this->makeParserText($tplvars['sidebar_section']); + $parser2 = $this->makeParserText($tplvars['sidebar_section_raw']); + + preg_match_all('#\{slider(2|)=(.*?)\}(.*?)\{\/slider(2|)\}#is', $message, $sb); + + // Modified to support the sweet new template var system + for($i=0;$iassign_vars(Array('TITLE'=>$sb[2][$i],'CONTENT'=>$sb[3][$i])); + $message = str_replace("{slider{$sb[1][$i]}={$sb[2][$i]}}{$sb[3][$i]}{/slider{$sb[4][$i]}}", $p->run(), $message); + } + + /* + Extras ;-) + $message = preg_replace('##is', '', $message); + $message = preg_replace('##is', '', $message); + $message = preg_replace('##is', '', $message); + $message = preg_replace('##is', '', $message); + $message = preg_replace('##is', '', $message); + */ + + //die('
'.htmlspecialchars($message).'
'); + //eval($message); exit; + return $message; + } + + /** + * Print a text field that auto-completes a username entered into it. + * @param string $name - the name of the form field + * @return string + */ + + function username_field($name, $value = false) + { + $randomid = md5( time() . microtime() . mt_rand() ); + $text = ''; + return $text; + } + + /** + * Sends a textarea that can be converted to and from a TinyMCE widget on the fly. + * @param string The name of the form element + * @param string The initial content. Optional, defaults to blank + * @param int Rows in textarea + * @param int Columns in textarea + * @return string HTML and Javascript code. + */ + + function tinymce_textarea($name, $content = '', $rows = 20, $cols = 60) + { + $randomid = md5(microtime() . mt_rand()); + $html = ''; + $html .= ''; + $html .= '
text editor  |  graphical editor
'; + $html .= ''; + return $html; + } + + /** + * Allows individual parsing of template files. Similar to phpBB but follows the spirit of object-oriented programming ;) + * Returns on object of class templateIndividual. Usage instructions can be found in the inline docs for that class. + * @param $filename the filename of the template to be parsed + * @return object + */ + + function makeParser($filename) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $filename = ENANO_ROOT.'/themes/'.$template->theme.'/'.$filename; + if(!file_exists($filename)) die('templateIndividual: file '.$filename.' does not exist'); + $code = file_get_contents($filename); + $parser = new templateIndividual($code); + return $parser; + } + + /** + * Same as $template->makeParser(), but takes a string instead of a filename. + * @param $text the text to parse + * @return object + */ + + function makeParserText($code) + { + $parser = new templateIndividual($code); + return $parser; + } + + /** + * Fetch the HTML for a plugin-added sidebar block + * @param $name the plugin name + * @return string + */ + + function fetch_block($id) + { + if(isset($this->plugin_blocks[$id])) return $this->plugin_blocks[$id]; + else return false; + } + + /** + * Fetches the contents of both sidebars. + * @return array - key 0 is left, key 1 is right + * @example list($left, $right) = $template->fetch_sidebar(); + */ + + function fetch_sidebar() + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $left = ''; + $right = ''; + + if ( !$this->fetch_block('Links') ) + $this->initLinksWidget(); + + $q = $db->sql_query('SELECT item_id,sidebar_id,block_name,block_type,block_content FROM '.table_prefix.'sidebar WHERE item_enabled=1 ORDER BY sidebar_id ASC, item_order ASC;'); + if(!$q) $db->_die('The sidebar text data could not be selected.'); + + $vars = $this->extract_vars('elements.tpl'); + + if(isset($vars['sidebar_top'])) + { + $left .= $this->parse($vars['sidebar_top']); + $right .= $this->parse($vars['sidebar_top']); + } + while($row = $db->fetchrow()) + { + switch($row['block_type']) + { + case BLOCK_WIKIFORMAT: + default: + $parser = $this->makeParserText($vars['sidebar_section']); + $c = RenderMan::render($row['block_content']); + break; + case BLOCK_TEMPLATEFORMAT: + $parser = $this->makeParserText($vars['sidebar_section']); + $c = $this->tplWikiFormat($row['block_content']); + break; + case BLOCK_HTML: + $parser = $this->makeParserText($vars['sidebar_section_raw']); + $c = $row['block_content']; + break; + case BLOCK_PHP: + $parser = $this->makeParserText($vars['sidebar_section_raw']); + ob_start(); + @eval($row['block_content']); + $c = ob_get_contents(); + ob_end_clean(); + break; + case BLOCK_PLUGIN: + $parser = $this->makeParserText($vars['sidebar_section_raw']); + $c = (gettype($this->fetch_block($row['block_content'])) == 'string') ? $this->fetch_block($row['block_content']) : 'Can\'t find plugin block'; + break; + } + $parser->assign_vars(Array( 'TITLE'=>$this->tplWikiFormat($row['block_name']), 'CONTENT'=>$c )); + if ($row['sidebar_id'] == SIDEBAR_LEFT ) $left .= $parser->run(); + elseif($row['sidebar_id'] == SIDEBAR_RIGHT) $right .= $parser->run(); + unset($parser); + } + $db->free_result(); + if(isset($vars['sidebar_bottom'])) + { + $left .= $this->parse($vars['sidebar_bottom']); + $right .= $this->parse($vars['sidebar_bottom']); + } + $min = ''; + if(isset($vars['sidebar_top'])) + { + $min .= $this->parse($vars['sidebar_top']); + } + if(isset($vars['sidebar_bottom'])) + { + $min .= $this->parse($vars['sidebar_bottom']); + } + return Array($left, $right, $min); + } + + function initLinksWidget() + { + global $db, $session, $paths, $template, $plugins; // Common objects + // SourceForge/W3C buttons + $ob = Array(); + if(getConfig('powered_btn') =='1') $ob[] = 'Powered by Enano'; + if(getConfig('sflogo_enabled')=='1') + { + $ob[] = 'SourceForge.net Logo'; + } + if(getConfig('w3c_v32') =='1') $ob[] = 'Valid HTML 3.2'; + if(getConfig('w3c_v40') =='1') $ob[] = 'Valid HTML 4.0'; + if(getConfig('w3c_v401') =='1') $ob[] = 'Valid HTML 4.01'; + if(getConfig('w3c_vxhtml10')=='1') $ob[] = 'Valid XHTML 1.0'; + if(getConfig('w3c_vxhtml11')=='1') $ob[] = 'Valid XHTML 1.1'; + if(getConfig('w3c_vcss') =='1') $ob[] = 'Valid CSS'; + if(getConfig('dbd_button') =='1') $ob[] = 'DRM technology restricts what you can do with your computer
Protect your freedom >>
'; + + $code = $plugins->setHook('links_widget'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + if(count($ob) > 0) $sb_links = '
'.implode('
', $ob).'
'; + else $sb_links = ''; + + $this->sidebar_widget('Links', $sb_links); + } + + /** + * Builds a box showing unread private messages. + */ + + function notify_unread_pms() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if ( ( $paths->cpage['urlname_nons'] == 'PrivateMessages' || $paths->cpage['urlname_nons'] == 'Preferences' ) && $paths->namespace == 'Special' ) + { + return ''; + } + $ob = '
'."\n"; + $s = ( $session->unread_pms == 1 ) ? '' : 's'; + $ob .= " You have $session->unread_pms unread private message$s.
\n Messages: "; + $q = $db->sql_query('SELECT message_id,message_from,subject,date FROM '.table_prefix.'privmsgs WHERE message_to=\'' . $session->username . '\' AND message_read=0 ORDER BY date DESC;'); + if ( !$q ) + $db->_die(); + $messages = array(); + while ( $row = $db->fetchrow() ) + { + $messages[] = '' . $row['subject'] . ''; + } + $ob .= implode(",\n " , $messages)."\n"; + $ob .= '
'."\n"; + return $ob; + } + +} // class template + +/** + * Handles parsing of an individual template file. Instances should only be created through $template->makeParser(). To use: + * - Call $template->makeParser(template file name) - file name should be something.tpl, css/whatever.css, etc. + * - Make an array of strings you want the template to access. $array['STRING'] would be referenced in the template like {STRING} + * - Make an array of boolean values. These can be used for conditionals in the template ( whatever ) + * - Call assign_vars() to pass the strings to the template parser. Same thing with assign_bool(). + * - Call run() to parse the template and get your fully compiled HTML. + * @access private + */ + +class templateIndividual extends template { + var $tpl_strings, $tpl_bool, $tpl_code; + var $compiled = false; + /** + * Constructor. + */ + function __construct($text) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $this->tpl_code = $text; + $this->tpl_strings = $template->tpl_strings; + $this->tpl_bool = $template->tpl_bool; + } + /** + * PHP 4 constructor. + */ + function templateIndividual($text) + { + $this->__construct($text); + } + /** + * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file. + * @param $vars array + */ + function assign_vars($vars) + { + $this->tpl_strings = array_merge($this->tpl_strings, $vars); + } + /** + * Assigns an array of boolean values to the template. These can be used for statements. + * @param $vars array + */ + function assign_bool($vars) + { + $this->tpl_bool = array_merge($this->tpl_bool, $vars); + } + /** + * Compiles and executes the template code. + * @return string + */ + function run() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$this->compiled) + { + $this->tpl_code = $this->compile_template_text($this->tpl_code); + $this->compiled = true; + } + return eval($this->tpl_code); + } +} + +/** + * A version of the template compiler that does not rely at all on the other parts of Enano. Used during installation and for showing + * "critical error" messages. ** REQUIRES ** the Oxygen theme. + */ + +class template_nodb { + var $tpl_strings, $tpl_bool, $theme, $style, $no_headers, $additional_headers, $sidebar_extra, $sidebar_widgets, $toolbar_menu, $theme_list; + function __construct() { + + $this->tpl_bool = Array(); + $this->tpl_strings = Array(); + $this->sidebar_extra = ''; + $this->sidebar_widgets = ''; + $this->toolbar_menu = ''; + $this->additional_headers = ''; + + $this->theme_list = Array(Array( + 'theme_id'=>'oxygen', + 'theme_name'=>'Oxygen', + 'theme_order'=>1, + 'enabled'=>1, + )); + } + function template() { + $this->__construct(); + } + function get_css($s = false) { + if($s) + return $this->process_template('css/'.$s); + else + return $this->process_template('css/'.$this->style.'.css'); + } + function load_theme($name, $css, $auto_init = true) { + $this->theme = $name; + $this->style = $css; + + $this->tpl_strings['SCRIPTPATH'] = scriptPath; + if ( $auto_init ) + $this->init_vars(); + } + function init_vars() + { + global $sideinfo; + global $this_page; + global $db, $session, $paths, $template, $plugins; // Common objects + $tplvars = $this->extract_vars('elements.tpl'); + $tb = ''; + // Get the "article" button text (depends on namespace) + if(defined('IN_ENANO_INSTALL')) $ns = 'installation page'; + else $ns = 'system error page'; + $t = str_replace('{FLAGS}', 'onclick="return false;" title="Hey! A button that doesn\'t do anything. Clever..." accesskey="a"', $tplvars['toolbar_button']); + $t = str_replace('{HREF}', '#', $t); + $t = str_replace('{TEXT}', $ns, $t); + $tb .= $t; + + // Page toolbar + + $this->tpl_bool = Array( + 'auth_admin'=>true, + 'user_logged_in'=>true, + 'right_sidebar'=>false, + ); + $this->tpl_bool['in_sidebar_admin'] = false; + + $this->tpl_bool['auth_rename'] = false; + + $asq = $asa = ''; + + $this->tpl_bool['fixed_menus'] = false; + $slink = defined('IN_ENANO_INSTALL') ? scriptPath.'/install.php?mode=css' : makeUrlNS('Special', 'CSS'); + + $title = ( is_object($paths) ) ? $paths->page : 'Critical error'; + + // The rewritten template engine will process all required vars during the load_template stage instead of (cough) re-processing everything each time around. + $tpl_strings = Array( + 'PAGE_NAME'=>$this_page, + 'PAGE_URLNAME'=>'Null', + 'SITE_NAME'=>'Enano Installation', + 'USERNAME'=>'admin', + 'SITE_DESC'=>'Install Enano on your server.', + 'TOOLBAR'=>$tb, + 'SCRIPTPATH'=>scriptPath, + 'CONTENTPATH'=>contentPath, + 'ADMIN_SID_QUES'=>$asq, + 'ADMIN_SID_AMP'=>$asa, + 'ADMIN_SID_AMP_HTML'=>'', + 'ADDITIONAL_HEADERS'=>'', + 'SIDEBAR_EXTRA'=>'', + 'COPYRIGHT'=>'Enano and all of its code, graphics, and more code is copyright © 2006 Dan Fuhry.
This program is Free Software; see the file "GPL" included with this package for details.', + 'TOOLBAR_EXTRAS'=>'', + 'REQUEST_URI'=>$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'], + 'STYLE_LINK'=>$slink, + 'LOGOUT_LINK'=>'', + 'THEME_LINK'=>'', + 'TEMPLATE_DIR'=>scriptPath.'/themes/'.$this->theme, + 'THEME_ID'=>$this->theme, + 'STYLE_ID'=>$this->style, + 'JS_DYNAMIC_VARS'=>'', + 'SIDEBAR_RIGHT'=>'', + ); + $this->tpl_strings = array_merge($tpl_strings, $this->tpl_strings); + + $sidebar = ( gettype($sideinfo) == 'string' ) ? $sideinfo : ''; + if($sidebar != '') + { + if(isset($tplvars['sidebar_top'])) + { + $text = $this->makeParserText($tplvars['sidebar_top']); + $top = $text->run(); + } else { + $top = ''; + } + $p = $this->makeParserText($tplvars['sidebar_section']); + $p->assign_vars(Array( + 'TITLE'=>'Installation progress', + 'CONTENT'=>$sidebar, + )); + $sidebar = $p->run(); + if(isset($tplvars['sidebar_bottom'])) + { + $text = $this->makeParserText($tplvars['sidebar_bottom']); + $bottom = $text->run(); + } else { + $bottom = ''; + } + $sidebar = $top . $sidebar . $bottom; + } + $this->tpl_strings['SIDEBAR_LEFT'] = $sidebar; + + $this->tpl_bool['sidebar_left'] = ( $this->tpl_strings['SIDEBAR_LEFT'] != '') ? true : false; + $this->tpl_bool['sidebar_right'] = ( $this->tpl_strings['SIDEBAR_RIGHT'] != '') ? true : false; + $this->tpl_bool['right_sidebar'] = $this->tpl_bool['sidebar_right']; // backward compatibility + $this->tpl_bool['stupid_mode'] = true; + } + function header() + { + if(!$this->no_headers) echo $this->process_template('header.tpl'); + } + function footer() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$this->no_headers) { + global $_starttime; + $f = microtime(true); + $f = $f - $_starttime; + $f = round($f, 4); + if(defined('IN_ENANO_INSTALL')) $nq = 'N/A'; + else $nq = $db->num_queries; + if($nq == 0) $nq = 'N/A'; + $dbg = 'Time: '.$f.'s | Queries: '.$nq; + $t = $this->process_template('footer.tpl'); + $t = str_replace('[[Stats]]', $dbg, $t); + echo $t; + } + else return ''; + } + function getHeader() + { + if(!$this->no_headers) return $this->process_template('header.tpl'); + else return ''; + } + function getFooter() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$this->no_headers) { + global $_starttime; + $f = microtime(true); + $f = $f - $_starttime; + $f = round($f, 4); + if(defined('IN_ENANO_INSTALL')) $nq = 'N/A'; + else $nq = $db->num_queries; + if($nq == 0) $nq = 'N/A'; + $dbg = 'Time: '.$f.'s | Queries: '.$nq; + if($nq == 0) $nq = 'N/A'; + $t = $this->process_template('footer.tpl'); + $t = str_replace('[[Stats]]', $dbg, $t); + return $t; + } + else return ''; + } + + function process_template($file) { + + eval($this->compile_template($file)); + return $tpl_code; + } + + function extract_vars($file) { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!is_file(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file)) die('Cannot find '.$file.' file for style "'.$this->theme.'", exiting'); + $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file); + preg_match_all('#<\!-- VAR ([A-z0-9_-]*) -->(.*?)<\!-- ENDVAR \\1 -->#is', $text, $matches); + $tplvars = Array(); + for($i=0;$itheme.'/'.$text); + $text = str_replace('', '', $text); // Remove the AJAX code - we don't need it, and it requires a database connection + $text = '$tpl_code = \''.str_replace('\'', '\\\'', $text).'\'; return $tpl_code;'; + $text = preg_replace('##is', '\'; if($this->tpl_bool[\'\\1\']) { $tpl_code .= \'', $text); + $text = preg_replace('##is', '\'; if(getConfig(\'plugin_\\1\')==\'1\') { $tpl_code .= \'', $text); + if(defined('IN_ENANO_INSTALL')) $text = str_replace('', '', $text); + else $text = str_replace('', '', $text); + $text = preg_replace('##is', '', $text); + $text = preg_replace('##is', '\'; if(!$this->tpl_bool[\'\\1\']) { $tpl_code .= \'', $text); + $text = preg_replace('##is', '\'; } else { $tpl_code .= \'', $text); + $text = preg_replace('##is', '\'; } $tpl_code .= \'', $text); + $text = preg_replace('#{([A-z0-9]*)}#is', '\'.$this->tpl_strings[\'\\1\'].\'', $text); + return $text; //('
'.htmlspecialchars($text).'
'); + } + + function compile_template_text($text) { + global $sideinfo; + $text = str_replace('', '', $text); // Remove the AJAX code - we don't need it, and it requires a database connection + $text = '$tpl_code = \''.str_replace('\'', '\\\'', $text).'\'; return $tpl_code;'; + $text = preg_replace('##is', '\'; if($this->tpl_bool[\'\\1\']) { $tpl_code .= \'', $text); + $text = preg_replace('##is', '\'; if(getConfig(\'plugin_\\1\')==\'1\') { $tpl_code .= \'', $text); + if(defined('IN_ENANO_INSTALL')) $text = str_replace('', '', $text); + else $text = str_replace('', '', $text); + $text = preg_replace('##is', '', $text); + $text = preg_replace('##is', '\'; if(!$this->tpl_bool[\'\\1\']) { $tpl_code .= \'', $text); + $text = preg_replace('##is', '\'; } else { $tpl_code .= \'', $text); + $text = preg_replace('##is', '\'; } $tpl_code .= \'', $text); + $text = preg_replace('#{([A-z0-9]*)}#is', '\'.$this->tpl_strings[\'\\1\'].\'', $text); + return $text; //('
'.htmlspecialchars($text).'
'); + } + + /** + * Allows individual parsing of template files. Similar to phpBB but follows the spirit of object-oriented programming ;) + * Returns on object of class templateIndividual. Usage instructions can be found in the inline docs for that class. + * @param $filename the filename of the template to be parsed + * @return object + */ + + function makeParser($filename) + { + $filename = ENANO_ROOT.'/themes/'.$this->theme.'/'.$filename; + if(!file_exists($filename)) die('templateIndividual: file '.$filename.' does not exist'); + $code = file_get_contents($filename); + $parser = new templateIndividualSafe($code, $this); + return $parser; + } + + /** + * Same as $template->makeParser(), but takes a string instead of a filename. + * @param $text the text to parse + * @return object + */ + + function makeParserText($code) + { + $parser = new templateIndividualSafe($code, $this); + return $parser; + } + +} // class template_nodb + +/** + * Identical to templateIndividual, except extends template_nodb instead of template + * @see class template + */ + +class templateIndividualSafe extends template_nodb { + var $tpl_strings, $tpl_bool, $tpl_code; + var $compiled = false; + /** + * Constructor. + */ + function __construct($text, $parent) + { + global $db, $session, $paths, $template, $plugins; // Common objects + $this->tpl_code = $text; + $this->tpl_strings = $parent->tpl_strings; + $this->tpl_bool = $parent->tpl_bool; + } + /** + * PHP 4 constructor. + */ + function templateIndividual($text) + { + $this->__construct($text); + } + /** + * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file. + * @param $vars array + */ + function assign_vars($vars) + { + if(is_array($this->tpl_strings)) + $this->tpl_strings = array_merge($this->tpl_strings, $vars); + else + $this->tpl_strings = $vars; + } + /** + * Assigns an array of boolean values to the template. These can be used for statements. + * @param $vars array + */ + function assign_bool($vars) + { + $this->tpl_bool = array_merge($this->tpl_bool, $vars); + } + /** + * Compiles and executes the template code. + * @return string + */ + function run() + { + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$this->compiled) + { + $this->tpl_code = $this->compile_template_text($this->tpl_code); + $this->compiled = true; + } + return eval($this->tpl_code); + } +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Default.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Default.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,27 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Default.php,v 1.1 2006/03/01 16:58:17 justinpatrin Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +require_once('Text/Wiki.php'); + +/** + * This is the parser for the Default ruleset. For now, this simply extends Text_Wiki. + * + * @category Text + * @package Text_Wiki + * @version Release: @package_version@ + * @author Justin Patrin + */ +class Text_Wiki_Default extends Text_Wiki { +} diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Mediawiki.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Mediawiki.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,93 @@ + + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Mediawiki.php,v 1.8 2006/02/25 09:59:34 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * "master" class for handling the management and convenience + */ +require_once(ENANO_ROOT.'/includes/wikiformat.php'); + +/** + * Base Text_Wiki handler class extension for Mediawiki markup + * + * @category Text + * @package Text_Wiki + * @author Bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + * @see Text_Wiki::Text_Wiki() + */ +class Text_Wiki_Mediawiki extends Text_Wiki { + var $rules = array( + 'Prefilter', + 'Delimiter', + 'Code', +// 'Plugin', +// 'Function', +// 'Html', + 'Raw', +// 'Preformatted', +// 'Include', +// 'Embed', +// 'Page', +// 'Anchor', + 'Heading', + 'Toc', +// 'Titlebar', + 'Horiz', + 'Break', + 'Blockquote', + 'List', + 'Deflist', +// 'Table', +// 'Box', +// 'Image', // done by Wikilink but still possible to disable/configure +// 'Phplookup', + 'Center', + 'Newline', + 'Paragraph', + 'Url', +// 'Freelink', +// 'Colortext', + 'Wikilink', +// 'Strong', ** will be only fake inserted by Emphasis if needed for render + 'Bold', + 'Emphasis', + 'Italic', + 'Underline', + 'Tt', + 'Superscript', + 'Subscript', +// 'Specialchar', + 'Revise', +// 'Interwiki', // done by Wikilink but still possible to disable/configure + 'Tighten' + ); + + /** + * Constructor: just adds the path to Mediawiki rules + * + * @access public + * @param array $rules The set of rules to load for this object. + */ + function Text_Wiki_Mediawiki($rules = null) { + parent::Text_Wiki($rules); + $this->addPath('parse', $this->fixPath(dirname(__FILE__)).'Parse/Mediawiki'); + } +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Parse.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Parse.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,264 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Parse.php,v 1.5 2005/07/29 08:57:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * Baseline rule class for extension into a "real" parser component. + * + * Text_Wiki_Rule classes do not stand on their own; they are called by a + * Text_Wiki object, typcially in the transform() method. Each rule class + * performs three main activities: parse, process, and render. + * + * The parse() method takes a regex and applies it to the whole block of + * source text at one time. Each match is sent as $matches to the + * process() method. + * + * The process() method acts on the matched text from the source, and + * then processes the source text is some way. This may mean the + * creation of a delimited token using addToken(). In every case, the + * process() method returns the text that should replace the matched text + * from parse(). + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Parse { + + + /** + * + * Configuration options for this parser rule. + * + * @access public + * + * @var string + * + */ + + var $conf = array(); + + + /** + * + * Regular expression to find matching text for this rule. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = null; + + + /** + * + * The name of this rule for new token array elements. + * + * @access public + * + * @var string + * + */ + + var $rule = null; + + + /** + * + * A reference to the calling Text_Wiki object. + * + * This is needed so that each rule has access to the same source + * text, token set, URLs, interwiki maps, page names, etc. + * + * @access public + * + * @var object + */ + + var $wiki = null; + + + /** + * + * Constructor for this parser rule. + * + * @access public + * + * @param object &$obj The calling "parent" Text_Wiki object. + * + */ + + function Text_Wiki_Parse(&$obj) + { + // set the reference to the calling Text_Wiki object; + // this allows us access to the shared source text, token + // array, etc. + $this->wiki =& $obj; + + // set the name of this rule; generally used when adding + // to the tokens array. strip off the Text_Wiki_Parse_ portion. + // text_wiki_parse_ + // 0123456789012345 + $tmp = substr(get_class($this), 16); + $this->rule = ucwords(strtolower($tmp)); + + // override config options for the rule if specified + if (isset($this->wiki->parseConf[$this->rule]) && + is_array($this->wiki->parseConf[$this->rule])) { + + $this->conf = array_merge( + $this->conf, + $this->wiki->parseConf[$this->rule] + ); + + } + } + + + /** + * + * Abstrct method to parse source text for matches. + * + * Applies the rule's regular expression to the source text, passes + * every match to the process() method, and replaces the matched text + * with the results of the processing. + * + * @access public + * + * @see Text_Wiki_Parse::process() + * + */ + + function parse() + { + $this->wiki->source = preg_replace_callback( + $this->regex, + array(&$this, 'process'), + $this->wiki->source + ); + } + + + /** + * + * Abstract method to generate replacements for matched text. + * + * @access public + * + * @param array $matches An array of matches from the parse() method + * as generated by preg_replace_callback. $matches[0] is the full + * matched string, $matches[1] is the first matched pattern, + * $matches[2] is the second matched pattern, and so on. + * + * @return string The processed text replacement; defaults to the + * full matched string (i.e., no changes to the text). + * + * @see Text_Wiki_Parse::parse() + * + */ + + function process(&$matches) + { + return $matches[0]; + } + + + /** + * + * Simple method to safely get configuration key values. + * + * @access public + * + * @param string $key The configuration key. + * + * @param mixed $default If the key does not exist, return this value + * instead. + * + * @return mixed The configuration key value (if it exists) or the + * default value (if not). + * + */ + + function getConf($key, $default = null) + { + if (isset($this->conf[$key])) { + return $this->conf[$key]; + } else { + return $default; + } + } + + + /** + * + * Extract 'attribute="value"' portions of wiki markup. + * + * This kind of markup is typically used only in macros, but is useful + * anywhere. + * + * The syntax is pretty strict; there can be no spaces between the + * option name, the equals, and the first double-quote; the value + * must be surrounded by double-quotes. You can escape characters in + * the value with a backslash, and the backslash will be stripped for + * you. + * + * @access public + * + * @param string $text The "attributes" portion of markup. + * + * @return array An associative array of key-value pairs where the + * key is the option name and the value is the option value. + * + */ + + function getAttrs($text) + { + // find the =" sections; + $tmp = explode('="', trim($text)); + + // basic setup + $k = count($tmp) - 1; + $attrs = array(); + $key = null; + + // loop through the sections + foreach ($tmp as $i => $val) { + + // first element is always the first key + if ($i == 0) { + $key = trim($val); + continue; + } + + // find the last double-quote in the value. + // the part to the left is the value for the last key, + // the part to the right is the next key name + $pos = strrpos($val, '"'); + $attrs[$key] = stripslashes(substr($val, 0, $pos)); + $key = trim(substr($val, $pos+1)); + + } + + return $attrs; + + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Parse/Default/Anchor.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Parse/Default/Anchor.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,87 @@ + +* +* @author Paul M. Jones +* +* @license LGPL +* +* @version $Id: Anchor.php,v 1.3 2005/02/23 17:38:29 pmjones Exp $ +* +*/ + +/** +* +* This class implements a Text_Wiki_Parse to add an anchor target name +* in the wiki page. +* +* @author Manuel Holtgrewe +* +* @author Paul M. Jones +* +* @category Text +* +* @package Text_Wiki +* +*/ + +class Text_Wiki_Parse_Anchor extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to find source text matching this + * rule. Looks like a macro: [[# anchor_name]] + * + * @access public + * + * @var string + * + */ + + var $regex = '/(\[\[# )([-_A-Za-z0-9.]+?)( .+)?(\]\])/i'; + + + /** + * + * Generates a token entry for the matched text. Token options are: + * + * 'text' => The full matched text, not including the tags. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A delimited token number to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) { + + $name = $matches[2]; + $text = $matches[3]; + + $start = $this->wiki->addToken( + $this->rule, + array('type' => 'start', 'name' => $name) + ); + + $end = $this->wiki->addToken( + $this->rule, + array('type' => 'end', 'name' => $name) + ); + + // done, place the script output directly in the source + return $start . trim($text) . $end; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Parse/Default/Blockquote.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Parse/Default/Blockquote.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,179 @@ + +* +* @license LGPL +* +* @version $Id: Blockquote.php,v 1.3 2005/02/23 17:38:29 pmjones Exp $ +* +*/ + +/** +* +* Parse for block-quoted text. +* +* Find source text marked as a blockquote, identified by any number of +* greater-than signs '>' at the start of the line, followed by a space, +* and then the quote text; each '>' indicates an additional level of +* quoting. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Blockquote extends Text_Wiki_Parse { + + + /** + * + * Regex for parsing the source text. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/\n((\>).*\n)(?!(\>))/Us'; + + + /** + * + * Generates a replacement for the matched text. + * + * Token options are: + * + * 'type' => + * 'start' : the start of a blockquote + * 'end' : the end of a blockquote + * + * 'level' => the indent level (0 for the first level, 1 for the + * second, etc) + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A series of text and delimited tokens marking the different + * list text and list elements. + * + */ + + function process(&$matches) + { + // the replacement text we will return to parse() + $return = ''; + + // the list of post-processing matches + $list = array(); + + // $matches[1] is the text matched as a list set by parse(); + // create an array called $list that contains a new set of + // matches for the various list-item elements. + preg_match_all( + '=^(\>+) (.*\n)=Ums', + $matches[1], + $list, + PREG_SET_ORDER + ); + + // a stack of starts and ends; we keep this so that we know what + // indent level we're at. + $stack = array(); + + // loop through each list-item element. + foreach ($list as $key => $val) { + + // $val[0] is the full matched list-item line + // $val[1] is the number of initial '>' chars (indent level) + // $val[2] is the quote text + + // we number levels starting at 1, not zero + $level = strlen($val[1]); + + // get the text of the line + $text = $val[2]; + + // add a level to the list? + while ($level > count($stack)) { + + // the current indent level is greater than the number + // of stack elements, so we must be starting a new + // level. push the new level onto the stack with a + // dummy value (boolean true)... + array_push($stack, true); + + $return .= "\n"; + + // ...and add a start token to the return. + $return .= $this->wiki->addToken( + $this->rule, + array( + 'type' => 'start', + 'level' => $level - 1 + ) + ); + + $return .= "\n\n"; + } + + // remove a level? + while (count($stack) > $level) { + + // as long as the stack count is greater than the + // current indent level, we need to end list types. + // continue adding end-list tokens until the stack count + // and the indent level are the same. + array_pop($stack); + + $return .= "\n\n"; + + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => 'end', + 'level' => count($stack) + ) + ); + + $return .= "\n"; + } + + // add the line text. + $return .= $text; + } + + // the last line may have been indented. go through the stack + // and create end-tokens until the stack is empty. + $return .= "\n"; + + while (count($stack) > 0) { + array_pop($stack); + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => 'end', + 'level' => count($stack) + ) + ); + } + + // we're done! send back the replacement text. + return "\n$return\n\n"; + } +} +?> \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Parse/Default/Bold.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Parse/Default/Bold.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,79 @@ + +* +* @license LGPL +* +* @version $Id: Bold.php,v 1.3 2005/02/23 17:38:29 pmjones Exp $ +* +*/ + +/** +* +* Parses for bold text. +* +* This class implements a Text_Wiki_Rule to find source text marked for +* strong emphasis (bold) as defined by text surrounded by three +* single-quotes. On parsing, the text itself is left in place, but the +* starting and ending instances of three single-quotes are replaced with +* tokens. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Bold extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = "/'''(()|[^'].*)'''/U"; + + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * emphasized text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A pair of delimited tokens to be used as a placeholder in + * the source text surrounding the text to be emphasized. + * + */ + + function process(&$matches) + { + $start = $this->wiki->addToken($this->rule, array('type' => 'start')); + $end = $this->wiki->addToken($this->rule, array('type' => 'end')); + return $start . $matches[1] . $end; + } +} +?> \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Parse/Default/Break.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Parse/Default/Break.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,72 @@ + +* +* @license LGPL +* +* @version $Id: Break.php,v 1.3 2005/02/23 17:38:29 pmjones Exp $ +* +*/ + +/** +* +* Parses for explicit line breaks. +* +* This class implements a Text_Wiki_Parse to mark forced line breaks in the +* source text. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Break extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/ _\n/'; + + + /** + * + * Generates a replacement token for the matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A delimited token to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + return $this->wiki->addToken($this->rule); + } +} + +?> \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Parse/Default/Center.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Parse/Default/Center.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,78 @@ + +* +* @license LGPL +* +* @version $Id: Center.php,v 1.3 2005/02/23 17:38:29 pmjones Exp $ +* +*/ + +/** +* +* Parses for centered lines of text. +* +* This class implements a Text_Wiki_Parse to find lines marked for centering. +* The line must start with "= " (i.e., an equal-sign followed by a space). +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Center extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to find source text matching this + * rule. + * + * @access public + * + * @var string + * + */ + + var $regex = '/\n\= (.*?)\n/'; + + /** + * + * Generates a token entry for the matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A delimited token number to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + $start = $this->wiki->addToken( + $this->rule, + array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, + array('type' => 'end') + ); + + return "\n" . $start . $matches[1] . $end . "\n"; + } +} +?> \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Parse/Default/Code.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Parse/Default/Code.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,99 @@ + +* +* @license LGPL +* +* @version $Id: Code.php,v 1.10 2006/02/21 14:33:53 toggg Exp $ +* +*/ + +/** +* +* Parses for text marked as a code example block. +* +* This class implements a Text_Wiki_Parse to find sections marked as code +* examples. Blocks are marked as the string on a line by itself, +* followed by the inline code example, and terminated with the string +* on a line by itself. The code example is run through the +* native PHP highlight_string() function to colorize it, then surrounded +* with
...
tags when rendered as XHTML. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Code extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to find source text matching this + * rule. + * + * @access public + * + * @var string + * + */ + +/* var $regex = '/^(\)\n(.+)\n(\<\/code\>)(\s|$)/Umsi';*/ + var $regex = ';^]*)?>((?:(?R)|.)*?)\n
(\s|$);msi'; + + /** + * + * Generates a token entry for the matched text. Token options are: + * + * 'text' => The full matched text, not including the tags. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A delimited token number to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + // are there additional attribute arguments? + $args = trim($matches[1]); + + if ($args == '') { + $options = array( + 'text' => $matches[2], + 'attr' => array('type' => '') + ); + } else { + // get the attributes... + $attr = $this->getAttrs($args); + + // ... and make sure we have a 'type' + if (! isset($attr['type'])) { + $attr['type'] = ''; + } + + // retain the options + $options = array( + 'text' => $matches[2], + 'attr' => $attr + ); + } + + return $this->wiki->addToken($this->rule, $options) . $matches[3]; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Parse/Default/Colortext.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Parse/Default/Colortext.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,89 @@ + +* +* @license LGPL +* +* @version $Id: Colortext.php,v 1.3 2005/02/23 17:38:29 pmjones Exp $ +* +*/ + +/** +* +* Parses for colorized text. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Colortext extends Text_Wiki_Parse { + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = "/\#\#(.+?)\|(.+?)\#\#/"; + + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * emphasized text. The text itself is left in the source. + * + * 'color' => the color indicator + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A pair of delimited tokens to be used as a + * placeholder in the source text surrounding the text to be + * emphasized. + * + */ + + function process(&$matches) + { + $start = $this->wiki->addToken( + $this->rule, + array( + 'type' => 'start', + 'color' => $matches[1] + ) + ); + + $end = $this->wiki->addToken( + $this->rule, + array( + 'type' => 'end', + 'color' => $matches[1] + ) + ); + + return $start . $matches[2] . $end; + } +} +?> \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Parse/Default/Deflist.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Parse/Default/Deflist.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,122 @@ + +* +* @license LGPL +* +* @version $Id: Deflist.php,v 1.3 2005/02/23 17:38:29 pmjones Exp $ +* +*/ + +/** +* +* Parses for definition lists. +* +* This class implements a Text_Wiki_Parse to find source text marked as a +* definition list. In short, if a line starts with ':' then it is a +* definition list item; another ':' on the same line indicates the end +* of the definition term and the beginning of the definition narrative. +* The list items must be on sequential lines (no blank lines between +* them) -- a blank line indicates the beginning of a new list. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Deflist extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/\n((: ).*\n)(?!(: |\n))/Us'; + + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => + * 'list_start' : the start of a definition list + * 'list_end' : the end of a definition list + * 'term_start' : the start of a definition term + * 'term_end' : the end of a definition term + * 'narr_start' : the start of definition narrative + * 'narr_end' : the end of definition narrative + * 'unknown' : unknown type of definition portion + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A series of text and delimited tokens marking the different + * list text and list elements. + * + */ + + function process(&$matches) + { + // the replacement text we will return to parse() + $return = ''; + + // the list of post-processing matches + $list = array(); + + // start the deflist + $options = array('type' => 'list_start'); + $return .= $this->wiki->addToken($this->rule, $options); + + // $matches[1] is the text matched as a list set by parse(); + // create an array called $list that contains a new set of + // matches for the various definition-list elements. + preg_match_all( + '/^(: )(.*)?( : )(.*)?$/Ums', + $matches[1], + $list, + PREG_SET_ORDER + ); + + // add each term and narrative + foreach ($list as $key => $val) { + $return .= ( + $this->wiki->addToken($this->rule, array('type' => 'term_start')) . + trim($val[2]) . + $this->wiki->addToken($this->rule, array('type' => 'term_end')) . + $this->wiki->addToken($this->rule, array('type' => 'narr_start')) . + trim($val[4]) . + $this->wiki->addToken($this->rule, array('type' => 'narr_end')) + ); + } + + + // end the deflist + $options = array('type' => 'list_end'); + $return .= $this->wiki->addToken($this->rule, $options); + + // done! + return "\n" . $return . "\n\n"; + } +} +?> \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Parse/Default/Delimiter.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Parse/Default/Delimiter.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,80 @@ + +* +* @license LGPL +* +* @version $Id: Delimiter.php,v 1.3 2005/02/23 17:38:29 pmjones Exp $ +* +*/ + +/** +* +* Parses for Text_Wiki delimiter characters already in the source text. +* +* This class implements a Text_Wiki_Parse to find instances of the delimiter +* character already embedded in the source text; it extracts them and replaces +* them with a delimited token, then renders them as the delimiter itself +* when the target format is XHTML. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Delimiter extends Text_Wiki_Parse { + + /** + * + * Constructor. Overrides the Text_Wiki_Parse constructor so that we + * can set the $regex property dynamically (we need to include the + * Text_Wiki $delim character. + * + * @param object &$obj The calling "parent" Text_Wiki object. + * + * @param string $name The token name to use for this rule. + * + */ + + function Text_Wiki_Parse_delimiter(&$obj) + { + parent::Text_Wiki_Parse($obj); + $this->regex = '/' . $this->wiki->delim . '/'; + } + + + /** + * + * Generates a token entry for the matched text. Token options are: + * + * 'text' => The full matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A delimited token number to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + return $this->wiki->addToken( + $this->rule, + array('text' => $this->wiki->delim) + ); + } +} +?> \ No newline at end of file diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Parse/Default/Embed.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Parse/Default/Embed.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,106 @@ + +* +* @license LGPL +* +* @version $Id: Embed.php,v 1.3 2005/02/23 17:38:29 pmjones Exp $ +* +*/ + +/** +* +* Embeds the results of a PHP script at render-time. +* +* This class implements a Text_Wiki_Parse to embed the contents of a URL +* inside the page at render-time. Typically used to get script output. +* This differs from the 'include' rule, which incorporates results at +* parse-time; 'embed' output does not get parsed by Text_Wiki, while +* 'include' ouput does. +* +* This rule is inherently not secure; it allows cross-site scripting to +* occur if the embedded output has +'; + } + } else { + $js = ''; + } + return $js.' +
+'; + case 'endContent': + return ' +
+'; + } + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Horiz.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Horiz.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,51 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Horiz.php,v 1.7 2005/07/30 08:03:28 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders an horizontal bar in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Horiz extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $css = $this->formatConf(' class="%s"', 'css'); + return "\n"; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Html.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Html.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,47 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Html.php,v 1.5 2005/07/30 08:03:28 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders preformated html in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Html extends Text_Wiki_Render { + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return $options['text']; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Image.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Image.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,180 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Image.php,v 1.16 2006/02/10 23:07:03 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class inserts an image in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Image extends Text_Wiki_Render { + + var $conf = array( + 'base' => '/', + 'url_base' => null, + 'css' => null, + 'css_link' => null + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // note the image source + $src = $options['src']; + + // is the source a local file or URL? + if (strpos($src, '://') === false) { + // the source refers to a local file. + // add the URL base to it. + $src = $this->getConf('base', '/') . $src; + } + + // stephane@metacites.net + // is the image clickable? + if (isset($options['attr']['link'])) { + // yes, the image is clickable. + // are we linked to a URL or a wiki page? + if (strpos($options['attr']['link'], '://')) { + // it's a URL, prefix the URL base + $href = $this->getConf('url_base') . $options['attr']['link']; + } else { + // it's a WikiPage; assume it exists. + /** @todo This needs to honor sprintf wikilinks (pmjones) */ + /** @todo This needs to honor interwiki (pmjones) */ + /** @todo This needs to honor freelinks (pmjones) */ + $href = $this->wiki->getRenderConf('xhtml', 'wikilink', 'view_url') . + $options['attr']['link']; + } + } else { + // image is not clickable. + $href = null; + } + // unset so it won't show up as an attribute + unset($options['attr']['link']); + + // stephane@metacites.net -- 25/07/2004 + // we make up an align="center" value for the tag. + if (isset($options['attr']['align']) && + $options['attr']['align'] == 'center') { + + // unset so it won't show up as an attribute + unset($options['attr']['align']); + + // make sure we have a style attribute + if (! isset($options['attr']['style'])) { + // no style, set up a blank one + $options['attr']['style'] = ''; + } else { + // style exists, add a space + $options['attr']['style'] .= ' '; + } + + // add a "center" style to the existing style. + $options['attr']['style'] .= + 'display: block; margin-left: auto; margin-right: auto;'; + } + + // stephane@metacites.net -- 25/07/2004 + // try to guess width and height + if (! isset($options['attr']['width']) && + ! isset($options['attr']['height'])) { + + // does the source refer to a local file or a URL? + if (strpos($src,'://')) { + // is a URL link + $imageFile = $src; + } elseif ($src[0] == '.') { + // reg at dav-muz dot net -- 2005-03-07 + // is a local file on relative path. + $imageFile = $src; # ...don't do anything because it's perfect! + } else { + // is a local file on absolute path. + $imageFile = $_SERVER['DOCUMENT_ROOT'] . $src; + } + + // attempt to get the image size + $imageSize = @getimagesize($imageFile); + + if (is_array($imageSize)) { + $options['attr']['width'] = $imageSize[0]; + $options['attr']['height'] = $imageSize[1]; + } + + } + + // start the HTML output + $output = 'formatConf(' class="%s"', 'css'); + + // add the attributes to the output, and be sure to + // track whether or not we find an "alt" attribute + $alt = false; + foreach ($options['attr'] as $key => $val) { + + // track the 'alt' attribute + if (strtolower($key) == 'alt') { + $alt = true; + } + + // the 'class' attribute overrides the CSS class conf + if (strtolower($key) == 'class') { + $css = null; + } + + $key = $this->textEncode($key); + $val = $this->textEncode($val); + $output .= " $key=\"$val\""; + } + + // always add an "alt" attribute per Stephane Solliec + if (! $alt) { + $alt = $this->textEncode(basename($options['src'])); + $output .= " alt=\"$alt\""; + } + + // end the image tag with the automatic CSS class (if any) + $output .= "$css />"; + + // was the image clickable? + if ($href) { + // yes, add the href and return + $href = $this->textEncode($href); + $css = $this->formatConf(' class="%s"', 'css_link'); + $output = "$output"; + } + + return $output; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Include.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Include.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,32 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Include.php,v 1.6 2005/07/30 08:03:28 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders included maekup in XHTML. (empty) + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Include extends Text_Wiki_Render { + function token() + { + return ''; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Interwiki.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Interwiki.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,103 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Interwiki.php,v 1.14 2006/02/25 05:03:13 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders inter wikis links in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Interwiki extends Text_Wiki_Render { + + var $conf = array( + 'sites' => array( + 'MeatBall' => 'http://www.usemod.com/cgi-bin/mb.pl?%s', + 'Advogato' => 'http://advogato.org/%s', + 'Wiki' => 'http://c2.com/cgi/wiki?%s' + ), + 'target' => '_blank', + 'css' => null + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $text = $options['text']; + if (isset($options['url'])) { + // calculated by the parser (e.g. Mediawiki) + $href = $options['url']; + } else { + $site = $options['site']; + // toggg 2006/02/05 page name must be url encoded (e.g. may contain spaces) + $page = $this->urlEncode($options['page']); + + if (isset($this->conf['sites'][$site])) { + $href = $this->conf['sites'][$site]; + } else { + return $text; + } + + // old form where page is at end, + // or new form with %s placeholder for sprintf()? + if (strpos($href, '%s') === false) { + // use the old form + $href = $href . $page; + } else { + // use the new form + $href = sprintf($href, $page); + } + } + + // allow for alternative targets + $target = $this->getConf('target'); + + // build base link + $css = $this->formatConf(' class="%s"', 'css'); + $text = $this->textEncode($text); + $output = "textEncode($target); + $output .= " onclick=\"window.open(this.href, '$target');"; + $output .= " return false;\""; + } + + $output .= ">$text"; + + return $output; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Italic.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Italic.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,57 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Italic.php,v 1.7 2005/07/30 08:03:28 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders italic text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Italic extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/List.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/List.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,172 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: List.php,v 1.9 2005/11/06 10:38:22 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders bullet and ordered lists in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_List extends Text_Wiki_Render { + + var $conf = array( + 'css_ol' => null, + 'css_ol_li' => null, + 'css_ul' => null, + 'css_ul_li' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * This rendering method is syntactically and semantically compliant + * with XHTML 1.1 in that sub-lists are part of the previous list item. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // make nice variables (type, level, count) + extract($options); + + // set up indenting so that the results look nice; we do this + // in two steps to avoid str_pad mathematics. ;-) + $pad = str_pad('', $level, "\t"); + $pad = str_replace("\t", ' ', $pad); + + switch ($type) { + + case 'bullet_list_start': + + // build the base HTML + $css = $this->formatConf(' class="%s"', 'css_ul'); + $html = ""; + + /* + // if this is the opening block for the list, + // put an extra newline in front of it so the + // output looks nice. + if ($level == 0) { + $html = "\n$html"; + } + */ + + // done! + return $html; + break; + + case 'bullet_list_end': + + // build the base HTML + $html = "\n$pad"; + + // if this is the closing block for the list, + // put extra newlines after it so the output + // looks nice. + if ($level == 0) { + $html .= "\n\n"; + } + + // done! + return $html; + break; + + case 'number_list_start': + if (isset($format)) { + $format = ' type="' . $format . '"'; + } else { + $format = ''; + } + // build the base HTML + $css = $this->formatConf(' class="%s"', 'css_ol'); + $html = ""; + + /* + // if this is the opening block for the list, + // put an extra newline in front of it so the + // output looks nice. + if ($level == 0) { + $html = "\n$html"; + } + */ + + // done! + return $html; + break; + + case 'number_list_end': + + // build the base HTML + $html = "\n$pad"; + + // if this is the closing block for the list, + // put extra newlines after it so the output + // looks nice. + if ($level == 0) { + $html .= "\n\n"; + } + + // done! + return $html; + break; + + case 'bullet_item_start': + case 'number_item_start': + + // pick the proper CSS class + if ($type == 'bullet_item_start') { + $css = $this->formatConf(' class="%s"', 'css_ul_li'); + } else { + $css = $this->formatConf(' class="%s"', 'css_ol_li'); + } + + // build the base HTML + $html = "\n$pad"; + + // for the very first item in the list, do nothing. + // but for additional items, be sure to close the + // previous item. + if ($count > 0) { + $html = "$html"; + } + + // done! + return $html; + break; + + case 'bullet_item_end': + case 'number_item_end': + default: + // ignore item endings and all other types. + // item endings are taken care of by the other types + // depending on their place in the list. + return ''; + break; + } + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Newline.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Newline.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,35 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Newline.php,v 1.5 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders new lines in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Newline extends Text_Wiki_Render { + + + function token($options) + { + return "
\n"; + } +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Page.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Page.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,46 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Page.php,v 1.2 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders page markers in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Page extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return 'PAGE MARKER HERE*&^%$#^$%*PAGEMARKERHERE'; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Paragraph.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Paragraph.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,59 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Paragraph.php,v 1.7 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders paragraphs in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Paragraph extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + extract($options); //type + + if ($type == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($type == 'end') { + return "

\n\n"; + } + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Phplookup.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Phplookup.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,81 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Phplookup.php,v 1.11 2006/02/10 23:07:03 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders a link to php functions description in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Phplookup extends Text_Wiki_Render { + + var $conf = array( + 'target' => '_blank', + 'css' => null + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $text = trim($options['text']); + $css = $this->formatConf(' class="%s"', 'css'); + + // start the html + $output = "getConf('target', ''); + if ($target) { + // use a "popup" window. this is XHTML compliant, suggested by + // Aaron Kalin. uses the $target as the new window name. + $target = $this->textEncode($target); + $output .= " onclick=\"window.open(this.href, '$target');"; + $output .= " return false;\""; + } + + // take off the final parens for functions + if (substr($text, -2) == '()') { + $q = substr($text, 0, -2); + } else { + $q = $text; + } + + // toggg 2006/02/05 page name must be url encoded (e.g. may contain spaces) + $q = $this->urlEncode($q); + $text = $this->textEncode($text); + + // finish and return + $output .= " href=\"http://php.net/$q\">$text"; + return $output; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Plugin.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Plugin.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,47 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Plugin.php,v 1.2 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders wiki plugins in XHTML. (empty) + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Plugin extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * Plugins produce wiki markup so are processed by parsing, no tokens produced + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return ''; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Prefilter.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Prefilter.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,34 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Prefilter.php,v 1.7 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class implements a Text_Wiki_Render_Xhtml to "pre-filter" source text so + * that line endings are consistently \n, lines ending in a backslash \ + * are concatenated with the next line, and tabs are converted to spaces. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Prefilter extends Text_Wiki_Render { + function token() + { + return ''; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Preformatted.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Preformatted.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,46 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Preformatted.php,v 1.2 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders preformated text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Preformatted extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return '
'.$options['text'].'
'; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Raw.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Raw.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,46 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Raw.php,v 1.5 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders not processed blocks in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Raw extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return $options['text']; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Revise.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Revise.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,68 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Revise.php,v 1.7 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders revision marks in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Revise extends Text_Wiki_Render { + + var $conf = array( + 'css_ins' => null, + 'css_del' => null + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'del_start') { + $css = $this->formatConf(' class="%s"', 'css_del'); + return ""; + } + + if ($options['type'] == 'del_end') { + return ""; + } + + if ($options['type'] == 'ins_start') { + $css = $this->formatConf(' class="%s"', 'css_ins'); + return ""; + } + + if ($options['type'] == 'ins_end') { + return ""; + } + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Smiley.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Smiley.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,74 @@ + + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Smiley.php,v 1.2 2006/02/10 23:07:03 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * Smiley rule Xhtml render class + * + * @category Text + * @package Text_Wiki + * @author Bertrand Gugger + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + * @see Text_Wiki::Text_Wiki_Render() + */ +class Text_Wiki_Render_Xhtml_Smiley extends Text_Wiki_Render { + + /** + * Configuration keys for this rule + * 'prefix' => the path to smileys images inclusive file name prefix, + * starts with '/' ==> abolute reference + * if no file names prefix but some folder, terminates with '/' + * 'extension' => the file extension (inclusive '.'), e.g. : + * if prefix 'smileys/icon_' and extension '.gif' + * ':)' whose name is 'smile' will give relative file 'smileys/icon_smile.gif' + * if prefix '/image/smileys/' and extension '.png': absolute '/image/smileys/smile.gif' + * 'css' => optional style applied to smileys + * + * @access public + * @var array 'config-key' => mixed config-value + */ + var $conf = array( + 'prefix' => 'images/smiles/icon_', + 'extension' => '.gif', + 'css' => null + ); + + /** + * Renders a token into text matching the requested format. + * process the Smileys + * + * @access public + * @param array $options The "options" portion of the token (second element). + * @return string The text rendered from the token options. + */ + function token($options) + { + $imageFile = $this->getConf('prefix') . $options['name'] . $this->getConf('extension'); + + // attempt to get the image size + $imageSize = @getimagesize($imageFile); + + // return the HTML output + return '' . $options['desc'] . 'formatConf(' class="%s"', 'css') . ' />'; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Specialchar.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Specialchar.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,52 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Specialchar.php,v 1.2 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders special characters in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_SpecialChar extends Text_Wiki_Render { + + var $types = array('~bs~' => '\', + '~hs~' => ' ', + '~amp~' => '&', + '~ldq~' => '“', + '~rdq~' => '”', + '~lsq~' => '‘', + '~rsq~' => '’', + '~c~' => '©', + '~--~' => '—', + '" -- "' => '—', + '" -- "' => '—', + '~lt~' => '<', + '~gt~' => '>'); + + function token($options) + { + if (isset($this->types[$options['char']])) { + return $this->types[$options['char']]; + } else { + return '&#'.substr($options['char'], 1, -1).';'; + } + } +} + +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Strong.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Strong.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,58 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Strong.php,v 1.7 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders text marked as strong in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Strong extends Text_Wiki_Render { + + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Subscript.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Subscript.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,57 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Subscript.php,v 1.2 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders subscript text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Subscript extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Superscript.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Superscript.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,57 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Superscript.php,v 1.7 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders superscript text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Superscript extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Table.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Table.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,140 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Table.php,v 1.12 2005/12/06 15:29:29 ritzmo Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders tables in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Table extends Text_Wiki_Render { + + var $conf = array( + 'css_table' => null, + 'css_caption' => null, + 'css_tr' => null, + 'css_th' => null, + 'css_td' => null + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // make nice variable names (type, attr, span) + $span = $rowspan = 1; + extract($options); + + // free format + $format = isset($format) ? ' '. $format : ''; + + $pad = ' '; + + switch ($type) { + + case 'table_start': + $css = $this->formatConf(' class="%s"', 'css_table'); + return "\n\n\n"; + break; + + case 'table_end': + return "\n\n"; + break; + + case 'caption_start': + $css = $this->formatConf(' class="%s"', 'css_caption'); + return "\n"; + break; + + case 'caption_end': + return "\n"; + break; + + case 'row_start': + $css = $this->formatConf(' class="%s"', 'css_tr'); + return "$pad\n"; + break; + + case 'row_end': + return "$pad\n"; + break; + + case 'cell_start': + + // base html + $html = $pad . $pad; + + // is this a TH or TD cell? + if ($attr == 'header') { + // start a header cell + $css = $this->formatConf(' class="%s"', 'css_th'); + $html .= "formatConf(' class="%s"', 'css_td'); + $html .= " 1) { + $html .= " colspan=\"$span\""; + } + + // add the row span + if ($rowspan > 1) { + $html .= " rowspan=\"$rowspan\""; + } + + // add alignment + if ($attr != 'header' && $attr != '') { + $html .= " style=\"text-align: $attr;\""; + } + + // done! + $html .= "$format>"; + return $html; + break; + + case 'cell_end': + if ($attr == 'header') { + return "\n"; + } else { + return "\n"; + } + break; + + default: + return ''; + + } + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Tighten.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Tighten.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,34 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Tighten.php,v 1.6 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class makes the tightening in XHTML. (empty) + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Tighten extends Text_Wiki_Render { + + + function token() + { + return ''; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Titlebar.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Titlebar.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,57 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Titlebar.php,v 1.2 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders a title bar in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Titlebar extends Text_Wiki_Render { + + var $conf = array( + 'css' => 'titlebar' + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Toc.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Toc.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,115 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Toc.php,v 1.9 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class inserts a table of content in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Toc extends Text_Wiki_Render { + + var $conf = array( + 'css_list' => null, + 'css_item' => null, + 'title' => 'Table of Contents', + 'div_id' => 'toc', + 'collapse' => true + ); + + var $min = 2; + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // type, id, level, count, attr + extract($options); + + switch ($type) { + + case 'list_start': + + $css = $this->getConf('css_list'); + $html = ''; + + // collapse div within a table? + if ($this->getConf('collapse')) { + $html .= ''; + $html .= "
\n"; + } + + // add the div, class, and id + $html .= 'getConf('div_id'); + if ($div_id) { + $html .= " id=\"$div_id\""; + } + + // add the title, and done + $html .= '>'; + $html .= $this->getConf('title'); + return $html; + break; + + case 'list_end': + if ($this->getConf('collapse')) { + return "\n\n
\n\n"; + } else { + return "\n\n\n"; + } + break; + + case 'item_start': + $html = "\n\tgetConf('css_item'); + if ($css) { + $html .= " class=\"$css\""; + } + + $pad = ($level - $this->min); + $html .= " style=\"margin-left: {$pad}em;\">"; + + $html .= ""; + return $html; + break; + + case 'item_end': + return ""; + break; + } + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Tt.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Tt.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,58 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Tt.php,v 1.7 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders monospaced text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Tt extends Text_Wiki_Render { + + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Underline.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Underline.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,57 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Underline.php,v 1.2 2005/07/30 08:03:29 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders underlined text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Underline extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Url.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Url.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,128 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Url.php,v 1.13 2006/02/10 23:07:03 toggg Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders URL links in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Url extends Text_Wiki_Render { + + + var $conf = array( + 'target' => '_blank', + 'images' => true, + 'img_ext' => array('jpg', 'jpeg', 'gif', 'png'), + 'css_inline' => null, + 'css_footnote' => null, + 'css_descr' => null, + 'css_img' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + global $email; + // create local variables from the options array (text, + // href, type) + extract($options); + + $quote = '"'; + + // find the rightmost dot and determine the filename + // extension. + $pos = strrpos($href, '.'); + $ext = strtolower(substr($href, $pos + 1)); + + if(substr($href, 0, 7) == 'mailto:') + { + $a = substr($href, 7); + $code = $email->encryptEmail($a, '', '', $text); + return $code; + } + + $href = $this->textEncode($href); + + // does the filename extension indicate an image file? + if ($this->getConf('images') && + in_array($ext, $this->getConf('img_ext', array()))) { + + // create alt text for the image + if (! isset($text) || $text == '') { + $text = basename($href); + $text = $this->textEncode($text); + } + + // generate an image tag + $css = $this->formatConf(' class="%s"', 'css_img'); + $output = ""; + + } else { + + // should we build a target clause? + if ($href{0} == '#' || + strtolower(substr($href, 0, 7)) == 'mailto:') { + // targets not allowed for on-page anchors + // and mailto: links. + $target = ''; + } else { + // allow targets on non-anchor non-mailto links + $target = $this->getConf('target'); + } + + // generate a regular link (not an image) + $text = $this->textEncode($text); + $css = $this->formatConf(' class="%s"', "css_$type"); + $output = "textEncode($target); + $output .= " onclick={$quote}window.open(this.href, '$target');"; + $output .= " return false;{$quote}"; + } + + // finish up output + $output .= ">$text"; + + // make numbered references look like footnotes when no + // CSS class specified, make them superscript by default + if ($type == 'footnote' && ! $css) { + $output = '' . $output . ''; + } + } + + return $output; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Render/Xhtml/Wikilink.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Render/Xhtml/Wikilink.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,163 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Wikilink.php,v 1.17 2006/02/28 03:15:09 justinpatrin Exp $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders wiki links in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Wikilink extends Text_Wiki_Render { + + var $conf; + + function Text_Wiki_Render_Xhtml_Wikilink() { + $_utemp = contentPath.'%s'; + $this->conf = array( + 'pages' => array(), // set to null or false to turn off page checks + 'view_url' => $_utemp, + 'new_url' => $_utemp, + 'new_text' => ' [x]', + 'new_text_pos' => false, // 'before', 'after', or null/false + 'css' => null, + 'css_new' => null, + 'exists_callback' => 'isPage' // call_user_func() callback + ); + } + + /** + * + * Renders a token into XHTML. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + global $session; + if($session->sid_super) $as = htmlspecialchars(urlSeparator).'auth='.$session->sid_super; + else $as = ''; + // make nice variable names (page, anchor, text) + extract($options); + + // is there a "page existence" callback? + // we need to access it directly instead of through + // getConf() because we'll need a reference (for + // object instance method callbacks). + if (isset($this->conf['exists_callback'])) { + $callback =& $this->conf['exists_callback']; + } else { + $callback = false; + } + + if ($callback) { + // use the callback function + $exists = call_user_func($callback, $page); + } else { + // no callback, go to the naive page array. + $list = $this->getConf('pages'); + if (is_array($list)) { + // yes, check against the page list + $exists = in_array($page, $list); + } else { + // no, assume it exists + $exists = true; + } + } + + // convert *after* checking against page names so as not to mess + // up what the user typed and what we're checking. + //$page = $this->urlEncode($page); + $anchor = $this->urlEncode($anchor); + $text = $this->textEncode($text); + + // does the page exist? + if ($exists) { + + // PAGE EXISTS. + + // link to the page view, but we have to build + // the HREF. we support both the old form where + // the page always comes at the end, and the new + // form that uses %s for sprintf() + $href = $this->getConf('view_url'); + + if (strpos($href, '%s') === false) { + // use the old form (page-at-end) + $href = $href . $page . $anchor; + } else { + // use the new form (sprintf format string) + $href = sprintf($href, $page . $anchor); + } + + // get the CSS class and generate output + $css = $this->formatConf(' class="%s"', 'css'); + + $start = ''; + $end = ''; + } else { + + // PAGE DOES NOT EXIST. + + // link to the page view, but we have to build + // the HREF. we support both the old form where + // the page always comes at the end, and the new + // form that uses %s for sprintf() + $href = $this->getConf('view_url'); + + if (strpos($href, '%s') === false) { + // use the old form (page-at-end) + $href = $href . $page . $anchor; + } else { + // use the new form (sprintf format string) + $href = sprintf($href, $page . $anchor); + } + + // get the CSS class and generate output + $css = $this->formatConf(' class="%s"', 'css'); + + $start = ''; + $end = ''; + + } + if (!strlen($text)) { + $start .= $this->textEncode($options['page']); + } + if (isset($type)) { + switch ($type) { + case 'start': + $output = $start; + break; + case 'end': + $output = $end; + break; + } + } else { + $output = $start.$text.$end; + } + return $output; + } +} +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiengine/Tables.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiengine/Tables.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,997 @@ + + */ + + global $mStripState, $wgRandomKey; + $mStripState = Array(); + + $attrib = '[a-zA-Z0-9]'; + $space = '[\x09\x0a\x0d\x20]'; + + define( 'MW_CHAR_REFS_REGEX', + '/&([A-Za-z0-9]+); + |&\#([0-9]+); + |&\#x([0-9A-Za-z]+); + |&\#X([0-9A-Za-z]+); + |(&)/x' ); + + define( 'MW_ATTRIBS_REGEX', + "/(?:^|$space)($attrib+) + ($space*=$space* + (?: + # The attribute value: quoted or alone + ".'"'."([^<".'"'."]*)".'"'." + | '([^<']*)' + | ([a-zA-Z0-9!#$%&()*,\\-.\\/:;<>?@[\\]^_`{|}~]+) + | (\#[0-9a-fA-F]+) # Technically wrong, but lots of + # colors are specified like this. + # We'll be normalizing it. + ) + )?(?=$space|\$)/sx" ); + + /** + * emulate mediawiki parser, including stripping, etc. + * + * @param string $text the text to parse + * @return string + * @access public + */ + + function process_tables( $text ) + { + // include some globals, do some parser stuff that would normally be done in the parent parser function + global $mStripState; + $x =& $mStripState; + //$text = mwStrip( $text, $x ); + + // parse the text + $text = doTableStuff($text); + + // Unstrip it + // $text = unstrip( $text, $mStripState ); + // $text = unstripNoWiki( $text, $mStripState ); + //die('
'.print_r($mStripState, true).'
'); + return $text; + } + + /** + * parse the wiki syntax used to render tables + * + * @param string $t the text to parse + * @return string + * @access private + */ + function doTableStuff( $t ) { + + $t = explode ( "\n" , $t ) ; + $td = array () ; # Is currently a td tag open? + $ltd = array () ; # Was it TD or TH? + $tr = array () ; # Is currently a tr tag open? + $ltr = array () ; # tr attributes + $has_opened_tr = array(); # Did this table open a element? + $indent_level = 0; # indent level of the table + foreach ( $t AS $k => $x ) + { + $x = trim ( $x ) ; + $fc = substr ( $x , 0 , 1 ) ; + if ( preg_match( '/^(:*)\{\|(.*)$/', $x, $matches ) ) { + $indent_level = strlen( $matches[1] ); + + $attributes = unstripForHTML( $matches[2] ); + + $t[$k] = str_repeat( '
', $indent_level ) . + '' ; + array_push ( $td , false ) ; + array_push ( $ltd , '' ) ; + array_push ( $tr , false ) ; + array_push ( $ltr , '' ) ; + array_push ( $has_opened_tr, false ); + } + else if ( count ( $td ) == 0 ) { } # Don't do any of the following + else if ( '|}' == substr ( $x , 0 , 2 ) ) { + $z = "" . substr ( $x , 2); + $l = array_pop ( $ltd ) ; + if ( !array_pop ( $has_opened_tr ) ) $z = "" . $z ; + if ( array_pop ( $tr ) ) $z = '' . $z ; + if ( array_pop ( $td ) ) $z = '' . $z ; + array_pop ( $ltr ) ; + $t[$k] = $z . str_repeat( '
', $indent_level ); + } + else if ( '|-' == substr ( $x , 0 , 2 ) ) { # Allows for |--------------- + $x = substr ( $x , 1 ) ; + while ( $x != '' && substr ( $x , 0 , 1 ) == '-' ) $x = substr ( $x , 1 ) ; + $z = '' ; + $l = array_pop ( $ltd ) ; + array_pop ( $has_opened_tr ); + array_push ( $has_opened_tr , true ) ; + if ( array_pop ( $tr ) ) $z = '' . $z ; + if ( array_pop ( $td ) ) $z = '' . $z ; + array_pop ( $ltr ) ; + $t[$k] = $z ; + array_push ( $tr , false ) ; + array_push ( $td , false ) ; + array_push ( $ltd , '' ) ; + $attributes = unstripForHTML( $x ); + array_push ( $ltr , fixTagAttributes( $attributes, 'tr' ) ) ; + } + else if ( '|' == $fc || '!' == $fc || '|+' == substr ( $x , 0 , 2 ) ) { # Caption + # $x is a table row + if ( '|+' == substr ( $x , 0 , 2 ) ) { + $fc = '+' ; + $x = substr ( $x , 1 ) ; + } + $after = substr ( $x , 1 ) ; + if ( $fc == '!' ) $after = str_replace ( '!!' , '||' , $after ) ; + + // Split up multiple cells on the same line. + // FIXME: This can result in improper nesting of tags processed + // by earlier parser steps, but should avoid splitting up eg + // attribute values containing literal "||". + $after = wfExplodeMarkup( '||', $after ); + + $t[$k] = '' ; + + # Loop through each table cell + foreach ( $after AS $theline ) + { + $z = '' ; + if ( $fc != '+' ) + { + $tra = array_pop ( $ltr ) ; + if ( !array_pop ( $tr ) ) $z = '\n" ; + array_push ( $tr , true ) ; + array_push ( $ltr , '' ) ; + array_pop ( $has_opened_tr ); + array_push ( $has_opened_tr , true ) ; + } + + $l = array_pop ( $ltd ) ; + if ( array_pop ( $td ) ) $z = '' . $z ; + if ( $fc == '|' ) $l = 'td' ; + else if ( $fc == '!' ) $l = 'th' ; + else if ( $fc == '+' ) $l = 'caption' ; + else $l = '' ; + array_push ( $ltd , $l ) ; + + # Cell parameters + $y = explode ( '|' , $theline , 2 ) ; + # Note that a '|' inside an invalid link should not + # be mistaken as delimiting cell parameters + if ( strpos( $y[0], '[[' ) !== false ) { + $y = array ($theline); + } + if ( count ( $y ) == 1 ) + $y = "{$z}<{$l}>{$y[0]}" ; + else { + $attributes = unstripForHTML( $y[0] ); + $y = "{$z}<{$l}".fixTagAttributes($attributes, $l).">{$y[1]}" ; + } + $t[$k] .= $y ; + array_push ( $td , true ) ; + } + } + } + + # Closing open td, tr && table + while ( count ( $td ) > 0 ) + { + $l = array_pop ( $ltd ) ; + if ( array_pop ( $td ) ) $t[] = '' ; + if ( array_pop ( $tr ) ) $t[] = '' ; + if ( !array_pop ( $has_opened_tr ) ) $t[] = "" ; + $t[] = '' ; + } + + $t = implode ( "\n" , $t ) ; + + # special case: don't return empty table + if($t == "\n\n
") + $t = ''; + return $t ; + } + + /** + * Take a tag soup fragment listing an HTML element's attributes + * and normalize it to well-formed XML, discarding unwanted attributes. + * Output is safe for further wikitext processing, with escaping of + * values that could trigger problems. + * + * - Normalizes attribute names to lowercase + * - Discards attributes not on a whitelist for the given element + * - Turns broken or invalid entities into plaintext + * - Double-quotes all attribute values + * - Attributes without values are given the name as attribute + * - Double attributes are discarded + * - Unsafe style attributes are discarded + * - Prepends space if there are attributes. + * + * @param string $text + * @param string $element + * @return string + */ + function fixTagAttributes( $text, $element ) { + if( trim( $text ) == '' ) { + return ''; + } + + $stripped = validateTagAttributes( + decodeTagAttributes( $text ), $element ); + + $attribs = array(); + foreach( $stripped as $attribute => $value ) { + $encAttribute = htmlspecialchars( $attribute ); + $encValue = safeEncodeAttribute( $value ); + + $attribs[] = "$encAttribute=".'"'."$encValue".'"'.""; // " + } + return count( $attribs ) ? ' ' . implode( ' ', $attribs ) : ''; + } + + /** + * Encode an attribute value for HTML tags, with extra armoring + * against further wiki processing. + * @param $text + * @return HTML-encoded text fragment + */ + function safeEncodeAttribute( $text ) { + $encValue= encodeAttribute( $text ); + + # Templates and links may be expanded in later parsing, + # creating invalid or dangerous output. Suppress this. + $encValue = strtr( $encValue, array( + '<' => '<', // This should never happen, + '>' => '>', // we've received invalid input + '"' => '"', // which should have been escaped. + '{' => '{', + '[' => '[', + "''" => '''', + 'ISBN' => 'ISBN', + 'RFC' => 'RFC', + 'PMID' => 'PMID', + '|' => '|', + '__' => '__', + ) ); + + return $encValue; + } + + /** + * Encode an attribute value for HTML output. + * @param $text + * @return HTML-encoded text fragment + */ + function encodeAttribute( $text ) { + $encValue = htmlspecialchars( $text ); + + // Whitespace is normalized during attribute decoding, + // so if we've been passed non-spaces we must encode them + // ahead of time or they won't be preserved. + $encValue = strtr( $encValue, array( + "\n" => ' ', + "\r" => ' ', + "\t" => ' ', + ) ); + + return $encValue; + } + + function unstripForHTML( $text ) { + global $mStripState; + $text = unstrip( $text, $mStripState ); + $text = unstripNoWiki( $text, $mStripState ); + return $text; + } + + /** + * Always call this after unstrip() to preserve the order + * + * @private + */ + function unstripNoWiki( $text, &$state ) { + if ( !isset( $state['nowiki'] ) ) { + return $text; + } + + # TODO: good candidate for FSS + $text = strtr( $text, $state['nowiki'] ); + + return $text; + } + + /** + * Take an array of attribute names and values and normalize or discard + * illegal values for the given element type. + * + * - Discards attributes not on a whitelist for the given element + * - Unsafe style attributes are discarded + * + * @param array $attribs + * @param string $element + * @return array + * + * @todo Check for legal values where the DTD limits things. + * @todo Check for unique id attribute :P + */ + function validateTagAttributes( $attribs, $element ) { + $whitelist = array_flip( attributeWhitelist( $element ) ); + $out = array(); + foreach( $attribs as $attribute => $value ) { + if( !isset( $whitelist[$attribute] ) ) { + continue; + } + # Strip javascript "expression" from stylesheets. + # http://msdn.microsoft.com/workshop/author/dhtml/overview/recalc.asp + if( $attribute == 'style' ) { + $value = checkCss( $value ); + if( $value === false ) { + # haxx0r + continue; + } + } + + if ( $attribute === 'id' ) + $value = escapeId( $value ); + + // If this attribute was previously set, override it. + // Output should only have one attribute of each name. + $out[$attribute] = $value; + } + return $out; + } + + /** + * Pick apart some CSS and check it for forbidden or unsafe structures. + * Returns a sanitized string, or false if it was just too evil. + * + * Currently URL references, 'expression', 'tps' are forbidden. + * + * @param string $value + * @return mixed + */ + function checkCss( $value ) { + $stripped = decodeCharReferences( $value ); + + // Remove any comments; IE gets token splitting wrong + $stripped = preg_replace( '!/\\*.*?\\*/!S', '', $stripped ); + $value = $stripped; + + // ... and continue checks + $stripped = preg_replace( '!\\\\([0-9A-Fa-f]{1,6})[ \\n\\r\\t\\f]?!e', + 'codepointToUtf8(hexdec("$1"))', $stripped ); + $stripped = str_replace( '\\', '', $stripped ); + if( preg_match( '/(expression|tps*:\/\/|url\\s*\().*/is', + $stripped ) ) { + # haxx0r + return false; + } + + return $value; + } + + /** + * Decode any character references, numeric or named entities, + * in the text and return a UTF-8 string. + * + * @param string $text + * @return string + * @access public + * @static + */ + function decodeCharReferences( $text ) { + return preg_replace_callback( + MW_CHAR_REFS_REGEX, + 'decodeCharReferencesCallback', + $text ); + } + + /** + * Fetch the whitelist of acceptable attributes for a given + * element name. + * + * @param string $element + * @return array + */ + function attributeWhitelist( $element ) { + static $list; + if( !isset( $list ) ) { + $list = setupAttributeWhitelist(); + } + return isset( $list[$element] ) + ? $list[$element] + : array(); + } + + /** + * @todo Document it a bit + * @return array + */ + function setupAttributeWhitelist() { + $common = array( 'id', 'class', 'lang', 'dir', 'title', 'style' ); + $block = array_merge( $common, array( 'align' ) ); + $tablealign = array( 'align', 'char', 'charoff', 'valign' ); + $tablecell = array( 'abbr', + 'axis', + 'headers', + 'scope', + 'rowspan', + 'colspan', + 'nowrap', # deprecated + 'width', # deprecated + 'height', # deprecated + 'bgcolor' # deprecated + ); + + # Numbers refer to sections in HTML 4.01 standard describing the element. + # See: http://www.w3.org/TR/html4/ + $whitelist = array ( + # 7.5.4 + 'div' => $block, + 'center' => $common, # deprecated + 'span' => $block, # ?? + + # 7.5.5 + 'h1' => $block, + 'h2' => $block, + 'h3' => $block, + 'h4' => $block, + 'h5' => $block, + 'h6' => $block, + + # 7.5.6 + # address + + # 8.2.4 + # bdo + + # 9.2.1 + 'em' => $common, + 'strong' => $common, + 'cite' => $common, + # dfn + 'code' => $common, + # samp + # kbd + 'var' => $common, + # abbr + # acronym + + # 9.2.2 + 'blockquote' => array_merge( $common, array( 'cite' ) ), + # q + + # 9.2.3 + 'sub' => $common, + 'sup' => $common, + + # 9.3.1 + 'p' => $block, + + # 9.3.2 + 'br' => array( 'id', 'class', 'title', 'style', 'clear' ), + + # 9.3.4 + 'pre' => array_merge( $common, array( 'width' ) ), + + # 9.4 + 'ins' => array_merge( $common, array( 'cite', 'datetime' ) ), + 'del' => array_merge( $common, array( 'cite', 'datetime' ) ), + + # 10.2 + 'ul' => array_merge( $common, array( 'type' ) ), + 'ol' => array_merge( $common, array( 'type', 'start' ) ), + 'li' => array_merge( $common, array( 'type', 'value' ) ), + + # 10.3 + 'dl' => $common, + 'dd' => $common, + 'dt' => $common, + + # 11.2.1 + 'table' => array_merge( $common, + array( 'summary', 'width', 'border', 'frame', + 'rules', 'cellspacing', 'cellpadding', + 'align', 'bgcolor', + ) ), + + # 11.2.2 + 'caption' => array_merge( $common, array( 'align' ) ), + + # 11.2.3 + 'thead' => array_merge( $common, $tablealign ), + 'tfoot' => array_merge( $common, $tablealign ), + 'tbody' => array_merge( $common, $tablealign ), + + # 11.2.4 + 'colgroup' => array_merge( $common, array( 'span', 'width' ), $tablealign ), + 'col' => array_merge( $common, array( 'span', 'width' ), $tablealign ), + + # 11.2.5 + 'tr' => array_merge( $common, array( 'bgcolor' ), $tablealign ), + + # 11.2.6 + 'td' => array_merge( $common, $tablecell, $tablealign ), + 'th' => array_merge( $common, $tablecell, $tablealign ), + + # 12.2 + # added by dan + 'a' => array_merge( $common, array( 'href', 'name' ) ), + + # 13.2 + # added by dan + 'img' => array_merge( $common, array( 'src', 'width', 'height', 'alt' ) ), + + # 15.2.1 + 'tt' => $common, + 'b' => $common, + 'i' => $common, + 'big' => $common, + 'small' => $common, + 'strike' => $common, + 's' => $common, + 'u' => $common, + + # 15.2.2 + 'font' => array_merge( $common, array( 'size', 'color', 'face' ) ), + # basefont + + # 15.3 + 'hr' => array_merge( $common, array( 'noshade', 'size', 'width' ) ), + + # XHTML Ruby annotation text module, simple ruby only. + # http://www.w3c.org/TR/ruby/ + 'ruby' => $common, + # rbc + # rtc + 'rb' => $common, + 'rt' => $common, #array_merge( $common, array( 'rbspan' ) ), + 'rp' => $common, + + # For compatibility with the XHTML parser. + 'nowiki' => array(), + 'noinclude' => array(), + 'nodisplay' => array(), + + # XHTML stuff + 'acronym' => $common + ); + return $whitelist; + } + + /** + * Given a value escape it so that it can be used in an id attribute and + * return it, this does not validate the value however (see first link) + * + * @link http://www.w3.org/TR/html401/types.html#type-name Valid characters + * in the id and + * name attributes + * @link http://www.w3.org/TR/html401/struct/links.html#h-12.2.3 Anchors with the id attribute + * + * @bug 4461 + * + * @static + * + * @param string $id + * @return string + */ + function escapeId( $id ) { + static $replace = array( + '%3A' => ':', + '%' => '.' + ); + + $id = urlencode( decodeCharReferences( strtr( $id, ' ', '_' ) ) ); + + return str_replace( array_keys( $replace ), array_values( $replace ), $id ); + } + + /** + * More or less "markup-safe" explode() + * Ignores any instances of the separator inside <...> + * @param string $separator + * @param string $text + * @return array + */ + function wfExplodeMarkup( $separator, $text ) { + $placeholder = "\x00"; + + // Just in case... + $text = str_replace( $placeholder, '', $text ); + + // Trim stuff + $replacer = new ReplacerCallback( $separator, $placeholder ); + $cleaned = preg_replace_callback( '/(<.*?>)/', array( $replacer, 'go' ), $text ); + + $items = explode( $separator, $cleaned ); + foreach( $items as $i => $str ) { + $items[$i] = str_replace( $placeholder, $separator, $str ); + } + + return $items; + } + + class ReplacerCallback { + function ReplacerCallback( $from, $to ) { + $this->from = $from; + $this->to = $to; + } + + function go( $matches ) { + return str_replace( $this->from, $this->to, $matches[1] ); + } + } + + /** + * Return an associative array of attribute names and values from + * a partial tag string. Attribute names are forces to lowercase, + * character references are decoded to UTF-8 text. + * + * @param string + * @return array + */ + function decodeTagAttributes( $text ) { + $attribs = array(); + + if( trim( $text ) == '' ) { + return $attribs; + } + + $pairs = array(); + if( !preg_match_all( + MW_ATTRIBS_REGEX, + $text, + $pairs, + PREG_SET_ORDER ) ) { + return $attribs; + } + + foreach( $pairs as $set ) { + $attribute = strtolower( $set[1] ); + $value = getTagAttributeCallback( $set ); + + // Normalize whitespace + $value = preg_replace( '/[\t\r\n ]+/', ' ', $value ); + $value = trim( $value ); + + // Decode character references + $attribs[$attribute] = decodeCharReferences( $value ); + } + return $attribs; + } + + /** + * Pick the appropriate attribute value from a match set from the + * MW_ATTRIBS_REGEX matches. + * + * @param array $set + * @return string + * @access private + */ + function getTagAttributeCallback( $set ) { + if( isset( $set[6] ) ) { + # Illegal #XXXXXX color with no quotes. + return $set[6]; + } elseif( isset( $set[5] ) ) { + # No quotes. + return $set[5]; + } elseif( isset( $set[4] ) ) { + # Single-quoted + return $set[4]; + } elseif( isset( $set[3] ) ) { + # Double-quoted + return $set[3]; + } elseif( !isset( $set[2] ) ) { + # In XHTML, attributes must have a value. + # For 'reduced' form, return explicitly the attribute name here. + return $set[1]; + } else { + die_friendly('Parser error', "

Tag conditions not met. This should never happen and is a bug.

" ); + } + } + + /** + * Strips and renders nowiki, pre, math, hiero + * If $render is set, performs necessary rendering operations on plugins + * Returns the text, and fills an array with data needed in unstrip() + * If the $state is already a valid strip state, it adds to the state + * + * @param bool $stripcomments when set, HTML comments + * will be stripped in addition to other tags. This is important + * for section editing, where these comments cause confusion when + * counting the sections in the wikisource + * + * @param array dontstrip contains tags which should not be stripped; + * used to prevent stipping of when saving (fixes bug 2700) + * + * @access private + */ + function mwStrip( $text, &$state, $stripcomments = false , $dontstrip = array () ) { + global $wgRandomKey; + $render = true; + + $wgRandomKey = "\x07UNIQ" . dechex(mt_rand(0, 0x7fffffff)) . dechex(mt_rand(0, 0x7fffffff)); + $uniq_prefix =& $wgRandomKey; + $commentState = array(); + + $elements = array( 'nowiki', 'gallery' ); + + # Removing $dontstrip tags from $elements list (currently only 'gallery', fixing bug 2700) + foreach ( $elements AS $k => $v ) { + if ( !in_array ( $v , $dontstrip ) ) continue; + unset ( $elements[$k] ); + } + + $matches = array(); + $text = extractTagsAndParams( $elements, $text, $matches, $uniq_prefix ); + + foreach( $matches as $marker => $data ) { + list( $element, $content, $params, $tag ) = $data; + if( $render ) { + $tagName = strtolower( $element ); + switch( $tagName ) { + case '!--': + // Comment + if( substr( $tag, -3 ) == '-->' ) { + $output = $tag; + } else { + // Unclosed comment in input. + // Close it so later stripping can remove it + $output = "$tag-->"; + } + break; + case 'html': + if( $wgRawHtml ) { + $output = $content; + break; + } + // Shouldn't happen otherwise. :) + case 'nowiki': + $output = wfEscapeHTMLTagsOnly( $content ); + break; + default: + } + } else { + // Just stripping tags; keep the source + $output = $tag; + } + + // Unstrip the output, because unstrip() is no longer recursive so + // it won't do it itself + $output = unstrip( $output, $state ); + + if( !$stripcomments && $element == '!--' ) { + $commentState[$marker] = $output; + } elseif ( $element == 'html' || $element == 'nowiki' ) { + $state['nowiki'][$marker] = $output; + } else { + $state['general'][$marker] = $output; + } + } + + # Unstrip comments unless explicitly told otherwise. + # (The comments are always stripped prior to this point, so as to + # not invoke any extension tags / parser hooks contained within + # a comment.) + if ( !$stripcomments ) { + // Put them all back and forget them + $text = strtr( $text, $commentState ); + } + + return $text; + } + + /** + * Replaces all occurrences of HTML-style comments and the given tags + * in the text with a random marker and returns teh next text. The output + * parameter $matches will be an associative array filled with data in + * the form: + * 'UNIQ-xxxxx' => array( + * 'element', + * 'tag content', + * array( 'param' => 'x' ), + * 'tag content' ) ) + * + * @param $elements list of element names. Comments are always extracted. + * @param $text Source text string. + * @param $uniq_prefix + * + * @access private + * @static + */ + function extractTagsAndParams($elements, $text, &$matches, $uniq_prefix = ''){ + static $n = 1; + $stripped = ''; + $matches = array(); + + $taglist = implode( '|', $elements ); + $start = "/<($taglist)(\\s+[^>]*?|\\s*?)(\/?>)|<(!--)/i"; + + while ( '' != $text ) { + $p = preg_split( $start, $text, 2, PREG_SPLIT_DELIM_CAPTURE ); + $stripped .= $p[0]; + if( count( $p ) < 5 ) { + break; + } + if( count( $p ) > 5 ) { + // comment + $element = $p[4]; + $attributes = ''; + $close = ''; + $inside = $p[5]; + } else { + // tag + $element = $p[1]; + $attributes = $p[2]; + $close = $p[3]; + $inside = $p[4]; + } + + $marker = "$uniq_prefix-$element-" . sprintf('%08X', $n++) . '-QINU'; + $stripped .= $marker; + + if ( $close === '/>' ) { + // Empty element tag, + $content = null; + $text = $inside; + $tail = null; + } else { + if( $element == '!--' ) { + $end = '/(-->)/'; + } else { + $end = "/(<\\/$element\\s*>)/i"; + } + $q = preg_split( $end, $inside, 2, PREG_SPLIT_DELIM_CAPTURE ); + $content = $q[0]; + if( count( $q ) < 3 ) { + # No end tag -- let it run out to the end of the text. + $tail = ''; + $text = ''; + } else { + $tail = $q[1]; + $text = $q[2]; + } + } + + $matches[$marker] = array( $element, + $content, + decodeTagAttributes( $attributes ), + "<$element$attributes$close$content$tail" ); + } + return $stripped; + } + + /** + * Escape html tags + * Basically replacing " > and < with HTML entities ( ", >, <) + * + * @param $in String: text that might contain HTML tags. + * @return string Escaped string + */ + function wfEscapeHTMLTagsOnly( $in ) { + return str_replace( + array( '"', '>', '<' ), + array( '"', '>', '<' ), + $in ); + } + + /** + * Restores pre, math, and other extensions removed by strip() + * + * always call unstripNoWiki() after this one + * @private + */ + function unstrip( $text, &$state ) { + if ( !isset( $state['general'] ) ) { + return $text; + } + + # TODO: good candidate for FSS + $text = strtr( $text, $state['general'] ); + + return $text; + } + + /** + * Return UTF-8 string for a codepoint if that is a valid + * character reference, otherwise U+FFFD REPLACEMENT CHARACTER. + * @param int $codepoint + * @return string + * @private + */ + function decodeChar( $codepoint ) { + if( validateCodepoint( $codepoint ) ) { + return codepointToUtf8( $codepoint ); + } else { + return UTF8_REPLACEMENT; + } + } + + /** + * If the named entity is defined in the HTML 4.0/XHTML 1.0 DTD, + * return the UTF-8 encoding of that character. Otherwise, returns + * pseudo-entity source (eg &foo;) + * + * @param string $name + * @return string + */ + function decodeEntity( $name ) { + global $wgHtmlEntities; + if( isset( $wgHtmlEntities[$name] ) ) { + return codepointToUtf8( $wgHtmlEntities[$name] ); + } else { + return "&$name;"; + } + } + + /** + * Returns true if a given Unicode codepoint is a valid character in XML. + * @param int $codepoint + * @return bool + */ + function validateCodepoint( $codepoint ) { + return ($codepoint == 0x09) + || ($codepoint == 0x0a) + || ($codepoint == 0x0d) + || ($codepoint >= 0x20 && $codepoint <= 0xd7ff) + || ($codepoint >= 0xe000 && $codepoint <= 0xfffd) + || ($codepoint >= 0x10000 && $codepoint <= 0x10ffff); + } + +/** + * Return UTF-8 sequence for a given Unicode code point. + * May die if fed out of range data. + * + * @param $codepoint Integer: + * @return String + * @public + */ +function codepointToUtf8( $codepoint ) { + if($codepoint < 0x80) return chr($codepoint); + if($codepoint < 0x800) return chr($codepoint >> 6 & 0x3f | 0xc0) . + chr($codepoint & 0x3f | 0x80); + if($codepoint < 0x10000) return chr($codepoint >> 12 & 0x0f | 0xe0) . + chr($codepoint >> 6 & 0x3f | 0x80) . + chr($codepoint & 0x3f | 0x80); + if($codepoint < 0x110000) return chr($codepoint >> 18 & 0x07 | 0xf0) . + chr($codepoint >> 12 & 0x3f | 0x80) . + chr($codepoint >> 6 & 0x3f | 0x80) . + chr($codepoint & 0x3f | 0x80); + + echo "Asked for code outside of range ($codepoint)\n"; + die( -1 ); +} + + /** + * @param string $matches + * @return string + */ + function decodeCharReferencesCallback( $matches ) { + if( $matches[1] != '' ) { + return Sanitizer::decodeEntity( $matches[1] ); + } elseif( $matches[2] != '' ) { + return Sanitizer::decodeChar( intval( $matches[2] ) ); + } elseif( $matches[3] != '' ) { + return Sanitizer::decodeChar( hexdec( $matches[3] ) ); + } elseif( $matches[4] != '' ) { + return Sanitizer::decodeChar( hexdec( $matches[4] ) ); + } + # Last case should be an ampersand by itself + return $matches[0]; + } + +?> diff -r 902822492a68 -r fe660c52c48f includes/wikiformat.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/wikiformat.php Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,615 @@ + + * @license http://www.gnu.org/licenses/lgpl.html + * @version CVS: $Id: Wiki.php,v 1.44 2006/03/02 04:04:59 justinpatrin Exp $ + * @link http://wiki.ciaweb.net/yawiki/index.php?area=Text_Wiki + * + * This code was modified for use in Enano. The Text_Wiki engine is licensed + * under the GNU Lesser General Public License; see + * http://www.gnu.org/licenses/lgpl.html for details. + * + */ + +require_once ENANO_ROOT.'/includes/wikiengine/Parse.php'; +require_once ENANO_ROOT.'/includes/wikiengine/Render.php'; + +class Text_Wiki { + + var $rules = array( + 'Prefilter', + 'Delimiter', + 'Code', + 'Function', + 'Html', + 'Raw', + 'Include', + 'Embed', + 'Anchor', + 'Heading', + 'Toc', + 'Horiz', + 'Break', + 'Blockquote', + 'List', + 'Deflist', + 'Table', + 'Image', + 'Phplookup', + 'Center', + 'Newline', + 'Paragraph', + 'Url', + 'Freelink', + 'Interwiki', + 'Wikilink', + 'Colortext', + 'Strong', + 'Bold', + 'Emphasis', + 'Italic', + 'Underline', + 'Tt', + 'Superscript', + 'Subscript', + 'Revise', + 'Tighten' + ); + + var $disable = array( + 'Html', + 'Include', + 'Embed' + ); + + var $parseConf = array(); + + var $renderConf = array( + 'Docbook' => array(), + 'Latex' => array(), + 'Pdf' => array(), + 'Plain' => array(), + 'Rtf' => array(), + 'Xhtml' => array() + ); + + var $formatConf = array( + 'Docbook' => array(), + 'Latex' => array(), + 'Pdf' => array(), + 'Plain' => array(), + 'Rtf' => array(), + 'Xhtml' => array() + ); + var $delim = "\xFF"; + var $tokens = array(); + var $_countRulesTokens = array(); + var $source = ''; + var $parseObj = array(); + var $renderObj = array(); + var $formatObj = array(); + var $path = array( + 'parse' => array(), + 'render' => array() + ); + var $_dirSep = DIRECTORY_SEPARATOR; + function Text_Wiki($rules = null) + { + if (is_array($rules)) { + $this->rules = $rules; + } + + $this->addPath( + 'parse', + $this->fixPath(ENANO_ROOT) . 'includes/wikiengine/Parse/Default/' + ); + $this->addPath( + 'render', + $this->fixPath(ENANO_ROOT) . 'includes/wikiengine/Render/' + ); + + } + + function &singleton($parser = 'Default', $rules = null) + { + static $only = array(); + if (!isset($only[$parser])) { + $ret =& Text_Wiki::factory($parser, $rules); + if (!$ret) { + return $ret; + } + $only[$parser] =& $ret; + } + return $only[$parser]; + } + + function &factory($parser = 'Default', $rules = null) + { + $d=getcwd(); + chdir(ENANO_ROOT); + + $class = 'Text_Wiki_' . $parser; + $c2 = '._includes_wikiengine_' . $parser; + $file = str_replace('_', '/', $c2).'.php'; + if (!class_exists($class)) { + $fp = @fopen($file, 'r', true); + if ($fp === false) { + die_semicritical('Wiki formatting engine error', '

Could not find file '.$file.' in include_path

'); + } + fclose($fp); + include_once($file); + if (!class_exists($class)) { + die_semicritical('Wiki formatting engine error', '

Class '.$class.' does not exist after including '.$file.'

'); + } + } + + chdir($d); + + $obj =& new $class($rules); + return $obj; + } + + function setParseConf($rule, $arg1, $arg2 = null) + { + $rule = ucwords(strtolower($rule)); + + if (! isset($this->parseConf[$rule])) { + $this->parseConf[$rule] = array(); + } + + if (is_array($arg1)) { + $this->parseConf[$rule] = $arg1; + } else { + $this->parseConf[$rule][$arg1] = $arg2; + } + } + + function getParseConf($rule, $key = null) + { + $rule = ucwords(strtolower($rule)); + + if (! isset($this->parseConf[$rule])) { + return null; + } + + if (is_null($key)) { + return $this->parseConf[$rule]; + } + + if (isset($this->parseConf[$rule][$key])) { + return $this->parseConf[$rule][$key]; + } else { + return null; + } + } + + function setRenderConf($format, $rule, $arg1, $arg2 = null) + { + $format = ucwords(strtolower($format)); + $rule = ucwords(strtolower($rule)); + + if (! isset($this->renderConf[$format])) { + $this->renderConf[$format] = array(); + } + + if (! isset($this->renderConf[$format][$rule])) { + $this->renderConf[$format][$rule] = array(); + } + + if (is_array($arg1)) { + $this->renderConf[$format][$rule] = $arg1; + } else { + $this->renderConf[$format][$rule][$arg1] = $arg2; + } + } + + function getRenderConf($format, $rule, $key = null) + { + $format = ucwords(strtolower($format)); + $rule = ucwords(strtolower($rule)); + + if (! isset($this->renderConf[$format]) || + ! isset($this->renderConf[$format][$rule])) { + return null; + } + + if (is_null($key)) { + return $this->renderConf[$format][$rule]; + } + + if (isset($this->renderConf[$format][$rule][$key])) { + return $this->renderConf[$format][$rule][$key]; + } else { + return null; + } + + } + + function setFormatConf($format, $arg1, $arg2 = null) + { + if (! is_array($this->formatConf[$format])) { + $this->formatConf[$format] = array(); + } + + if (is_array($arg1)) { + $this->formatConf[$format] = $arg1; + } else { + $this->formatConf[$format][$arg1] = $arg2; + } + } + + function getFormatConf($format, $key = null) + { + if (! isset($this->formatConf[$format])) { + return null; + } + + if (is_null($key)) { + return $this->formatConf[$format]; + } + + if (isset($this->formatConf[$format][$key])) { + return $this->formatConf[$format][$key]; + } else { + return null; + } + } + + function insertRule($name, $tgt = null) + { + $name = ucwords(strtolower($name)); + if (! is_null($tgt)) { + $tgt = ucwords(strtolower($tgt)); + } + if (in_array($name, $this->rules)) { + return null; + } + + if (! is_null($tgt) && $tgt != '' && + ! in_array($tgt, $this->rules)) { + return false; + } + + if (is_null($tgt)) { + $this->rules[] = $name; + return true; + } + + if ($tgt == '') { + array_unshift($this->rules, $name); + return true; + } + + $tmp = $this->rules; + $this->rules = array(); + + foreach ($tmp as $val) { + $this->rules[] = $val; + if ($val == $tgt) { + $this->rules[] = $name; + } + } + + return true; + } + + function deleteRule($name) + { + $name = ucwords(strtolower($name)); + $key = array_search($name, $this->rules); + if ($key !== false) { + unset($this->rules[$key]); + } + } + + function changeRule($old, $new) + { + $old = ucwords(strtolower($old)); + $new = ucwords(strtolower($new)); + $key = array_search($old, $this->rules); + if ($key !== false) { + $this->deleteRule($new); + $this->rules[$key] = $new; + } + } + + function enableRule($name) + { + $name = ucwords(strtolower($name)); + $key = array_search($name, $this->disable); + if ($key !== false) { + unset($this->disable[$key]); + } + } + + function disableRule($name) + { + $name = ucwords(strtolower($name)); + $key = array_search($name, $this->disable); + if ($key === false) { + $this->disable[] = $name; + } + } + + function transform($text, $format = 'Xhtml') + { + $this->parse($text); + return $this->render($format); + } + + function parse($text) + { + $this->source = $text; + + $this->tokens = array(); + $this->_countRulesTokens = array(); + + foreach ($this->rules as $name) { + if (! in_array($name, $this->disable)) { + $this->loadParseObj($name); + + if (is_object($this->parseObj[$name])) { + $this->parseObj[$name]->parse(); + } + } + } + } + + function render($format = 'Xhtml') + { + $format = ucwords(strtolower($format)); + + $output = ''; + + $in_delim = false; + + $key = ''; + + $result = $this->loadFormatObj($format); + if ($this->isError($result)) { + return $result; + } + + if (is_object($this->formatObj[$format])) { + $output .= $this->formatObj[$format]->pre(); + } + + foreach (array_keys($this->_countRulesTokens) as $rule) { + $this->loadRenderObj($format, $rule); + } + + $k = strlen($this->source); + for ($i = 0; $i < $k; $i++) { + + $char = $this->source{$i}; + + if ($in_delim) { + + if ($char == $this->delim) { + + $key = (int)$key; + $rule = $this->tokens[$key][0]; + $opts = $this->tokens[$key][1]; + $output .= $this->renderObj[$rule]->token($opts); + $in_delim = false; + + } else { + + $key .= $char; + + } + + } else { + + if ($char == $this->delim) { + $key = ''; + $in_delim = true; + } else { + $output .= $char; + } + } + } + + if (is_object($this->formatObj[$format])) { + $output .= $this->formatObj[$format]->post(); + } + + return $output; + } + + function getSource() + { + return $this->source; + } + + function getTokens($rules = null) + { + if (is_null($rules)) { + return $this->tokens; + } else { + settype($rules, 'array'); + $result = array(); + foreach ($this->tokens as $key => $val) { + if (in_array($val[0], $rules)) { + $result[$key] = $val; + } + } + return $result; + } + } + + function addToken($rule, $options = array(), $id_only = false) + { + static $id; + if (! isset($id)) { + $id = 0; + } else { + $id ++; + } + + settype($options, 'array'); + + $this->tokens[$id] = array( + 0 => $rule, + 1 => $options + ); + if (!isset($this->_countRulesTokens[$rule])) { + $this->_countRulesTokens[$rule] = 1; + } else { + ++$this->_countRulesTokens[$rule]; + } + + if ($id_only) { + return $id; + } else { + return $this->delim . $id . $this->delim; + } + } + + function setToken($id, $rule, $options = array()) + { + $oldRule = $this->tokens[$id][0]; + $this->tokens[$id] = array( + 0 => $rule, + 1 => $options + ); + if ($rule != $oldRule) { + if (!($this->_countRulesTokens[$oldRule]--)) { + unset($this->_countRulesTokens[$oldRule]); + } + if (!isset($this->_countRulesTokens[$rule])) { + $this->_countRulesTokens[$rule] = 1; + } else { + ++$this->_countRulesTokens[$rule]; + } + } + } + + function loadParseObj($rule) + { + $rule = ucwords(strtolower($rule)); + $file = $rule . '.php'; + $class = "Text_Wiki_Parse_$rule"; + + if (! class_exists($class)) { + $loc = $this->findFile('parse', $file); + if ($loc) { + include_once $loc; + } else { + $this->parseObj[$rule] = null; + return $this->error( + "Parse rule '$rule' not found" + ); + } + } + + $this->parseObj[$rule] =& new $class($this); + + } + + function loadRenderObj($format, $rule) + { + $format = ucwords(strtolower($format)); + $rule = ucwords(strtolower($rule)); + $file = "$format/$rule.php"; + $class = "Text_Wiki_Render_$format" . "_$rule"; + + if (! class_exists($class)) { + $loc = $this->findFile('render', $file); + if ($loc) { + include_once $loc; + } else { + return $this->error( + "Render rule '$rule' in format '$format' not found" + ); + } + } + + $this->renderObj[$rule] =& new $class($this); + } + + function loadFormatObj($format) + { + $format = ucwords(strtolower($format)); + $file = $format . '.php'; + $class = "Text_Wiki_Render_$format"; + + if (! class_exists($class)) { + $loc = $this->findFile('render', $file); + if ($loc) { + include_once $loc; + } else { + return $this->error( + "Rendering format class '$class' not found" + ); + } + } + + $this->formatObj[$format] =& new $class($this); + } + + function addPath($type, $dir) + { + $dir = $this->fixPath($dir); + if (! isset($this->path[$type])) { + $this->path[$type] = array($dir); + } else { + array_unshift($this->path[$type], $dir); + } + } + + function getPath($type = null) + { + if (is_null($type)) { + return $this->path; + } elseif (! isset($this->path[$type])) { + return array(); + } else { + return $this->path[$type]; + } + } + + function findFile($type, $file) + { + $set = $this->getPath($type); + + foreach ($set as $path) { + $fullname = $path . $file; + if (file_exists($fullname) && is_readable($fullname)) { + return $fullname; + } + } + + return false; + } + + function fixPath($path) + { + $len = strlen($this->_dirSep); + + if (! empty($path) && + substr($path, -1 * $len, $len) != $this->_dirSep) { + return $path . $this->_dirSep; + } else { + return $path; + } + } + + function &error($message) + { + die($message); + } + + function isError(&$obj) + { + return is_a($obj, 'PEAR_Error'); + } +} + +?>