around the generated // line. this is useful for making html of headlines and titles. as it is not too // handy to have

inside your

// function any_wiki_tohtml ( $s, $options = array() ) { list($tk, $tk_s) = wiki_tokenizer($s, $options); $block = array(); // lines for current block $line = array(); // stacked tokens for current line $line_s = array(); // stacked texts for current line $html = ''; // generated html $i = 0; $toc = false; // is there a toc or not? do { $tok = $tk[$i]; switch ($tok) { case 'br': $line[] = 'html'; $line_s[] = "
\n"; break; case 'html': list($_html, $block) = _wiki_reduce_line($block, $line, $line_s); $html .= $_html . _wiki_reduce_block($block); if (wiki_allow_html()) { $html .= "\n" . $tk_s[$i] . "\n"; } else { $html .= '

' . nl2br(strip_tags($tk_s[$i])) . '

'; } $line = array(); $line_s = array(); $block = array(); break; case 'code': list($_html, $block) = _wiki_reduce_line($block, $line, $line_s); $html .= $_html . _wiki_reduce_block($block); $html .= "\n
\n" . htmlspecialchars($tk_s[$i]) . "
\n"; $line = array(); $line_s = array(); $block = array(); break; case 'p': case 'end': list($_html, $block) = _wiki_reduce_line($block, $line, $line_s); $html .= $_html . "\n" . _wiki_reduce_block($block); $line = array(); $line_s = array(); $block = array(); break; case 'newline': list($_html, $block) = _wiki_reduce_line($block, $line, $line_s); $html .= $_html; $line = array(); $line_s = array(); break; case 'toc': $html .= ''; $line = array(); $line_s = array(); $toc = true; break; case 'comment': if ($i == 0) { // Comment at the start of a line or in a block $html .= ''; } else { // Comment in a line list($line, $line_s) = _wiki_shift_reduce($line, $line_s, $tok, $tk_s[$i]); } break; case 'word': case ' ': default: list($line, $line_s) = _wiki_shift_reduce($line, $line_s, $tok, $tk_s[$i]); break; } $i++; } while ($tok != 'end'); // Merge

's over more than one line $html = preg_replace("|

\n

|", "
\n", $html); //PH: \-newline to "skip" newlining. $html = preg_replace("|\\\\
|", "", $html); if (!empty($options['target']) && $options['target'] == 'line') { // Strip the

tags... the user wants a single line. $html = trim(preg_replace('||', ' ', $html)); } else if ($toc) { $html = _wiki_toc($html); } return trim($html); } // Function: wiki_allow_html // Access: EXTERNAL // Parameters: - // Returns: false no html allowed // true html allowed // Description: Check if the ACL allows html entry // function wiki_allow_html ( ) { $allow = false; if (isset($GLOBALS['any_acl'])) { $allow = $GLOBALS['any_acl']->allowHtml(); } return $allow; } // Function: wiki_filter_uri // Access: EXTERNAL // Parameters: $uri uri to be checked // Returns: false when uri not allowed // uri when allowed // Description: Check if the ACL allows the given uri // function wiki_filter_uri ( $uri ) { if (isset($GLOBALS['any_acl'])) { $uri = $GLOBALS['any_acl']->filterUri($uri); } return $uri; } // Function: wiki_filter_attrs // Access: EXTERNAL // Parameters: $uri uri to be checked // Returns: false when uri not allowed // uri when allowed // Description: Check if the ACL allows the given attrs. // This function has a short whitelist of allowed attributes. // function wiki_filter_attrs ( $attr ) { $as = array(); foreach ($attr as $a => $v) { switch ($a) { case 'id': case 'name': case 'align': case 'valign': case 'title': case 'width': case 'height': case 'rel': case 'alt': case 'class': case 'link': case 'caption': $as[$a] = $v; break; default: if ( isset($GLOBALS['any_acl']) && $GLOBALS['any_acl']->allowHtml()) { $as[$a] = $v; } break; } } return $as; } // Function: _wiki_reduce_block // Access: INTERNAL // Parameters: $block the tokens in the block // Returns: html fragment // Description: Force the complete reduction of a block to html // function _wiki_reduce_block ( $block ) { if (count($block) > 0) { list($html, $block) = _wiki_shift_reduce_block($block, array('end'), array('')); } else { $html = ''; } return $html; } // Function: _wiki_shift_reduce_block // Access: INTERNAL // Parameters: $block the tokens in the block // $line line tokens // $line_s line strings // Returns: array(html-fragment, block) // Description: (Partially) reduces the block after encountering the given line // // Checks for: // - enumerated lists // - tables // - blockquote // // // Each block entry is as follows: // // ( class, depth, class-parms, line_tokens, line_strings ) // // Where class is one of: // // table, ul, ol, blockquote, dl // // Depth is valid for: // // ul, ol, blockqoute // function _wiki_shift_reduce_block ( $block, $line, $line_s ) { if (!empty($line)) { if ($line[0] == '=' && @$line[1] == ' ') { $html = _wiki_reduce_block($block); list($line, $line_s) = _wiki_merge($line, $line_s, 2, false, true); $html .= "\n

" . $line_s[2] . "

\n"; return array($html, array()); } } $block_line = _wiki_block_line($line, $line_s); if ($block_line[0] == 'p' || $block_line[0] == 'end') { $html = _wiki_reduce_block_lines($block); if ($block_line[0] == 'p') { list($line, $line_s) = _wiki_merge($line, $line_s, 0, false, true); if (!empty($line_s[0])) { $html .= "

" . $line_s[0] . "

\n"; } } $block = array(); } else { $block[] = $block_line; $html = ''; } return array($html, $block); } // Function: _wiki_reduce_block_lines // Access: INTERNAL // Parameters: $block a complete block // Returns: html // Description: recursively reduces a block to html // all line level reductions have been done // what we get is a block of lines, each preparsed. // function _wiki_reduce_block_lines ( &$block ) { if (empty($block)) { return ''; } $len = count($block); $class = $block[0][0]; $depth = $block[0][1]; // Collect all lines with the same class and depth $sub_block = array(); $sub_block[] = array_shift($block); if ($class == 'ol') { $alt_class = 'ul'; } else if ($class == 'ul') { $alt_class = 'ol'; } else { $alt_class = false; } while ( !empty($block) && $block[0][1] >= $depth && ( $block[0][0] == $class || $block[0][0] == $alt_class)) { if ($block[0][1] > $depth || $block[0][0] != $class) { // this is a nested block of the same kind // reduce this one separately and remember the html in the previous block line $html = _wiki_reduce_block_lines($block); if (!empty($html)) { $sub_block[count($sub_block)-1][5] = $html; } } else { $sub_block[] = array_shift($block); } } // special handling for a table $td = 0; if ($class == 'table') { foreach ($sub_block as $sub) { $td = max($td, $sub[2]); } } // generate the html for the captured block $html = "<$class>\n"; $nr = 0; foreach ($sub_block as $sub) { $pars = $sub[2]; $line = $sub[3]; $line_s = $sub[4]; $nested = isset($sub[5]) ? $sub[5] : ''; $nr++; switch ($class) { case 'ol': case 'ul': list($line, $line_s) = _wiki_merge($line, $line_s, 2, false, true); $html .= '
  • ' . trim($line_s[2]) . $nested . "
  • \n"; break; case 'table': // Generate a row $html .= _wiki_table_row($td, $line, $line_s, $pars); break; case 'blockquote': if ($nr == 1) { $html .= '

    '; } list($line, $line_s) = _wiki_merge($line, $line_s, 2, false, true); $html .= $line_s[2] . $nested; if ($nr != count($sub_block)) { $html .= '
    '; } else { $html .= "

    \n"; } break; case 'dl': // $pars is the offset of the first ' ' of the ' : ' separating the dt from the dd list($line, $line_s) = _wiki_merge($line, $line_s, $pars+3, false, true); // the reduced html of the dd $dd = array_pop($line_s); array_pop($line); // op the ' ' ':' ' '; array_pop($line_s); array_pop($line); array_pop($line_s); array_pop($line); array_pop($line_s); array_pop($line); // Reduce the dt part list($line, $line_s) = _wiki_merge($line, $line_s, 2, false, true); $dt = array_pop($line_s); $html .= "
    $dt
    \n
    $dd
    \n"; break; } } $html .= "\n\n"; return $html; } // Function: _wiki_table_row // Access: INTERNAL // Parameters: $table_cols nr of tds // $line tokens in line // $line_s text of tokens // Returns: html for row // Description: generates the html for a row // function _wiki_table_row ( $table_cols, $line, $line_s ) { $html = ""; $len = count($line); $td = array(); $start = 1; $colspan= 1; // Split the line in tds for ($i=1;$i<$len;$i++) { if ($line[$i] == '||') { if ($line[$i-1] == '||' && $i+1 < $len) { $colspan++; $start++; } else { // A td from $start to $i-1 if ($i - $start > 0) { $td[] = array( array_slice($line, $start, $i - $start), array_slice($line_s, $start, $i - $start), $colspan); } else { $td[] = array(false, false, $colspan); } $start = $i+1; $colspan = 1; } } } // Generate the html per td foreach ($td as $t) { $line = $t[0]; $line_s = $t[1]; if ($t[2] > 1) { $colspan = ' colspan="' . $t[2] . '" '; } else { $colspan = ''; } if (!empty($line)) { $end = ""; switch ($line[0]) { case '>': $html .= "\n "; $start = 1; break; case '<': $html .= "\n "; $start = 1; break; case '=': $html .= "\n "; $start = 1; break; case '~': $html .= "\n "; $end = ""; $start = 1; break; default: $html .= "\n "; $start = 0; break; } list($line, $line_s) = _wiki_merge($line, $line_s, $start, false, true); $html .= trim($line_s[$start]) . $end; } else { $html .= "\n "; } } $html .= "\n\n"; return $html; } // Function: _wiki_block_line // Access: INTERNAL // Parameters: $line line tokens // $line_s line strings // Returns: a block line entry // Description: checks the line to see what kind of block line the line is // function _wiki_block_line ( $line, $line_s ) { $len = count($line); if ($len >= 2) { // : term : definition if ( $line[0] == ':' && $line[1] == ' ') { // Try to find (' ', ':' , ' '); $i = 2; $offs = false; while ($i < $len - 2 && $offs === false) { if ($line[$i] == ':' && $line[$i-1] == ' ' && $line[$i+1] == ' ') { $offs = $i-1; } $i++; } if ($offs !== false) { return array('dl', 0, $offs, $line, $line_s); } } // || td || .. || if ($line[0] == '||' && $line[$len-1] == '||') { // count the number of cols $cols = 0; for ($i = 0; $i<$len; $i++) { if ($line[$i] == '||') { $cols++; } } return array('table', 0, $cols-1, $line, $line_s); } // > block quoted text if ($line[0] == '>' && $line[1] == ' ') { return array('blockquote', strlen($line_s[0]), 0, $line, $line_s); } // * unordered list if ($line[0] == '*' && $line[1] == ' ') { return array('ul', 0, 0, $line, $line_s); } if ($line[0] == ' ' && $line[1] == '*' && $line[2] == ' ') { return array('ul', strlen($line_s[0]), 0, $line, $line_s); } // # ordered list if ($line[0] == '#' && $line[1] == ' ') { return array('ol', 0, 0, $line, $line_s); } if ($line[0] == ' ' && $line[1] == '#' && $len > 2 && $line[2] == ' ') { return array('ol', strlen($line_s[0]), 0, $line, $line_s); } } // Just another part of a paragraph if ($len > 0 && $line[0] == 'end') { return array('end', 0, 0, $line, $line_s); } else { return array('p', 0, 0, $line, $line_s); } } // Function: _wiki_reduce_line // Access: INTERNAL // Parameters: $block the tokens in the block // $line the line stack // $line_s line texts // Returns: html fragment // modified block // Description: Reduce the current line and append it to the current block. // The reduction of a single line checks for: // - non reduced :// or mailto: urls // - non reduced wiki words // - headers // - blockquote levels // - enumerated lists // - table rows // function _wiki_reduce_line ( $block, $line, $line_s ) { // wiki words list($line, $line_s) = _wiki_replace_wikiwords($line, $line_s); if (count($line) == 1 && $line[0] == '-' && (strlen($line_s[0]) == 4 || strlen($line_s[0]) == 3)) { // horiz \n----\n $html = _wiki_reduce_block($block); return array($html . "\n
    \n", array()); } if (count($line) > 2 && $line[0] == '+' && $line[1] == ' ' && strlen($line_s[0]) <= 6) { // \n+++++ headline 1..6 list($line, $line_s) = _wiki_merge($line, $line_s, 2, false, true); $html = _wiki_reduce_block($block); $level = strlen($line_s[0]); $html .= "\n".trim($line_s[2])."\n"; return array($html, array()); } return _wiki_shift_reduce_block($block, $line, $line_s); } // Function: _wiki_shift_reduce // Access: INTERNAL // Parameters: $line // $line_s // $tok // $tok_s // Returns: the new line state // Description: Shifts the given token on the stack and reduces the stack // returning a new line state. // function _wiki_shift_reduce ( $line, $line_s, $tok, $tok_s ) { switch ($tok) { case "u": // "__" $offs = _wiki_offset($line, _wiki_inline_start($tok)); if ($offs !== false) { list($line, $line_s) = _wiki_merge($line, $line_s, $offs+1, false, true); array_pop($line); $text = array_pop($line_s); array_pop($line); array_pop($line_s); $line[] = 'html'; $line_s[] = _wiki_inline_html($tok, $text); } else { $line[] = $tok; $line_s[] = $tok_s; } break; case "em": case "strong": case "sup": case '}}': // "//" or "**" or "^^" or {{ }} $offs = _wiki_offset($line, _wiki_inline_start($tok)); if ($offs !== false) { list($line, $line_s) = _wiki_merge($line, $line_s, $offs+1, false, true); array_pop($line); $text = array_pop($line_s); array_pop($line); array_pop($line_s); $line[] = 'html'; $line_s[] = _wiki_inline_html($tok, $text); } else { $line[] = $tok; $line_s[] = $tok_s; } break; case '@@': // @@---minus+++revision@@ $offs = _wiki_offset($line, '@@'); if ($offs !== false) { list($line, $line_s) = _wiki_reduce_revise($line, $line_s, $offs); } else { $line[] = $tok; $line_s[] = $tok_s; } break; case '##': // ##color|text## $offs = _wiki_offset($line, '##'); if ($offs !== false) { list($line, $line_s) = _wiki_reduce_colortext($line, $line_s, $offs); } else { $line[] = $tok; $line_s[] = $tok_s; } break; case ']': // [uri descr] $offs = _wiki_offset($line, '['); if ($offs !== false) { list($line, $line_s) = _wiki_reduce_link($line, $line_s, $offs); } else { $line[] = $tok; $line_s[] = $tok_s; } break; case ']]': // [[# anchor-name]] // [[image image-pars]] // [[image:image-pars]] $offs = _wiki_offset($line, '[['); if ($offs !== false && $line[$offs+1] == '#') { list($line, $line_s) = _wiki_reduce_anchor($line, $line_s, $offs); } else if ($offs !== false && $line[$offs+1] == 'word' && $line_s[$offs+1] == 'image') { list($line, $line_s) = _wiki_reduce_image($line, $line_s, $offs); } else #MediaWiki-style link { list($line, $line_s) = _wiki_reduce_freelink($line, $line_s, $offs); } # else # { # $line[] = $tok; # $line_s[] = $tok_s; # } break; case '))': // ((name|descr)) $offs = _wiki_offset($line, '(('); if ($offs !== false) { list($line, $line_s) = _wiki_reduce_freelink($line, $line_s, $offs); } else { $line[] = $tok; $line_s[] = $tok_s; } break; case 'comment': $line[] = 'html'; $line_s[] = ''; break; default: $line[] = $tok; $line_s[] = $tok_s; break; } return array($line, $line_s); } // helper for @@--- +++ @@ revision patterns function _wiki_reduce_revise ( $line, $line_s, $offs ) { // @@---minus+++revision@@ $len = count($line_s); $offs = _wiki_offset($line, '@@'); if ( $offs !== false && $offs < $len-1 && ($line_s[$offs+1] == '---' || $line_s[$offs+1] == '+++')) { if ($line_s[$offs+1] === '---') { $offs_del = $offs+1; $offs_ins = $offs+2; // Try to find the '+++' while ($offs_ins < $len && $line_s[$offs_ins] != '+++') { $offs_ins++; } } else { $offs_del = false; $offs_ins = $offs+1; } if ($offs_ins < $len) { list($line, $line_s) = _wiki_merge($line, $line_s, $offs_ins+1, false, true); array_pop($line); $ins = array_pop($line_s); // Remove the '+++' array_pop($line); array_pop($line_s); } else { $ins = false; } if ($offs_del !== false) { list($line, $line_s) = _wiki_merge($line, $line_s, $offs_del+1, false, true); array_pop($line); $del = array_pop($line_s); // Remove the '---' array_pop($line); array_pop($line_s); } else { $del = false; } // Remove the '@@'; array_pop($line); array_pop($line_s); if (!empty($del)) { $line[] = 'html'; $line_s[] = _wiki_inline_html('del', $del); } if (!empty($ins)) { $line[] = 'html'; $line_s[] = _wiki_inline_html('ins', $ins); } } return array($line, $line_s); } // helper for [[# anchor-name]] function _wiki_reduce_anchor ( $line, $line_s, $offs ) { // fetch the anchor name list($line, $line_s) = _wiki_merge($line, $line_s, $offs+2, -1, false, false); // pop the name array_pop($line); $name = array_pop($line_s); // pop the # array_pop($line); array_pop($line_s); $line[$offs] = 'html'; $line_s[$offs] = ''; return array($line, $line_s); } // helper for [[image path/to/image image-pars]] function _wiki_reduce_image ( $line, $line_s, $offs ) { // fetch the complete text list($line, $line_s) = _wiki_merge($line, $line_s, $offs+2, -1, false, false); // pop the image path and parameters array_pop($line); $text = trim(array_pop($line_s)); // pop 'image' array_pop($line); array_pop($line_s); //PH: "image:..." syntax if ($text[0] == ':') { $text = substr($text,1); } // Extract the interesting parts from the image description $pos = strpos($text, ' '); if ($pos === false) { $src = $text; $attr = array(); } else { $src = substr($text, 0, $pos); $attr = _wiki_get_attrs(substr($text, $pos+1)); } // Remove double quotes around the uri, some people do type them... if (strlen($src) >= 2 && $src{0} == '"' && $src{strlen($src)-1} == '"') { $src = substr($src, 1, -1); } // We have to postpone the image generation till 'showtime' because an image // typically refers to data that is dynamic. So we just pack the image data // in a special tag and do an expand in smarty. if ( ( strpos($src, '://') !== false || strpos($src, '/') !== false || preg_match('/^[a-zA-Z0-9_]+\.[a-z]{3}$/', $src)) && ( empty($attr['link']) || ( strpos($attr['link'], '://') !== false && strncasecmp($attr['link'], 'popup:', 6) != 0))) { if (!empty($attr['link'])) { // Remove double quotes around the uri, some people do type them... $link = $attr['link']; if (strlen($link) >= 2 && $link{0} == '"' && $link{strlen($link)-1} == '"') { $link = substr($link, 1, -1); } $pre = ''; $post = ''; unset($attr['link']); } else { $pre = ''; $post = ''; } $html = $pre . '$value) { $html .= htmlspecialchars($label) . '="' . htmlspecialchars($value) .'" '; } $html .= '/>' . $post; } else { // Pack the attributes so that we can easily expand them again. $html = ''; } $line[$offs] = 'html'; $line_s[$offs] = $html; return array($line, $line_s); } // helper for ##color| ## colored text function _wiki_reduce_colortext ( $line, $line_s, $offs ) { // Check for the optional description $space = _wiki_after($line, '|', $offs); if ($space != false) { // Fetch description of link list($line, $line_s) = _wiki_merge($line, $line_s, $space+1, -1, true); array_pop($line); $text = trim(array_pop($line_s)); array_pop($line); array_pop($line_s); } else { $text = false; } // Merge all tokens for the color list($line, $line_s) = _wiki_merge($line, $line_s, $offs+1, -1, false); array_pop($line); $color = trim(array_pop($line_s)); if ( (strlen($color) == 3 || strlen($color) === 6) && preg_match('/^[0-9a-fA-F]+$/', $color)) { $color = '#' . $color; } // pop the opening '##' array_pop($line); array_pop($line_s); // Create the span if (!empty($text)) { $line[] = 'html'; $line_s[] = "$text"; } return array($line, $line_s); } // helper for [uri descr] function _wiki_reduce_link ( $line, $line_s, $offs ) { // Keep a copy of line/line_s in case we don't find an uri $line0 = $line; $line_s0 = $line_s; // Check for the optional description $space = _wiki_after($line, ' ', $offs); if ($space != false) { // Fetch description of link list($line, $line_s) = _wiki_merge($line, $line_s, $space, -1, false); array_pop($line); $descr = trim(array_pop($line_s)); // Try to fetch any optional attributes list($descr, $attrs) = _wiki_split_descr_attrs($descr); } else { $descr = false; $attrs = false; } // Merge all tokens for the uri list($line, $line_s) = _wiki_merge($line, $line_s, $offs+1, -1, false, false); array_pop($line); $uri = array_pop($line_s); // only accept this construct when the uri looks like an uri $colon = strpos($uri, ':'); $dot = strpos($uri, '.'); $last = strlen($uri) - 1; if ( strpos($uri, '/') !== false || strpos($uri, '#') !== false || ($dot !== false && $dot < $last) || ($colon > 0 && $colon < $last)) { // pop the opening '[' array_pop($line); array_pop($line_s); // Create the link if (empty($descr)) { // Footnote $html = '' . _wiki_make_link($uri, '*', '', $attrs) .''; } else { // Described link $html = _wiki_make_link($uri, $descr, '', $attrs); } $line[] = 'html'; $line_s[] = $html; } else { // No uri found, do not reduce the found [uri descr] construct $line = $line0; $line_s = $line_s0; $line[] = ']'; $line_s[] = ']'; } return array($line, $line_s); } // helper for ((uri|descr)) function _wiki_reduce_freelink ( $line, $line_s, $offs ) { // Check for the optional description $anchor = false; $pipe = _wiki_after($line, '|', $offs); if ($pipe != false) { $hash = _wiki_after($line, '#', $pipe, true); if ($hash !== false) { list($line, $line_s) = _wiki_merge($line, $line_s, $hash+1, -1, false, false); array_pop($line); $anchor = '#' . trim(array_pop($line_s)); array_pop($line); array_pop($line_s); } // Fetch description of link list($line, $line_s) = _wiki_merge($line, $line_s, $pipe+1, -1, false); array_pop($line); $descr = trim(array_pop($line_s)); list($descr, $attrs) = _wiki_split_descr_attrs($descr); array_pop($line); array_pop($line_s); } else { $descr = false; $attrs = false; } // Merge all tokens for the uri (we will need unescaped text for this one) list($line, $line_s) = _wiki_merge($line, $line_s, $offs+1, -1, false, false); array_pop($line); $uri = array_pop($line_s); // pop the opening '[' array_pop($line); array_pop($line_s); // Create the link $line[] = 'html'; $line_s[] = _wiki_make_link($uri, $descr, $anchor, $attrs); return array($line, $line_s); } // Function: _wiki_offset // Access: INTERNAL // Parameters: $stack stack with tokens // $tok try to find this token // $start (optional) look below this offset // Returns: offset in stack // false when not found // Description: try to locate the token the stack in the stack, // starting to search on top // function _wiki_offset ( $stack, $tok, $start = false ) { if ($start === false) { $start = count($stack) - 1; } else { $start--; } // Don't scan through tds... while ( $start >= 0 && $stack[$start] != $tok && ($tok == '||' || $stack[$start] != '||')) { $start--; } if ($start < 0 || $stack[$start] != $tok) { $start = false; } return $start; } // Function: _wiki_after // Access: INTERNAL // Parameters: $line list of tokens // $tok token to find // $offs offset to start above // $space (optional) set to false to disallow whitespace // Returns: false when not found // offset otherwise // Description: find the given token _after_ the given offset // function _wiki_after ( $line, $tok, $offset, $space = true ) { $ct = count($line); while ( $offset < $ct && $line[$offset] != $tok && ($space || $line[$offset] != ' ')) { $offset ++; } if ($offset == $ct || $line[$offset] != $tok) { return false; } else { return $offset; } } // Function: _wiki_merge // Access: INTERNAL // Parameters: $stack the token stack // $stack_s the texts of the stack // $depth the offset to start the merge // $count number of tokens to merge (-1 for all) // $replace do some wikiword on uri replacements // $escape (optional) set to false to not escape html specialchars // Returns: modified token stack // Description: merges the given entries into one textual entry // literal and word entries will be escaped with htmlspecialchars. // function _wiki_merge ( $stack, $stack_s, $offset, $count, $replace, $escape = true ) { if ($count <= 0) { $len = count($stack); } else { $len = min(count($stack), $offset+$count); } $text = ''; for ($i=$offset; $i<$len; $i++) { if ($replace && $stack[$i] == 'wiki-word') { $text .= _wiki_make_link($stack_s[$i],''); } else if ($stack[$i] == 'html') { $text .= $stack_s[$i]; } else if ($stack[$i] == 'literal') { $text .= '' . htmlspecialchars($stack_s[$i]) . ''; } else if ($replace && $stack[$i] == 'url') { @list($protocol, $address) = explode('://', $stack_s[$i]); $text .= '' . htmlspecialchars($address) . ""; } else if ($replace && $stack[$i] == 'mailto') { // Add a marker to the mailto so that we can rebuild the wiki text $text .= '' . substr(htmlspecialchars($stack_s[$i]), 7) . ''; } else if ($escape) { $text .= htmlspecialchars($stack_s[$i]); } else { $text .= $stack_s[$i]; } } if ($len == count($stack)) { array_splice($stack, $offset); array_splice($stack_s, $offset); } else { array_splice($stack, $offset, $count); array_splice($stack_s, $offset, $count); } if ($escape) { $stack[] = 'html'; } else { $stack[] = 'text'; } $stack_s[] = $text; return array($stack, $stack_s); } // Function: _wiki_make_link // Access: INTERNAL // Parameters: $uri url, not escaped // $descr description, escaped // $anchor optional anchor ('#anchor') // $attrs attributes ( attr="value" ) // Returns: complete anchor tag // Description: creates the anchor tag for the given uri and descr. // when descr is empty then the anchor tag is generated from the uri. // function _wiki_make_link ( $uri, $descr, $anchor = '', $attrs = array() ) { $uri = trim($uri); if (!empty($descr)) { $descr = trim($descr); } // Remove double quotes around the uri, some people do type them... if (strlen($uri) >= 2 && $uri{0} == '"' && $uri{strlen($uri)-1} == '"') { $uri = substr($uri, 1, -1); } $pre = ''; $post = ''; if (!empty($attrs)) { $attrs = ' ' . implode(' ', $attrs); } else { $attrs = ''; } // 1. Check if the uri is a complete one if (strncasecmp($uri, 'mailto:', 7) == 0) { // Add a marker to the mailto so that we can rebuild the wiki text $descr = trim($descr); if (!empty($descr)) { $descr = ' '.$descr; } $text = '' . htmlspecialchars(substr($uri, 7)) . htmlspecialchars($descr) . ''; // Bail out! return $text; } else if ( strpos($uri, '/') === false && !preg_match('/^[a-zA-Z0-9_\-]+\.[a-zA-Z]{2,4}/', $uri) && strncasecmp($uri, 'javascript:', 11) != 0) { // assume symbolic name if (empty($descr)) { // Bail Out: Make special runtime tag, we will need the title of the thing we are linking to... $pre = ''; $post = ''; $descr = htmlspecialchars($uri); } if (!empty($uri)) { $uri = "id.php/" . str_replace(' ', '%20', $uri); } else if (empty($anchor)) { $anchor = '#'; } } else if ( !empty($uri) && strpos($uri, '://') === false && strncasecmp($uri, 'javascript:', 11) != 0 && preg_match('/^[a-z]+(\.[a-z]+)(\.[a-z]+)+(\/.*)?$/', $uri)) { // Make sure we have a protocol for our link, better for tags $uri = 'http://' . $uri; } // 2. Extract a description when we don't have one if (empty($descr) && strpos($uri, '://') !== false) { list($protocol, $col, $descr) = explode('://', $uri); } if (empty($descr)) { $descr = $uri; } if (isset($GLOBALS['any_acl'])) { $uri = $GLOBALS['any_acl']->filterUri($uri); } return $pre . '' . $descr . '' . $post; } // Function: _wiki_inline_start // Access: INTERNAL // Parameters: $descr // Returns: list($descr, $attrs) // Description: splits any attr="value" attributes from the given description // returns the descr and the list of attributes // function _wiki_split_descr_attrs ( $descr ) { global $_attrs; $_attrs = array(); $descr = preg_replace_callback('/\s([a-zA-Z]+)=("|")(.*?)("|")/', '_wiki_collect_attr', ' ' . $descr); return array(trim($descr), $_attrs); } // Helper function to collect all attributes from the descr function _wiki_collect_attr ( $match ) { global $_attrs; global $any_acl; if ( $match[1] == 'target' || $match[1] == 'class' || $any_acl->allowHtml()) { $_attrs[] = $match[1] . '="' . $match[3] . '"'; return ''; } else { return $match[0]; } } // Function: _wiki_inline_start // Access: INTERNAL // Parameters: $tok // Returns: start token for $tok // Description: returns the start token belonging to the inline token $tok // function _wiki_inline_start ( $tok ) { switch ($tok) { case '}}': return '{{'; default: break; } return $tok; } // Function: _wiki_inline_html // Access: INTERNAL // Parameters: $tok // $text // Returns: html for text // Description: surrounds text with the correct html tags for $tok // function _wiki_inline_html ( $tok, $text ) { switch ($tok) { case '}}': $tag = 'tt'; break; default: $tag = $tok; break; } return "<$tag>$text"; } // Function: _wiki_replace_wikiwords // Access: INTERNAL // Parameters: $line // $line_s // $offset (optional) start scanning at offset // $end (optional) stop at offset // Returns: (line, line_s) // Description: scans the line for WikiWords, when found then replaces them // with HTML fragments for freelinks. // function _wiki_replace_wikiwords( $line, $line_s, $offset = 0, $end = false ) { if ($end === false) { $end = count($line); } for ($i = $offset; $i< $end; $i++) { if ($line[$i] == 'wiki-word') { $line[$i] = 'html'; $line_s[$i] = _wiki_make_link($line_s[$i], ''); } } return array($line, $line_s); } // Function: _wiki_get_attrs // Access: INTERNAL // Parameters: $text the text containing 'attr="value"' pairs // Returns: array with attr=>value pairs // Description: parses the attributes of a tag // function _wiki_get_attrs ( $text ) { $parts = explode('="', trim($text)); $last = count($parts) - 1; $attrs = array(); $key = false; foreach ($parts as $i => $val) { if ($i == 0) { $key = trim($val); } else { $pos = strrpos($val, '"'); $attrs[$key] = stripslashes(substr($val, 0, $pos)); $key = trim(substr($val, $pos+1)); } } return $attrs; } // Function: _wiki_toc // Access: INTERNAL // Parameters: $html html with a toc marker // Returns: html with a table of contents // Description: Inserts a table of contents into the html // function _wiki_toc ( $html ) { global $toc_nr; global $toc_base; global $toc; $pos = strpos($html, ''); if ($pos !== false) { $toc_base = abs(crc32(microtime(true).'-'.rand(0,100))); $toc_nr = 0; // 1. Find all tags for insertion in the table of contents, no h1 tags are inserted $html = preg_replace_callback('|()(.*)()|U', '_wiki_toc_accum', $html); // 2. Create the table of contents at the place of the toc tag $s = "\n
      \n"; foreach ($toc as $entry) { list($anchor, $level, $title) = $entry; $s .= "
    • $title
    • \n"; } $s .= "
    \n\n"; $html = str_replace('', $s, $html); } return $html; } function _wiki_toc_accum ( $ms ) { global $toc_nr; global $toc_base; global $toc; $toc_nr++; $anchor = "$toc_base-$toc_nr"; $toc[] = array($anchor, $ms[1]{2}, $ms[2]); return $ms[1]."".$ms[2].$ms[3]; } ?> uri) when input is an array // uri when input is a single id // Description: Translates a thing id to an absolute uri. // Depending on the sharing status and the refering identity // the uri will direct to either the own or the refered // identity. // function any_thing_uri_abs ( $thg_id, $idn_id = null, $lang = null, $commit = null ) { return any_thing_uri($thg_id, $idn_id, $lang, $commit, true); } // Function: any_thing_uri // Access: EXTERNAL // Parameters: $thg_id Id of thing (may be an array) // $idn_id (optional) Identity reading the uris // $lang (optional) The target _must_ have this language // $commit (optional) not used (for now) // $abs (optional,internal) Force absolute uri // Returns: array(id => uri) when input is an array // uri when input is a single id // Description: Translates a thing id to an uri. // Depending on the sharing status and the refering identity // the uri will direct to either the own or the refered // identity. // function any_thing_uri ( $thg_id, $idn_id = null, $lang = null, $commit = null, $abs = false ) { if ($abs) { return 'http://' . $_SERVER['HTTP_HOST'] . "/id/" . urlencode($thg_id); } else { return 'id/' . urlencode($thg_id); } } // Function: any_uri_abs // Access: EXTERNAL // Parameters: $uri // Returns: uri with hostname etc. // Description: Prepends (when needed) the given uri with a full domain name. // function any_uri_abs ( $uri ) { if (strpos($uri, '://') === false) { $uri = 'http://' . $_SERVER['HTTP_HOST'] . '/' . ltrim($uri, '/'); } return $uri; } // Function: any_symbolic2id // Access: EXTERNAL // Parameters: $symbolic Symbolic name to look for // $options Options for search // Returns: id of thing with given symbolic id // false when no id found // Description: return id of thing with the given symbolic name // a thing of the internal source prevails above things // of external sources. // Optionally the selection is reduced to only the // own things and/or things of a certain kind. // When the symbolic id matches the pattern [0-9]+ then // the symbolic id is assumed to be a real id and the id // is returned as is. // function any_symbolic2id ( $symbolic, $options = array() ) { return false; } // Function: any_attach_label2pred // Access: EXTERNAL // Parameters: label label or nr of attachment // Returns: list(predicate, ord_nr) // Description: translates a label or nr to the correct predicate/ order nr. // // A label can have the format of FIG01, FIG02, ICON or DOC01 // Or just be a number, in that case we assume that the user // is refering to a figure. // The first figure is nr 1. Internally we use nr 0 for the // first figure. // function any_attach_label2pred ( $label ) { if (strcasecmp($label, 'ICON') == 0) { $pred = 'ICON'; $nr = 1; } else if (strncasecmp($label, 'FIG', 3) == 0) { $pred = 'FIGURE'; $nr = @intval(substr($label, 3)); } else if (strncasecmp($label, 'DOC', 3) == 0) { $pred = 'DOCUMENT'; $nr = @intval(substr($label, 3)); } else { // Assume numeric $pred = 'FIGURE'; $nr = @intval($label); } // We need an offset from 0 if ($nr > 0) { $nr--; } else { $nr = 0; } return array($pred, $nr); } // Function: any_attach_caption_label // Access: EXTERNAL // Parameters: thg_id thing id // pred predicate (figure, icon, document) // nr order nr of figure etc. (0..n) // Returns: list(att_id, alt, caption) // false when not found // Description: tryes to find the named attachment. // returns the found attachment id, the alt and the caption text // does not consider _any_ access rights! // // In anyMeta a 'thing' can have many images attached to it. // This images are 'things' of the kind 'attachment'. // We identify a particular image by a predicate on the edge // to the image and by the order nr. // // The alt text is typically a short title describing the image. // The caption text could be longer and is typically shown underneath // the image. // function any_attach_caption_pred ( $thg_id, $label ) { return false; } // Function: any_attach_caption // Access: EXTERNAL // Parameters: thg_id thing id // Returns: list(alt, caption) // false when not found // Description: returns the alt and the caption text of the attachment // does not consider _any_ access rights! // // an attachment is an image, mainly used in the context of // articles etc. see the function any_attach_caption_label() // above for a short discussion about attachments. // function any_attach_caption ( $thg_id ) { return false; } // Function: any_thing_title_short // Access: EXTERNAL // Parameters: $thg_id Id of the thing (may be an array, must be real id) // $usr_id (optional) User reading the titles // $lang (optional) Preferred language array // Returns: array(id=>title) // error string with error message // Description: Reads the short titles of the given thing(s). // // in anyMeta every thing must have a title. it might also have a // short title. this function returns the short title, and when // missing it returns the (long) title. // function any_thing_title_short ( $thg_id, $usr_id = null, $lang = null ) { $ts = array(); if (!is_array($thg_id)) { foreach ($thg_id as $id) { $ts[$id] = 'title of ' . htmlspecialchars($id); } } else { $ts[$thg_id] = 'title of ' . htmlspecialchars($thg_id); } return $ts; } // Function: any_text_utf8 // Access: EXTERNAL // Parameters: $html text to check // Returns: utf8 version of text // Description: This checks the input string to be really utf8, replaces non utf8 characters // with a question mark. This validity check is needed before you want to parse // the string with any XML parser. // function any_text_utf8 ( $html ) { if (function_exists('iconv')) { do { $ok = true; $text = @iconv('UTF-8', 'UTF-8//TRANSLIT', $html); if (strlen($text) != strlen($html)) { // Remove the offending character... $html = $text . '?' . substr($html, strlen($text) + 1); $ok = false; } } while (!$ok); } return $html; } // Function: any_encode_mailto // Access: EXTERNAL // Parameters: $href mailto link // $text (optional) description for link // Returns: for mailto // Description: This generates the anchor-tag for an mailto url. // The email address is encoded so that most web-bots won't recognise // it as an email address. // function any_encode_mailto ( $href, $text = '', $encode = 'javascript' ) { if (substr($href, 0, 7) == 'mailto:') { $href = substr($href, 7); } if (empty($text)) { $text = $href; } $html = '' . htmlspecialchars(str_replace('@',' [at] ',$text), ENT_QUOTES) . ''; if ($encode == 'javascript' ) { // Double encode the text using javascript // $js = ''; for ($x=0; $x < strlen($html); $x++) { if (rand(0,5) == 1) { $js .= '\'+\''; } if (strchr('><\'@', $html[$x]) !== false || rand(0,2) == 1) { $js .= '%' . bin2hex($html[$x]); } else { $js .= $html[$x]; } } $html = ''; $js = ''; for ($x=0; $x < strlen($html); $x++) { if (strchr('><\'', $html[$x]) !== false || rand(0,2) == 1) { $js .= '%' . bin2hex($html[$x]); } else { $js .= $html[$x]; } } $html = ''; } else { // Simple non-javascript version // $text_encode = ''; for ($x=0; $x < strlen($href); $x++) { $text_encode .= '&#' . ord($href[$x]) . ';'; } $href = $text_encode; $text_encode = ''; $text = str_replace('@', ' [at] ', $text); for ($x=0; $x < strlen($text); $x++) { $text_encode .= '&#' . ord($text[$x]) . ';'; } $text = $text_encode; $html = "$text"; } return $html; } // Class: Anymeta_ACL // Access: EXTERNAL // Provides: Access control for the anymeta system // class Anymeta_ACL { function Anymeta_ACL () { } // Function: Anymeta_ACL::allowHtml // Access: PUBLIC // Parameters: - // Returns: false when user is not allowed to edit html text // true when user is allowed to edit html text // Description: Checks if the current user is allowed to edit html. // This is a very special right, and should be given // with caution! // // This should be a right of an editor, letting normal users // enter HTML really defies the idea of an wiki, and of the // security of letting people enter markup text. // function allowHtml () { return defined('ALLOWHTML'); } // Function: Anymeta_ACL::filterUri // Access: PUBLIC // Parameters: uri uri to be filtered // Returns: '' when uri was not allowed // uri when uri was allowed // Description: Checks the given uri with the access permissions of the user. // The user needs text/html permissions for entering javascrpt uris. // function filterUri ( $uri ) { $allow = false; $uri = trim($uri); $u = urldecode($uri); if ( strpos($u, '&#') === false && strpos($u, '"') === false && strncasecmp($u, 'javascript:', 11) != 0) { $allow = true; } if (!$allow) { // user needs to have the right to edit HTML $allow = $this->allowHtml(); } return $allow ? $uri : ''; } } $any_acl = new Anymeta_ACL(); ?> base width in pixels for images // height base height in pixels for images // thg_id the id of the thing the texts belong to // (needed to find images) // function any_wiki_runtime ( $text, $options = array() ) { global $_wiki_options; $_wiki_options = $options; // 1. Filter remainings of and html tags $text = preg_replace('//', '', $text); // 2. Grep all image tags and produce tags. $text = preg_replace_callback('//', '_wiki_runtime_image', $text); // 3. Handle the runtime replacement of links $text = preg_replace_callback('/.+?/', '_wiki_runtime_link', $text); // 4. Handle the runtime replacement of mailto hrefs $text = preg_replace_callback('/([^ ]+?)( .+?)?/', '_wiki_runtime_mailto', $text); if (!empty($options['abs_uri'])) { // 5. Make the id.php/xxxx uris absolute $text = preg_replace_callback('||', '_wiki_runtime_anchor_abs_uri', $text); } return $text; } // Function: any_wiki_runtime_image_labels // Access: EXTERNAL // Parameters: $text the text to be expanded // Returns: array with image indices used // Description: Fetch all image nrs used in the given text. // Does recognise the 'FIGxx' format for labels. // Does also handle numerical references correctly // function any_wiki_runtime_image_labels ( $text ) { $image = array(); if (preg_match_all('//', $text, $ms, PREG_PATTERN_ORDER)) { foreach ($ms[1] as $m) { if (strncasecmp($m, 'FIG', 3) == 0) { $image[] = @intval(substr($m,3)) - 1; } else if (is_numeric($m) && $m < 100) { $image[] = intval($m) - 1; } } sort($image); } return $image; } // Function: _wiki_runtime_link // Access: INTERNAL // Parameters: $matches pattern match of the tag // Returns: tag // Description: runtime produces the tag with the correct title. // function _wiki_runtime_link ( $matches ) { global $_wiki_options; $page = $matches[1]; $abs_uri = !empty($_wiki_options['abs_uri']); $attr = ''; $ct = count($matches); for ($i=2; $i<$ct; $i+=3) { $attr .= ' ' . $matches[$i]; } @list($page, $anchor) = explode('#', $page); if (strncasecmp($page, 'uri:', 4) == 0) { $page = substr($page, 4); } else if ( strpos($page, '://') === false && strpos($page, '/' ) === false) { // try to translate the given name to an anymeta id $thg_id = any_symbolic2id($page); } if (!empty($thg_id)) { // It is an anymeta id, use the anymeta routines to generate the link $text = any_thing_title_short($thg_id); if (is_array($text)) { $text = reset($text); } if (empty($text)) { $text = $page; } // Fetch the uri, prefered from the pre-fetched uris in the template if ($abs_uri) { $href = any_thing_uri_abs($thg_id); } else { $href = any_thing_uri($thg_id); } // Add the anchor if (!empty($anchor)) { $href .= "#$anchor"; } $html = '' . htmlspecialchars($text) . ''; } else if (strpos($page, '://') !== false) { $n = strpos($page, '://'); $text = substr($page, $n+3); $html = "".htmlspecialchars($text).""; } else { // the page does not exist, show the page name and // the "new page" text $page = htmlspecialchars($page); $url = 'id.php/'.urlencode($page); if ($abs_uri) { $url = any_uri_abs($url); } $html = "$page"; } return $html; } // Function: _wiki_runtime_anchor_abs_uri // Access: INTERNAL // Parameters: $matches pattern of the tag // Returns: modified tag // Description: makes the enclose uri absolute // function _wiki_runtime_anchor_abs_uri ( $matches ) { return ''; } // Function: _wiki_runtime_mailto // Access: INTERNAL // Parameters: $matches pattern match of the tag // Returns: tag // Description: runtime produces the tag with the correct title. // function _wiki_runtime_mailto ( $matches ) { global $_wiki_options; if (empty($_wiki_options['nojavascript'])) { $encode = 'javascript'; } else { $encode = 'entities'; } return any_encode_mailto($matches[1], @trim($matches[2]), $encode); } // Function: _wiki_runtime_image // Access: INTERNAL // Parameters: $matches pattern match of the tag // Returns: tag // Description: runtime produce the tag for the given image description // function _wiki_runtime_image ( $matches ) { global $_wiki_options; $attr = array(); $src = $matches[1]; $ct = count($matches); for ($i=2; $i<$ct; $i+=3) { $attr[trim($matches[$i+1])] = $matches[$i+2]; } $base_thg_id = !empty($_wiki_options['thg_id']) ? $_wiki_options['thg_id'] : false; $base_width = !empty($_wiki_options['width']) ? $_wiki_options['width'] : 400; $base_height = !empty($_wiki_options['height']) ? $_wiki_options['height'] : 400; $abs_uri = !empty($_wiki_options['abs_uri']) ? $_wiki_options['abs_uri'] : false; // Fetch the requested width and height if (!empty($attr['width'])) { $width = _wiki_runtime_img_size($attr['width'], $base_width); } else { $width = round($base_width); } if (!empty($attr['height'])) { $height = _wiki_runtime_img_size($attr['height'], $base_height); } else { $height = round($base_height); } if (substr($src, 0, 1) == '"' && substr($src, -1) == '"') { $src = substr($src, 1, -1); } $src = trim($src); // See where we have to fetch the image from if (!empty($src)) { if (strpos($src, 'uri:') === 0) { // direct uri $src = substr($src, 4); $id = false; } else if (strpos($src, '://') === false) { if (strpos($src, ':') !== false) { list($a, $b) = explode(':', $src); if (empty($b)) { $thg_id = $a; $lbl = false; } else if (empty($a)) { $thg_id = $base_thg_id; $lbl = $b; } else { $thg_id = $a; $lbl = $b; } } else { $thg_id = $base_thg_id; $lbl = $src; } // Try to translate to a real thg_id if (!is_numeric($thg_id)) { if (empty($lbl)) { $thg_id = any_symbolic2id($thg_id, array('kind'=>'ATTACHMENT')); } else { $thg_id = any_symbolic2id($thg_id); } } // Fetch the thing id of the attachment if (!empty($lbl) && !empty($thg_id)) { list($pred, $nr) = any_attach_label2pred($lbl); @list($aid, $alt, $caption) = any_attach_caption_pred($thg_id, $pred, $nr); } else { // Assume the given src is an attachment id if (!empty($attr['caption'])) { @list($alt, $caption) = any_attach_caption($thg_id); } else { $alt = ''; $caption = ''; } $aid = $thg_id; $lbl = false; } if (empty($caption) && !empty($alt)) { $caption = $alt; } $alt = strip_tags($alt); } else { $id = false; $aid = false; $lbl = false; $alt = ''; $caption = ''; } } else { $src = '#'; // Unknown source $id = false; $aid = false; $lbl = false; } if (!empty($attr['caption'])) { if (!empty($caption)) { $alt = trim($alt); $caption = trim(str_replace(array('

    ', '

    '), array('','
    '), $caption)); while (substr($caption, -5) == '
    ') { $caption = trim(substr($caption, 0, -5)); } if (!empty($alt) && !empty($intro)) { $cap = ''; if (!empty($alt)) { $cap .= '' . any_wiki_runtime($alt, $_wiki_options) .''; } if (!empty($caption)) { $cap .= any_wiki_runtime($caption, $_wiki_options); } $cap .= ''; } else { $cap = ''; } if (strcasecmp($attr['caption'], 'before') == 0) { $cap1 = $cap; $cap2 = ''; } else { $cap1 = ''; $cap2 = $cap; } } else { $cap1 = ''; $cap2 = ''; } unset($attr['caption']); } else { $cap1 = ''; $cap2 = ''; } // // Expand the anchor tag around the image // if (array_key_exists('link',$attr)) { $link = trim($attr['link']); } else { $link = false; } $expand = false; if (empty($link) && !empty($aid)) { // Link to the attachment ;-) $href = 'id/' . $aid; if ($abs_uri) { $href = any_uri_abs($href); } } else if ($link[0] == '#') { // Ref to local anchor $href = $link; } else if ( strpos($link, '://') > 0 || strpos($link, '.php') !== false || strpos($link, '.html') !== false || strpos($link, '.htm') !== false) { // Literal link $href = $link; } else if (strncmp($link, 'javascript:', 12) == 0) { $href = $link; } else if (strncmp($link, 'popup:', 6) == 0) { $popup = substr($link, 6); if (strlen($popup) == 0) { if (is_numeric($thg_id)) { $label = addslashes($lbl); $href = "javascript:popup('$thg_id','$label')"; } else if (!empty($aid) && is_numeric($aid)) { $href = "javascript:popup('{$aid}')"; $expand = true; } } else { // Undefined behaviour for now... $href = false; } } else { // Assume a thing id if (!empty($abs_uri)) { $href = any_thing_uri_abs($link); } else { $href = any_thing_uri($link); } } // Perform any macro expansion (when needed) if (!empty($href) && $expand) { $href = str_replace(array('{$thg_id}', '{$label}', '{$att_id}'), array($thg_id, $lbl, $aid), $href); } $href = htmlspecialchars($href); // unset these so they don't show up as attributes unset($attr['link']); // Build the image tag if (!empty($aid) && is_numeric($aid)) { if (empty($attr['alt'])) { $attr['alt'] = $alt; } if (empty($attr['title'])) { $attr['title'] = $alt; } unset($attr['height']); unset($attr['width']); $pars = $attr; $pars['abs'] = $abs_uri; $img = any_attach_img_tag($aid, $pars, $width, $height); } else { if (!array_key_exists('alt', $attr)) { $attr['alt'] = basename($src); } // A normal src, build the tag ourselves $attr_s = ''; foreach ($attr as $key => $val) { $attr_s .= " $key=\"$val\""; } $img = ""; } if (!empty($href)) { $html = "$cap1
    $img$cap2"; } else { $html = "$cap1$img$cap2"; } return $html; } // Calculate a size, using a base size and a percentage // function _wiki_runtime_img_size ( $req, $base ) { if (substr($req, -2) == 'px') { // Absolute size $ret = substr($req, 0, -2); } else if (is_string($req) && is_numeric($base)) { // Assume percentage of base size if (substr($req, -1) == '%') { $req = substr($req, 0, -1); } $ret = ceil(floatval($req) * floatval($base) / 100.0); } else { // No size $ret = null; } return $ret; } ?> Animeband - History

    History of Animeband

    WARNING: This list may contain game spoilers. Viewer discretion is advised.

    Changelist

    2008.03.31 -
    Changes in Animeband 0.6.1

    • Train station as menu.
    • Enhanced Replacement Technique so that you don't teleport mid-attack into middle of another attack.
    • Cleaned up compile snags for automake-1.9, autoconf-2.60, gcc-4.1.

    11/26/05 - 03/31/08
    Changes in Animeband 0.6.0a

    • Switch to Cygwin/GCC instead of VC++. Source changed somewhat so that it compiles in Cygwin GCC.**
    • Chuukei patch added. Now you can have people watch you play, or watch other people play. Currently there are no servers for non-Japanese players, so there's no explanation on how it works. However, if someone is willing to host a server, download this: gridbug.dyndns.org/broadcastd.tar.gz and e-mail me. (Habu, iks, Shun, PhaethonH)
    • Movie playback and record feature available. Saves in the lib/user directory as an amv file. Records about 10 minutes per meg. Thanks to iks for this wonderful feature.
    • Fixed version number
    • Fixed dash and pyrotechnics crash
    • Fixed Sand glitch. Sand was only doing 1/6th damage instead of 2/3rds. (Deliverything)
    • Fixed Wizard mode damage display glitch; was displaying wrong numbers (Deliverything)
    • Replacement Technique will now only work 25% of the time for the player.
    • Armor of Sand only blocks attacks 25% of the time.
    • Moon the Cat no longer sits there like an idiot
    • Last boss changed from Neo to Raoh (Hokuto no Ken)
    • Lupin now has replacement technique. Color changed to red.
    • Fixed Umbrella of Eagle and other artifact oddities
    • A few monster name oddities fixed (natsukemono).
    • Taunt and Pose glitch crash glitch fixed (??) Also, taunt and pose speed glitch fixed. Possibly the same?
    • Town level creatures no longer tauntable for meter.
    • Fixed rather silly Sanjiyan Sand glitch.
    • Replacement Technique for monsters forces monsters to wake up.
    • Testing: XAngband Style status bar and level feeling (MarRabbit, iks)
    • Testing: Naive solution to the so-called "Too Much Junk" problem. Gold is dropped most of the time; presumption that whatever player needs is in Black Market. Black Market just hands you the item now. The too_much_junk option changes the distribution to normal if shut off. Default is off.
    • Number of creatures with ONLY_ITEM flag severely reduced.
    • Testing: Hastily modified char dump to output resistances and sustains
    • Fixed odd spellbook bug (Henkma)
    • Fixed Rasengan bug (sorta). At least it doesn't crash. (p_ouji)
    • Made Randart exceptions to Triumph and Perseverence.
    • Testing: Tsukuyomi now does stuff.
    • Fixed mimicing glitch. (p_ouji)
    • Fixed negative mana glitch, I think (p_ouji)
    • Fixed odd Sharingan/Byakugan glitch where it doesn't dissipate despite zero mana.
    • Testing: XAngband targeting system (iks).
    • Fixed negative meter gain Sand Glitch (p_ouji)

    9/24/05 - 11/26/05
    Changes in Animeband 0.5.9.1

    • Timing changed to remove movement clumping based on a modification of Kornelis' solution, only this should cover all cases. Thanks to Vaevictus for doing an algorithm analysis on my variation to ensure that it works :).
    • Fixed "Umbrella of Axl-Low" glitch and other amusing artifact oddities
    • Fixed mecha weight glitch for sentai.
    • Sentai can no longer taunt/pose level one creatures.
    • New artifacts and monsters.
    • Drain mana removed from mimicing for now (it didn't do anything anyways).
    • Fixed Regen glitch for cosplayers
    • Fixed silly Ninja overflow glitch

    10/30/04 - 9/23/05
    Changes in Animeband 0.5.9

    • New Icon ^_^
    • Fixed a bug with the replacement technique that put a log in your mecha slot (K2White)
    • Fixed minor SFX glitch when giving someone an item.
    • Targeting system changed for Mimic, Talking, and Giving
    • Put level restrictions on Happy Fun Puzzle Land, Pagoda, and Tokyo Tower
    • Aman changed to Shinsengumi
    • Elvenkind changed to Genji
    • Holy Avenger changed to Holy Order
    • EXTRA_SHOT flag given to Sentai, Warrior, and Ninja
    • All mechas now have feather falling
    • Dust toned down slightly
    • Kira Guards damage increased from 20d10 to 20d15. Experience gained from killing guard cut in half. Dungeon level reduced to lvl 1. Should no longer appear in normal dunegons.
    • Added a confirmation prompt when 'p' is pressed
    • Dango san-Kyodai changed from DROP_1D3 to DROP_1D2 (Raist)
    • Lots of new monsters. Worms ('w') replaced with Weird Creatures. Ants ('a') replaced with Android goons. Nagas ('n') replaced with turtles. Orcs ('o') replaced with sentai. Someone may want to suggest better letters here.
    • Replaced some sounds
    • Mimic class removed. Mimicing is now a Ninjutsu and no longer has to require an adjacent monster.
    • Super Punch from Chi Warrior now sends them flying 13 spaces on average instead of 5. Induced Stun reduced by half. Renamed to Shinkansen (because you hit like a train ^_^).
    • Warriors given STUN_HANDS flag. Warrior hits will now induce stun on good or better hits. Warriors start with Katanas instead of Broadswords.
    • Android nuke limit break changed from GF_FIRE to GF_RADI (radiation damage). Behaves just just like a tactical nuke would.
    • New weapon types: Weapons of Float will launch monsters into the air on a good or better hit.
    • New Class: Ninjas (replacing Mimic)
      • +1 STR, +2 INT, -3 WIS, +4 DEX, +2 CON, -2 CHR
      • Ninjutsus used from Large Scrolls just like mages uses books
      • Mana is CON based
      • May only carry 12 items in inventory
      • 25% extra exp required
      • Strong Psuedo-ID
      • Extra shot flag given
    • New Class: Cosplayer (replacing Paladin)
      • -2 STR, -5 INT, -5 WIS, -2 DEX, -2 CON, -5 CHR
      • Use Costumes to mimic creatures. Costumes are created by using the 'U' key. Costs 100 meter to alter new Costume (default is set to "Odd Clothes")
      • Costumes are sellable (General, Armory)
      • Costumes appear in the General Store.
      • While wearing a costume, you mimic the creature of that costume. Mimic requires no upkeep (FREE_MIMIC) Mimic powers cost meter, however.
      • EXP requirement reduced by half.
      • Cosplayers have MECHA_SENSE (reduced damage while riding a Mecha, including zero damage from 1 damage attacks)
      • Weak Psuedo-ID
      • Analyze works just like probing: tells you all of the monster's resistances, melee attacks and other stuff.
    • New Class: Sentai (replacing Ranger)
      • No stat penalties, no stat bonuses
      • Meter based powers. Sentai can taunt and pose to gain meter.
      • MECHA_SENSE and EXTRA_SHOT
      • Sentai will spark when hit 1 time in 10, inducing double damage.
    • New Race : Moogles
      • -1 STR, -1 INT, +2 DEX, +2 CON, -2 CHR
      • Inherently Resistant to Poison
      • Dance Limit break which does very random things (in a radius too!)
      • Get the best prices from stores (unless the store owner is a Hententmon)
    • New Fighting Style: Sand
      • Inherent +5 accuracy, +5 speed, +2 blows, +3 stealth
      • Meter gained from damage taken AND damage dealt. Should be the fastest meter building groove now.
      • Deals 33% less damage
      • Takes 50% more damage
    • Priest prayers sometimes cost no mana (player level dependent random event, done by INTERVENTION flag)
    • Warning message now given if you're close to being drunk, and also when you're close to being sober.
    • You can now request items from the black market. Only one type of item (though you can request multiple items), and it'll be delivered to you. Will cost 100% extra of black market price. You can only have one delivery at a time, and you cannot request another item until delivery succeeds or fails. Max 20 of an item per request (to avoid stupid overflow errors). Incidentally, this code is so ugly that sometimes I wake up and cry at night thinking about it...
    • Broadsword of Perseverance and Quarterstaff of Triumph weight restored to 050 weights + 75 pounds to make it very difficult to abuse. Upped the unwanted summoning chance from 5% to 20%. Induces Teleportation randomly.
    • New artifacts replacing all of the stupid ones.
    • Kira's Helmet is now a forced set drop (or it should be).
    • Updated docs (everything should now be current; if not please tell me)
    • Random Title Screens! I thought this would be kinda cool.
    • New throwing weapons (Shuriken, Kunai) appear in general store now
    • Magic Knight swords can no longer be store items.
    • Magic Knights are given more spells.
    • Terrible Engrish (Student) replaced with Gaijin Smash
    • Wrath (Chi Warrior) replaced with "THE WORLD!"
    • Cure/Heal potions/staves/spells/activates are now percentage based rather than set die roll.
    • Battle Arena quest replaced with Dueling Guild. Vegeta is now a normal dungeon monster. Dueling guild allows hundred men battles (hyakunin kumite) as well has having a list of uniques challenge you. Quest is completed upon clearing the list.
    • Monster leaks to wrong locations should be fixed, hopefully.
    • Quest quota eliminated. Side quests are now optional.
    • Train ticket price eliminated.
    • Artifacts can be dropped by Wilderness creatures. Quality of items dropped by wilderness creatures increased.
    • Fixed a bug that put Pagoda uniques in the dungeon.
    • Wizard mode allows meter adjustment as well (PhaetonH)
    • Added a little surprise in the town once you reach a certain depth ^_^
    • Changed the history a little bit to be a bit more animeish
    • Fixed a small Student display glitch
    • Rune of Protection replaced by Gaijin Perimeter
    • Life Insurance scrolls work a little bit better.

    Todo:

    • Doujins
    • Squelch?
    • Monster Personality Matrix
    • Monsters with Meter bars that can super

    9/24/04 - 10/30/04
    Changes in Animeband 0.5.8.2

    • More spelling mistakes fixed (Achijah)
    • Removed floating eye insta-kills. *SHOULD* no longer happen.
    • Display bug when examining items in a store is now fixed (Kusunose Toru)
    • croff() bug fixed from older version of Angband (Kusunose Toru)
    • Monsters "Natto" and "Dango-san Kyodai" added
    • Fixed glitch that causes sound to crash Animeband in WinXP. Sound should work now.
    • Fonts should now display correctly in WinXP (Robert Ruehlmann, Kusunose Toru)

    9/23/04 - 9/24/04
    Changes in Animeband 0.5.8.1

    • Reduced occurances of Demonic Door (was depth/120 per door, now depth/180).
    • Fixed some more misspellings and data values, courtesy of Kusunose Toru

    6/27/04 - 9/22/04
    Changes in Animeband 0.5.8

    • 'u' stands for Mechanical monsters now. There will most likely be balance issues -_-.
    • Fixed a stupid glitch that makes compiling not work in other OSes.
    • Changed some data values and misspellings (Saiya-jin), courtesy of Kusunose Toru
    • Changed names in the random name generator, courtesy of Kusunose Toru
    • Added some new uniques and monsters
    • Added the origin of the monster in the monster description
    • Added artifact descriptions! You get to see how much thought I actually put into the artifacts :)
    • Sanjiyan limit break changed to Wu Transformation.
    • Similar to Super Sayajin, only much more powerful.
    • GOI and no mana consumption during transformation
    • Once transform is complete, the strain reduces you to zero hp and mp, and paralyzation occurs.
    • Transformation does not last very long.
    • Created some most evil new flags
    • New Quest - Pagoda. Does not work from a pre 058 save file however. Solving this quest will give you better damage reduction.
    • Updated Chi Warrior FAQ
    • Students and Magic Knights are better with mechas than other classes. Guns will do more damage, and you'll get hit less.
    • To compensate for some rather bad evil, taunt has been replaced by intrinsic class powers (the 'U' key). These powers cost meter, but they're very useful.
    • A new helpful unique is there too. Go find him.
    • Note: You probably won't survive if you try going past DL 100 :)

    Todo:

    • Clean up the Wilderness code and make it more customizable
    • Survival mode game?
    • More powers for Chi Warrior
    • Sacred Edge
    • Primary Lotus
    • Nightmare Circular
    • Firebird Attack
    • Explosion Pills
    • Chidori (All meter into one blow)
    • Leaf Slash

    5/22/04 - 6/27/04
    Changes in Animeband 0.5.7

    • Androids never go hungry.
    • Lose less HP per turn in Drunk Style. Alcohol lasts a little longer.
    • Study glitch fixed
    • Increased Parry Success, Parry Life recovery, Water style now gains meter when attacked.
    • Juraians int/wis is +3/+3, Sanjiyan int/wis is +5/+5
    • Metal style builds meter slightly slower
    • Added the Hengband monster speaking patch, courtesy of koka
    • Upgraded Hententmon and Ctarl
    • Ctarl Stealth, lowered Juraian Stealth.
    • Mimic strength penalty changed from -5 to -2
    • Mimic mp loss during mimic changed from one mp every three turns to one every ten turns.
    • Mimic has much better stealth
    • Blade catch 50% less likely
    • Fixed an idiot glitch that disables you from beating the game. Thanks, AdamH :P

    Todo:

    • Add Blue Monsters
    • FAQ
    • Dante's sword
    • Throw ?
    • Enlightenment limit break
    • Chi Warrior Power - Issen
    • New weapon types - Fan,
    • Store back rooms :)
    • Helpful creatures - Araiguma Rascal and Doraemon

    4/20/04 - 5/22/04
    Changes in Animeband 0.5.6

    • Added the HTML screenshot patch. It spews funky ASCII sometimes though...
    • Blade catching is toned down quite significantly. Also, only a few monsters have it now.
    • Increased chances in cooking success.
    • New Dust attack flag for weapons and monsters. Also add NO_LAUNCH flag. Allows players to paralyze monsters.
    • New artifacts
    • Fixed a Magic Knight Glitch
    • Adjusted Fire and Drunk groove to do more damage
    • Updated Documents somewhat

    Todo:

    • Weapon descriptions
    • FAQ
    • Dante's sword
    • Throw ?
    • Enlightenment limit break
    • Chi Warrior Power - Issen

    4/02/04 - 4/20/04
    Changes in Animeband 0.5.5

    • A VERY bad Magic Knight glitch fixed (if you hit level 50, weapon turns into an umbrella. Woo-hoo. This won't happen anymore :))
    • Magic Knights nerfed a bit. I felt that they were too strong.
    • Some people may have gotten an old changes.txt file. In that case ignore the part about the Magic Knights. The dice/sides DO evolve.
    • Chi Warrior Powers finally correctly implemented. I also nerfed them a lot because they seem severely unbalanced. They can't cook well either or wield shields. Oh well, only time will tell.
    • Mimics nerfed slightly.
    • Some monsters can "blade catch" you. MWAHAHAHAHA!
    • Reduced required quests from 4 to just 2. You must solve two quests and kill Lina for the stairs to appear to DL 100, if you're in quest mode. Currently there are only 6 quests available..of course, this will change eventually.
    • On the plus side, there's a new scroll called "Life Insurance". Maybe this will make the game a lot easier.
    • Some new, most evil monsters :)
    • Some new vaults

    Todo:

    • Use flags more often instead of ghetto code :)
    • Implement a most evil AI....hopefully it'll be nastier than 4GAI.

    9/08/03 - 4/02/04
    Changes in Animeband 0.5.4

    • QUESTS!!
    • Vending Machines and Ramen stands...oh my :)
    • More tiles
    • Decided to cancel the idea of respawning uniques for now. Had some odd trouble with this.
    • Finally got rid of the useless load1.c
    • Magic Knight glitch fixed. If you're loading an earlier version, just level up to the next 10n, and it'll be fixed from there.
    • Sanjiyan new limit break: Raging Demon
    • Bullets do a lot more damage (from people who shoot guns)
    • Some new monsters
    • Fixed a metal/mecha glitch
    • Wrote a wilderness maker that combines the graph
    • like properties of Un with the customizability of ToME. I had fun with this one ^_^
    • "Wildernesses" are accessible by a Train Station, located at the top left of the town. It costs 300 AU to board the train.
    • Mechas no longer lose life for firing guns
    • Mecha HP replaces player HP on the screen when you wield a mecha.
    • New classes, Mimic and Chi Warrior. Mimics can mimic whatever monster is adjacent to them with the 'O' key. Chi Warriors are not allowed to use weapons, but get a punch bonus, and learn powers from parchments. Once a power is learned, it can be swapped into your knowledge queue for use at any time.
    • Limit Break command changed to 'p'
    • New command: Press CTRL-T to talk to people.
    • New command: Press CTRL-B to give an item to someone.
    • New command: Press CTRL-H to cook items.
    • Student and Mimic transform state extended a bit longer.
    • Updated docs to a degree
    • Two types of games are available: Classical for the V lovers, and Quest mode for people who like quests. If you use an old version savefile, it will default to Classical.
    • Mecha repair scrolls are available. However, they weigh 500 pounds. :)

    Todo:

    • Make some of the mimic powers less retarded
    • Monster mana....anyone?
    • Sort out the terrains and object list (get rid of that stupid "pit" glitch)
    • Remap the controls so they're more intuitive (need to look at the roguelike keyset)
    • More tiles/monsters
    • More quests, I guess.
    • Implement AI....after Monster mana.

    8/02/03 - 9/08/03
    Changes in Animeband 0.5.3

    • Added a random name generator
    • Added more monsters
    • Fixed a small error in the display console
    • Added a RISC OS makefile (courtesy of ajps)
    • Vegeta now apperars at DL 75
    • Photon toned down slightly
    • Miyamoto Musashi moved to a deeper DL
    • Added some new tiles (8X8, click on old tiles, courtesy of Tybwyn)

    7/11/03 - 8/02/03
    Changes in Animeband 0.5.2

    • Idiotic Paladin Glitch fixed
    • Fixed *really* abusable Student glitch, so now Student transform is no longer instant. In exchange, student mana costs for the last two spells are lower.
    • It is now pointless to enchant Magic Swords, as the to hit and to dam will be level dependent.
    • Magic Knights toned down just slightly (probably not noticeable)
    • Sky Dragon Student and Magic Knight glitch fixed. The exp needed to level up is now correct.
    • Black Market may sell some strange food stuffs. That is fine. It will be part of an upcoming "Cooking" feature.
    • Some new monsters added -> I am running out of ideas. Suggestions welcome.
    • Kamitori no longer spawn.

    5/23/2003 - 7/11/03
    Changes in Animeband 0.5.1

    • ALL C++ STYLE COMMENTS ARE GONE! :o
    • Meter is now displayed in file dump
    • Player now takes longer to sober up
    • You can theoretically no longer die from Alcohol Withdrawal
    • Mana cost for Student powers are increased
    • Students get less bonuses from transform
    • Student Limit Break costs more
    • Students need a LOT more EXP to level up
    • Water no longer auto parry 0 damage attacks
    • Wind no longer auto dodges 0 damage attacks
    • New Mechas added
    • Some new monsters added
    • Some misspellings corrected
    • Some tables updated
    • Some Artifacts changed
    • Psuedo-Japanese scroll titles implemented
    • Charisma potions now cost the same as other stat potions
    • Mapped Student Powers, Prayers, and Rayearth Powers to the 'm' key for ease of use
    • New Class added - Magic Knight
    • Magic Knights get a perma cursed weapon that is wielded since birth
    • Weapon of Magic Knight Evolves, and starts with brand depending on Groove
    • Magic Knights also get 3 intrinsic spells depending on groove
    • Once the Magic Knight hits level 50, they can wield any weapon they wish
    • Venom attack added
    • Taunting added. It is the 'U' key
    • New Desktop Icon
    • Hello Kitty toned down a lot
    • Yumi Saotome appears rarely now and is slower
    • Makefiles fixed (I think)

    6/02/2002 - 5/23/2003
    Changes in Animeband 0.5.0 (from Angband 2.9.3)

    • New Sound FX
    • Added new monsters and new and interesting artifacts.
    • New Races with new resistances and stat bonuses/penalties
    • New and hopefully interesting spells for mages (priest prayers remain untouched)
    • Has its own version number and name ^_^
    • A new meter system is implemented. When you get max meter, you are allowed to "limit break" (or "super", whatever you want to call it). Such supers are dependent on what race you are and usually have good effects. When you have maximum meter, press 'Z' to super.
    • A new fighting style system is implemented. The groove system affects how you gain meter along with some other things (critical hits, dodges, parrys, etc.)
    • You can get drunk in this game :)) Though, the effects aren't exactly good.
    • Some uniques never die.
    • Some monsters will hit you, regardless of what armor you're wearing. There are monsters out there which I suggest you run from...


    -- animeband --