Location: PHPKode > projects > Processed Book Open Source > pbos-1.01/annotate.php
<?php
/**
 * Input/show/edit/save annotations.
 *
 * Copyright (C) 2005 Wayne Davison <hide@address.com>
 *
 * 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; 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 more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

require 'lib/main.inc';
require 'lib/wiki.inc';

switch ($_GET['do']) {
case 'input':
    if ($_POST['annoID'])
	save_page(''); // Handle re-anchor
    else
	input_page();
    break;
case 'show':
    show_page();
    break;
case 'edit':
    edit_page('');
    break;
case 'editnote':
    edit_page('note');
    break;
case 'save':
    save_page('');
    break;
case 'savenote':
    save_page('note');
    break;
case 'delete':
    delete_page();
    break;
default:
    redir_main();
    break;
}

function input_page()
{
    global $CGI;

    $book_num = $_GET['b'];
    $book_sect = $_GET['s'];
    $sel_pos = $_POST['pos'];
    $tID = $_POST['tID'];
    $nest = $_POST['nest'];
    if (!preg_match('/^\d+;-?\d+;\d+;\d+$/', join(';', array($book_num, $book_sect, $sel_pos, $tID)))
     || !preg_match('!^(/[A-Z]+[1-6]?(#\d+)?)+$!', $nest)) {
	redir_main();
	return;
    }

    open_db();

    list($uID, $scrname) = get_auth_cookie();
    if (!$uID) {
	start_content('Error', 'wide');
	echo <<<EOT
You must be signed in to add an Annotation.
<p>If you already have an account, please <a href="signin.php">sign in</a>.
<p>Otherwise, either <a href="signin.php?do=signup">sign up</a> or press the "Back"
button on your browser.
EOT;
	close_db();
	end_content();
	return;
    }

    sidebar_signin($uID, $scrname);

    start_content('Add New Annotation', 'wide');

    $group_list = get_groups($book_num, $scrname, 'CanAdd', $uID, $book_num, '');
    foreach ($group_list as $gID => $name) {
	if ($name == '/Private')
	    $group_def = $gID;
    }

    $sel = preg_replace(array('/&/', '/"/', '/</'), array('&amp;', '&quot;', '&lt;'), $_POST['sel']);

    $anno_attrs = get_attr_data($tID);
    #$anno_attrs[99]->PromptText = 'Assign to group(s)';
    $anno_attrs[99]->GroupList = $group_list;
    $anno_attrs[99]->DefaultValue = $group_def;
    $annotype = $anno_attrs['#NAME'];

    $action = "$CGI?do=save&b=$book_num&s=$book_sect&a=0";
    if ($anno_attrs[52]->DefaultValue != '') { // FilterURL
	$redir = "<input type=hidden name=redir value=\"$action\">\n";
	$action = $anno_attrs[52]->DefaultValue;
	$warn = '<b>Note:</b> submitted data is being filtered through a 3rd-party site.';
    }
    echo <<<EOT
<h2 style="margin:0">Creating a new $annotype instance</h2>
<form name=mainform method=post action="$action">
Please enter these attributes for a $annotype attached to the text <span style="border: solid black 1px">$sel</span>:
<input type=hidden name=uID value="$uID">
<input type=hidden name=tID value="$tID">
<input type=hidden name=nest value="$nest">
<input type=hidden name=pos value="$sel_pos">
<input type=hidden name=sel value="$sel">
<input type=hidden name=hasFields value=1>
<script>document.scrname = '$scrname'</script>
$redir<p><table border=1>

EOT;

    foreach ($anno_attrs['#ORDER'] as $aID) {
	$attrs = $anno_attrs[$aID];
	if ($attrs->Need == 'req') {
	    $note = '<div>(<b>*</b>) These attributes are required to have a non-empty value.';
	    $suffix = '<b>*</b>';
	} elseif ($attrs->Need == 'const')
	    continue;
	else
	    $suffix = '';
	echo "<tr valign=top><td>", $attrs->PromptText, $suffix, ":</td><td>";
	output_input_field($attrs->Name, $attrs, $attrs->DefaultValue, $annotype,
			   $attrs->InputWidth, $attrs->InputHeight, $attrs->Need == 'filt',
			   $book_num, 0);
	echo "</td></tr>\n";
    }

    echo <<<EOT
</table>
$warn<center><input type=submit name=sub value="Create" tabindex=1> &nbsp;
<input type=button value="Cancel" onclick="history.go(-1)" tabindex=1></center>
$note
</form>

EOT;

    close_db();
    end_content();
}

function show_page()
{
    global $CGIDIR;

    $book_num = $_GET['b'];
    $book_sect = $_GET['s'];
    $book_anno = $_GET['a'];
    if (!preg_match('/^\d+;-?\d+;\d+$/', join(';', array($book_num, $book_sect, $book_anno)))) {
	redir_main();
	return;
    }

    open_db();

    list($uID, $scrname) = get_auth_cookie();
    sidebar_signin($uID, $scrname);

    start_content('Show', 'wide');

    $do = "SELECT b.*, l.Name FROM BookAnnotations AS b LEFT JOIN AnnotationTypes AS l USING(TypeID)
	WHERE b.ID = $book_anno AND b.BookID = $book_num AND b.ParentID = $book_sect
	LIMIT 1";
    $result = mysql_query($do) or die('SELECT failed: ' . mysql_error() . " (cmd: $do)");
    if (($lnk = mysql_fetch_assoc($result)) === FALSE) {
	echo 'Invalid request';
	end_content();
	return;
    }

    $do = 'SELECT Name FROM Users WHERE UserID = ' . $lnk['UserID'];
    $result = mysql_query($do) or die('SELECT failed: ' . mysql_error() . " (cmd: $do)");
    list($lnk_author) = mysql_fetch_row($result);

    $group_list = get_groups($book_num, $scrname, '', $uID, $book_num, $lnk['_99']);

    echo <<<EOT
<h2 style="margin:0">Data for {$lnk['Name']}</h2>
<p><table border=1>
EOT;

    $url = $CGIDIR . 'read.php';
    $glist = array();
    foreach (explode(',', $lnk['_99']) as $gID)
	$glist[] = $group_list[$gID];
    natcasesort($glist);
    $lnk['_99'] = join(', ', $glist);

    $anno_attrs = get_attr_data($lnk['TypeID']);
    foreach ($anno_attrs['#ORDER'] as $aID) {
	if ($aID == 52) // skip FilterURL
	    continue;
	$attrs = $anno_attrs[$aID];
	echo "<tr valign=top><td>", $attrs->PromptText, ':</td><td>';
	$val = $lnk["_$aID"];
	if (preg_match('/<' . preg_quote($val, '/') . '=([^>]+)>/', $attrs->InheritedDefault, $out))
	    $val = $out[1];
	if ($attrs->InputHeight > 1) {
	    if (strlen($val) > 256)
		$val = substr($val, 0, 256) . '<b>...</b>';
	    echo '<pre>', $val, '</pre>';
	    if ($aID == 51) // NoteText
		echo '<a href="', "$url?b=$book_num&s=-$book_anno", '">[View Note]</a>';
	} else if ($aID == 50) { // URL
	    $val =  preg_replace(array('/^(\d+),(\d)/', '/^(\d)/'),
				 array("$url?b=$1&$2", "$url?b=$book_num&$1"),
				 $val);
	    echo '<a href="', $val, '">', $val, '</a>';
	} else
	    echo $val;
	echo "</td></tr>\n";
    }

    $url .= "?b=$book_num&s=$book_sect&t=" . $lnk['ID'] . '#pblnk';
    if ($lnk['UserID'] == $uID) {
	$edit = ' &nbsp; '
	      .  button('btn-editnote',
			"annotate.php?do=edit&b=$book_num&s=$book_sect&a=".$lnk['ID'],
			'width=34 height=20');
    }
    echo <<<EOT
<tr valign=top><td>Author:</td><td>$lnk_author$edit</td></tr>
<tr valign=top><td>Location:</td><td><a href="$url">$url</a></td></tr>
</table>

<p><INPUT TYPE=button onClick="history.go(-1)" VALUE="Go Back" tabindex=1>
EOT;

    close_db();
    end_content();
}

function edit_page($act_suffix)
{
    $book_num = $_GET['b'];
    $book_sect = $_GET['s'];
    $book_anno = $_GET['a'];
    if (!preg_match('/^\d+;-?\d+;\d+$/',
	    join(';', array($book_num, $book_sect, $book_anno)))) {
	redir_main();
	return;
    }

    open_db();

    list($uID, $scrname) = get_auth_cookie();
    if (!$uID) {
	redir_main();
	return;
    }

    sidebar_signin($uID, $scrname);

    start_content('Edit Annotation', 'wide');

    $do = "SELECT * FROM BookAnnotations AS b LEFT JOIN AnnotationTypes AS l USING(TypeID)
	WHERE b.ID = $book_anno AND b.BookID = $book_num AND b.ParentID = $book_sect AND b.UserID = $uID
	LIMIT 1";
    $result = mysql_query($do) or die('SELECT failed: ' . mysql_error() . " (cmd: $do)");
    if (($lnk = mysql_fetch_assoc($result)) === FALSE) {
	echo 'Invalid request';
	end_content();
	return;
    }

    $group_list = get_groups($book_num, $scrname, 'CanAdd', $uID, $book_num, $lnk['_99']);

    echo <<<EOT
<h2 style="margin:0">Editing: {$lnk['Name']}</h2>
<form name=mainform method=post action='?do=save$act_suffix&b=$book_num&s=$book_sect&a=$book_anno'>
<input type=hidden name=uID value="$uID">
<input type=hidden name=tID value="{$lnk['TypeID']}">
<input type=hidden name=pos value="-1">
<input type=hidden name=hasFields value=1>
<script>document.scrname = '$scrname'</script>
<p>(<b>*</b>) These attributes are required to have a non-empty value.
<p><table border=1 width=640>

EOT;

    $anno_attrs = get_attr_data($lnk['TypeID']);
    #$anno_attrs[99]->PromptText = 'Assign to group(s)';
    $anno_attrs[99]->GroupList = $group_list;
    $annotype = $anno_attrs['#NAME'];
    foreach ($anno_attrs['#ORDER'] as $aID) {
	$attrs = $anno_attrs[$aID];
	if ($attrs->Need == 'req')
	    $suffix = '<b>*</b>';
	elseif ($attrs->Need == 'const')
	    continue;
	else
	    $suffix = '';
	$val = $lnk["_$aID"];
	if ($val == '')
	    $val = $attrs->DefaultValue;
	echo "<tr valign=top><td>", $attrs->PromptText, $suffix, ":</td><td>";
	output_input_field($attrs->Name, $attrs, $val, $annotype,
			   $attrs->InputWidth, $attrs->InputHeight, 0,
			   $book_num, $book_anno);
	echo "</td></tr>\n";
    }

    echo <<<EOT
</table>

<center><input type=submit name=sub value="Save" tabindex=1> &nbsp;
<input type=button value="Cancel" onclick="history.go(-1)" tabindex=1></center>
EOT;

    foreach ($group_list as $g) {
	if ($g[0] == '~') {
	    echo <<<EOT
<div>Groups <span style="color:red">colored red</span> will be removed if you save the annotation.</div>
EOT;
	    break;
	}
    }

    echo "</form>\n";

    close_db();
    end_content();
}

function save_page($act_suffix)
{
    global $CGI, $icon_names;

    $book_num = $_GET['b'];
    $book_sect = $_GET['s'];
    $book_anno = $_GET['a'];
    if ($book_anno == '')
	$book_anno = $_POST['annoID'];
    $sel_pos = $_POST['pos'];
    $tID = $_POST['tID'];
    if (!preg_match(':^\d+;-?\d+;\d+;-?\d+;\d+$:',
	    join(';', array($book_num, $book_sect, $book_anno, $sel_pos, $tID)))) {
	redir_main();
	return;
    }

    if ($sel_pos >= 0) {
	$nest = $_POST['nest'];
	if (!preg_match('!^(/[A-Z]+[1-6]?(#\d+)?)+$!', $nest)) {
	    redir_main();
	    return;
	}

	$sel = $_POST['sel'];
	$testing = $tID == 99; // AnnotationType 99 is hard-wired as a testing item.

	if (preg_match('/^(\s+)/', $sel, $out)) {
	    $sel_pos += strlen($out[1]);
	    $sel = preg_replace('/^\s+/', '', $sel);
	}
	$sel = preg_replace(array('/\r/', '/ +$/m'), '', $sel);
	$sel_len = strlen($sel);
    }

    open_db();

    list($uID, $scrname) = get_auth_cookie();
    if (!$uID || $uID != $_POST['uID']) {
	redir_main();
	return;
    }

    add_sidebar_image('btn-return_to_book', "read.php?b=$book_num&s=$book_sect");
    sidebar_signin($uID, $scrname);

    start_content('Saving...', 'wide');

    if ($book_anno) {
	$do = "SELECT UserID
	    FROM BookAnnotations
	    WHERE ID=$book_anno AND BookID=$book_num AND ParentID=$book_sect AND TypeID='$tID'";
	$result = mysql_query($do) or die('SELECT failed: ' . mysql_error() . " (cmd: $do)");
	$row = mysql_fetch_row($result);
	if ($row === FALSE || $row[0] != $uID) {
	    echo 'Invalid request';
	    end_content();
	    return;
	}
    }

    // Note: the re-anchor action does not set hasFields.
    $has_fields = $_POST['hasFields'];
    $do_suf = '';

    // Note: $sel_pos < 0 when editing an existing annotation.
    if ($sel_pos >= 0) {
	if ($book_sect >= 0) {
	    $do = "SELECT Text, Offsets
		FROM BookSections
		WHERE BookID = $book_num AND SectionID = $book_sect
		LIMIT 1";
	} else {
	    $note_num = -$book_sect;
	    $do = "SELECT _51 AS Note, NoteOffsets
		FROM BookAnnotations
		WHERE ID = $note_num AND BookID = $book_num
		LIMIT 1";
	}
	$result = mysql_query($do) or die('SELECT failed: ' . mysql_error() . " (cmd: $do)");
	list($text,$tag_offsets) = mysql_fetch_row($result);

	if (!preg_match('!^' . $nest . ':(\d+)\r?\n(?:.*:(\d+))?!m', $tag_offsets, $out)) {
	    echo "Internal error!  Unable to find selected text's section in the book ($nest).\n";
	    end_content();
	    return;
	}
	$offset = $out[1];
	if ($out[2])
	    $snippet = substr($text, $offset, $out[2] - $offset);
	else
	    $snippet = substr($text, $offset);
	if ($testing) {
	    echo "Position: $offset + $sel_pos = ", $offset + $sel_pos, ", Selection len: $sel_len<br />\n";
	    $s1 = preg_replace(array('/&/', '/</'), array('&amp;', '&lt;'), $sel);
	    $s2 = preg_replace(array('/&/', '/</'), array('&amp;', '&lt;'), $snippet);
	    echo "<pre>###$s1###</pre><pre style='border: solid black 1px'>", $s2, "</pre>";
	}

	// Trim any start-of-section wiki markup (avoiding \s on purpose).
	// Also: table-start & table-end are not here because tables contain other containers.
	if (preg_match('/^[*#=;:]+ *|^[|!][^|\n]+\| *|^[|!] */', $snippet, $out)) {
	    $len = strlen($out[0]);
	    $snippet = substr($snippet, $len);
	    $offset += $len;
	}

	// Find any internal wiki markup so we can adjust the character offset.
	// N.B. We treat all &entities; as though everything after the '&' was
	// hidden since we assume the browser collapsed it into a single char.
	// Also, <> is treated like a single char, 'cuz it was mapped to nbsp.
	$ign = '(?<!\f)(?:\[\[Image:[^]]+(?<!\f)\]\]|\[\[[^]<> ]+ |\[\[|\]\]|'
	     . '<[^>]+>|\'\'\'|\'\'|(?<=<)>|(?<=&)(?:#\d+|[A-Z]?[a-z]+);| +(?= )|\f|^ +)';
	preg_match_all('/'.$ign.'/m', $snippet, $ignore, PREG_OFFSET_CAPTURE);
	foreach ($ignore[0] as $match) {
	    list($str,$pos) = $match;
	    if ($pos > $sel_pos)
		break;
	    $sel_pos += strlen($str);
	}

	if ($testing) {
	    echo "Real position: $offset + $sel_pos = ", $offset + $sel_pos, "<br />";
	    echo '<pre style="border: solid black 1px">', substr($snippet, $sel_pos), "</pre>";
	}
	$offset += $sel_pos;

	$do_suf .= ", AnchorStart='$offset', AnchorLen='$sel_len'";
    }

    if ($has_fields) {
	$group_list = get_groups($book_num, $scrname, 'CanAdd', $uID, $book_num, '');
	$anno_attrs = get_attr_data($tID);
	$annotype = $anno_attrs['#NAME'];
	$margin_map = array();
	$order = $anno_attrs['#ORDER'];
	foreach ($order as $aID) {
	    $attrs = $anno_attrs[$aID];
	    if ($attrs->Need == 'const')
		$val = preg_replace('/^\$_AnnoType$/', $annotype, $attrs->DefaultValue);
	    else {
		$val = preg_replace(array('/^\$URL$/', '/\r\n?/'),
				    array($_POST['URL'], "\n"),
				    $_POST[$attrs->Name]);
		if ($attrs->Need == 'req' && $val == '') {
		    echo "You did not specify the {$attrs->PromptText}.  Please go back and try again.";
		    close_db();
		    end_content();
		    return;
		}
	    }
	    if ($attrs->Name == 'NoteText') {
		// Caution: $book_anno is 0 when creating a new annotation!
		list($val) = to_internal_wiki($val, $book_anno ? $book_num : 0, -$book_anno);
		$do_suf .= ', NoteOffsets=NULL';
	    } elseif ($attrs->Name == 'Groups') {
		$groups = array();
		foreach (explode(',', $val) as $gID) {
		    if (!preg_match('/^(?:(\+)(?! )([-A-Za-z0-9_ ]+)(?<! )|-?\d+)$/', $gID, $out))
			continue;
		    if ($out[1] == '+') {
			$gname = $out[2];
			$gID = add_new_group($uID, $uID, $uID, $scrname, $gname, 1);
		    } elseif ($group_list[$gID] == '')
			continue;
		    $groups[] = $gID;
		}
		$val = join(',', $groups);
	    } elseif ($attrs->Name == 'FilterURL')
		continue;
	    $do_suf .= ", _$aID=\"" . mysql_real_escape_string($val) . '"';
	    if ($attrs->IconType > 0)
		$margin_map[$icon_names[$attrs->IconType]] = $aID;
	}
	foreach ($margin_map as $mm => $id)
	    $do_suf .= ", $mm=_$id";
    }

    if ($sel_pos >= 0 && $has_fields) {
	$do = "INSERT INTO BookAnnotations
	    SET TypeID='$tID', BookID=$book_num, ParentID=$book_sect, UserID=$uID" . $do_suf;
	$action = 'Added new';
    } else {
	$do = "UPDATE BookAnnotations SET" . preg_replace('/^,/', '', $do_suf)
	    . " WHERE ID=$book_anno";
	$action = 'Saved';
    }

    if ($testing)
	echo '<pre>', $do, '</pre>';
    else {
	mysql_query($do) or die('INSERT/UPDATE failed: ' . mysql_error() . " (cmd: $do)");
	$url = $CGI . "read.php?b=$book_num&s=$book_sect&t=" . mysql_insert_id() . '#pblnk';
    }

    echo $action, ' ', $annotype, ".\n";

    if ($annotype == 'IncomingLink' && !$book_anno) {
	$linktext = preg_replace(array('/&/', '/</'), array('&amp;', '&lt;'), $_POST['MarginMouseoverText']);
	if ($linktext == '')
	    $linktext = 'link text';
	$js = "<a href=\"$url\">\n$linktext</a>";
	$html = preg_replace('/</', '&lt;', $js);
	$js = preg_replace(array('/"/', '/\n/'), array('\\\\"', ''), $js);
	echo <<<EOT
<p>The incoming link generated was:
<pre>$url</pre>
<p>Example HTML would be:
<pre>$html</pre>
<script>
if (window.clipboardData && clipboardData.setData) {
    document.write('Copying this HTML to your clipboard...');
    clipboardData.setData('Text', "$js");
    document.write('done');
}
</script>
<p>You may now <a href="read.php?b=$book_num&s=$book_sect">return to the book</a>.
EOT;
    } elseif (!$testing) {
	if ($act_suffix == '')
	    $to_sect = $book_sect;
	else
	    $to_sect = -$book_anno;
	echo  <<<EOT
The page will automatically refresh in a moment...
<META HTTP-EQUIV=Refresh CONTENT="0; URL=read.php?b=$book_num&s=$to_sect">
EOT;
    }

    close_db();
    end_content();
}

function delete_page()
{
    $book_num = $_GET['b'];
    $book_sect = $_GET['s'];
    $book_anno = $_GET['a'];
    if (!preg_match('/^\d+;-?\d+;\d+$/', join(';', array($book_num, $book_sect, $book_anno)))) {
	redir_main();
	return;
    }

    open_db();

    list($uID, $scrname) = get_auth_cookie();
    if (!$uID) {
	redir_main();
	return;
    }

    start_content('Delete', 'wide');

    $do = "DELETE FROM BookAnnotations
	WHERE ID=$book_anno AND BookID=$book_num AND ParentID=$book_sect AND UserID=$uID
	LIMIT 1";
    mysql_query($do) or die('DELETE failed: ' . mysql_error() . " (cmd: $do)");

    echo <<<EOT
Annotation deleted!  The page will automatically refresh in a moment...
<META HTTP-EQUIV=Refresh CONTENT="0; URL=read.php?b=$book_num&s=$book_sect">

EOT;

    close_db();
    end_content();
}

function get_groups($book_num, $scrname, $need, $uID, $book_num, $groups)
{
    $has_perms = "REGEXP '(^|,)(0|$uID)(,|$)'";
    $need = $need != '' ? "$need $has_perms" : 1;
    $group_list = array();
    $do = "SELECT GroupID, GroupName, $need AS IsOK,
	(CanView $has_perms OR CanAdd $has_perms OR CanCull $has_perms OR CanAdmin $has_perms) AS IsVisible
	FROM Groups";
    $result = mysql_query($do) or die('SELECT failed: ' . mysql_error() . " (cmd: $do)");
    while (($obj = mysql_fetch_object($result)) !== FALSE) {
	$name = preg_replace(":^$scrname/:", '/', $obj->GroupName);
	if (preg_match('/^(Book-Default)#(\d+)$/', $name, $out)) {
	    if ($out[2] != $book_num)
		continue;
	    $name = '<i>' . $out[1] . '</i>';
	}
	if (!$obj->IsVisible)
	    $name = preg_replace(':/.*:', '/...', $name);
	if (!$obj->IsOK) {
	    if (!preg_match('/(^|,)' . $obj->GroupID . '(,|$)/', $groups))
		continue;
	    $name = "~$name";
	}
	$group_list[$obj->GroupID] = $name;
    }
    natcasesort($group_list);
    reset($group_list);
    return $group_list;
}

function output_input_field($var, $attrs, $default, $annotype, $width, $height,
			    $filt_hidden, $book_num, $book_anno)
{
    if ($attrs->Name == 'Groups') {
	// Note: $default is known to contain only digits and commas in this case.
	echo <<<EOT
<span id=lstgroup></span>
(Or: <input type=button value="Add a new group" onclick="new_group_prompt()" tabindex=1>)
<input type=hidden name=$var value="$default">
<script>
EOT;
	foreach ($attrs->GroupList as $gID => $name) {
	    if ($name[0] == '~') {
		$disabled = 1;
		$name = substr($name, 1);
	    } else
		$disabled = 0;
	    echo "add_group_checkbox($gID, '$name', $disabled);\n";
	}
	echo <<<EOT
set_group_checkboxes();
</script>
EOT;
	return;
    }
    $default = preg_replace('/\$_AnnoType/', $annotype, $default);
    if (preg_match('/^<(.*)>$/', $attrs->InheritedDefault, $out)) {
	$list = preg_replace('/(^|<)<([^>]*)>(>|$)/', '$1$2$3', $out[1]);
	echo "<select name=\"$var\" tabindex=1>\n";
	foreach (explode('><', $list) as $opt) {
	    $opt = preg_replace(array('/&/', '/"/', '/</', '/\\\\=/'), array('&amp;', '&quot;', '&lt;', '&#61;'), $opt);
	    if (preg_match('/^(.*?)=(.*)$/', $opt, $out)) {
		$opt = $out[1];
		$prompt = $out[2];
	    } else
		$prompt = $opt;
	    $selected = $opt == $default ? ' selected' : '';
	    echo "<option value=\"$opt\"$selected>$prompt\n";
	}
	echo "</select>\n";
    } elseif ($height > 1) {
	if ($attrs->Name == 'NoteText')
	    echo "<script>output_toolbar(\"$var\",'')</script><br>";
	echo "<textarea name=\"$var\" rows=$height cols=$width tabindex=1>";
	if ($attrs->Name == 'NoteText')
	    output_external_wiki($default, $book_num, -$book_anno);
	else
	    echo external_wiki_string($default, 0);
	echo '</textarea>';
    } elseif ($filt_hidden) {
	$val = external_wiki_string($default, 1);
	echo "<input type=hidden name=\"$var\" value=\"$default\"><i>(Value provided by filter.)</i>";
    } else {
	$val = external_wiki_string($default, 1);
	echo "<input type=text name=\"$var\" size=$width value=\"$default\" tabindex=1>";
    }
}

?>
Return current item: Processed Book Open Source